From e317e38b3db9124a29210c75677852e39f3044d8 Mon Sep 17 00:00:00 2001 From: Samuel Leweke Date: Thu, 31 Aug 2023 23:42:34 +0200 Subject: [PATCH 1/4] Add pre-commit support for code formatting Uses pre-commit and clang-format to automatically format code before it is committed. --- .clang-format | 223 ++++++++++++++++++++++++++++++++++++++++ .pre-commit-config.yaml | 16 +++ 2 files changed, 239 insertions(+) create mode 100644 .clang-format create mode 100644 .pre-commit-config.yaml diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..967e77bd5 --- /dev/null +++ b/.clang-format @@ -0,0 +1,223 @@ +--- +BasedOnStyle: Microsoft +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: false + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAfterAttributes: Never +BreakAfterJavaFieldAnnotations: false +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeConceptDeclarations: Always +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: "^ IWYU pragma:" +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: ^"(llvm|llvm-c|clang|clang-c)/ + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: ^(<|"(gtest|gmock|isl|json)/) + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: .* + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: (Test)?$ +IncludeIsMainSourceRegex: "" +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: true +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +Language: Cpp +LineEnding: DeriveLF +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PPIndentWidth: -1 +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 1000 +PointerAlignment: Left +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: Never +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDeclarationName: false + AfterFunctionDefinitionName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseTab: ForContinuationAndIndentation +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..aee8fd66b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,16 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: trailing-whitespace + args: [--markdown-linebreak-ext=md] + - id: end-of-file-fixer + - id: check-yaml +- repo: https://github.com/pre-commit/mirrors-clang-format + rev: v18.1.8 + hooks: + - id: clang-format + types_or: [c++, c] +exclude: ^ThirdParty/ From 2b0a2f687ae900922d3687c877e2151d6cbdf429 Mon Sep 17 00:00:00 2001 From: Samuel Leweke Date: Thu, 31 Aug 2023 23:52:33 +0200 Subject: [PATCH 2/4] Add some more IDE and tool folders to .gitignore Ignores VS Code and clangd LSP directories. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index b88c2eac2..305a8cd34 100644 --- a/.gitignore +++ b/.gitignore @@ -75,6 +75,8 @@ doc/tex/docs/*.aux ### Ignore IDE files ### .vs/ .idea/ +.vscode/ +.clangd/ *.iws *.kdev4 .kdev4/ From bc4008fd0c86f20b001f80f179466f667b184247 Mon Sep 17 00:00:00 2001 From: "r.jaepel" Date: Mon, 26 Aug 2024 16:50:38 +0200 Subject: [PATCH 3/4] Change ExternalFunctionTemplate.cpp to .cpp.in to avoid problems with clang --- src/libcadet/CMakeLists.txt | 18 +++++++++--------- ...ate.cpp => ExternalFunctionTemplate.cpp.in} | 14 +++++++------- ...ate.cpp => ExternalFunctionTemplate.cpp.in} | 10 +++++----- 3 files changed, 21 insertions(+), 21 deletions(-) rename src/libcadet/model/binding/{ExternalFunctionTemplate.cpp => ExternalFunctionTemplate.cpp.in} (99%) rename src/libcadet/model/reaction/{ExternalFunctionTemplate.cpp => ExternalFunctionTemplate.cpp.in} (99%) diff --git a/src/libcadet/CMakeLists.txt b/src/libcadet/CMakeLists.txt index c6c415c2b..3c5049be9 100644 --- a/src/libcadet/CMakeLists.txt +++ b/src/libcadet/CMakeLists.txt @@ -1,9 +1,9 @@ # ============================================================================= # CADET -# +# # Copyright © The CADET Authors # Please see the CONTRIBUTORS.md file. -# +# # All rights reserved. This program and the accompanying materials # are made available under the terms of the GNU Public License v3.0 (or, at # your option, any later version) which accompanies this distribution, and @@ -215,9 +215,9 @@ foreach(BM IN LISTS LIBCADET_BINDINGMODEL_SOURCES) get_filename_component(BMFILEWE ${BM} NAME_WE) get_filename_component(BMFILE ${BM} NAME) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${BMFILE} - COMMAND templateCodeGen ${CMAKE_SOURCE_DIR}/src/libcadet/model/binding/ExternalFunctionTemplate.cpp ${BM} ${CMAKE_CURRENT_BINARY_DIR}/${BMFILE} + COMMAND templateCodeGen ${CMAKE_SOURCE_DIR}/src/libcadet/model/binding/ExternalFunctionTemplate.cpp.in ${BM} ${CMAKE_CURRENT_BINARY_DIR}/${BMFILE} MAIN_DEPENDENCY ${BM} - DEPENDS ${CMAKE_SOURCE_DIR}/src/libcadet/model/binding/ExternalFunctionTemplate.cpp + DEPENDS ${CMAKE_SOURCE_DIR}/src/libcadet/model/binding/ExternalFunctionTemplate.cpp.in COMMENT "Generating code for ${BMFILEWE}" ) list (APPEND LIBCADET_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/${BMFILE}") @@ -227,9 +227,9 @@ foreach(RM IN LISTS LIBCADET_REACTIONMODEL_SOURCES) get_filename_component(RMFILEWE ${RM} NAME_WE) get_filename_component(RMFILE ${RM} NAME) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${RMFILE} - COMMAND templateCodeGen ${CMAKE_SOURCE_DIR}/src/libcadet/model/reaction/ExternalFunctionTemplate.cpp ${RM} ${CMAKE_CURRENT_BINARY_DIR}/${RMFILE} + COMMAND templateCodeGen ${CMAKE_SOURCE_DIR}/src/libcadet/model/reaction/ExternalFunctionTemplate.cpp.in ${RM} ${CMAKE_CURRENT_BINARY_DIR}/${RMFILE} MAIN_DEPENDENCY ${RM} - DEPENDS ${CMAKE_SOURCE_DIR}/src/libcadet/model/reaction/ExternalFunctionTemplate.cpp + DEPENDS ${CMAKE_SOURCE_DIR}/src/libcadet/model/reaction/ExternalFunctionTemplate.cpp.in COMMENT "Generating code for ${RMFILEWE}" ) list (APPEND LIBCADET_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/${RMFILE}") @@ -273,7 +273,7 @@ if (LAPACK_FOUND) add_library(libcadet_static STATIC $) set_target_properties(libcadet_static PROPERTIES OUTPUT_NAME cadet_static) target_link_libraries(libcadet_static PUBLIC CADET::CompileOptions CADET::LibOptions PRIVATE CADET::AD libcadet_nonlinalg_static SUNDIALS::sundials_idas ${SUNDIALS_NVEC_TARGET} ${TBB_TARGET} ${EIGEN_TARGET}) - + # --------------------------------------------------- # Build the shared library # --------------------------------------------------- @@ -281,7 +281,7 @@ if (LAPACK_FOUND) add_library(libcadet_shared SHARED $) set_target_properties(libcadet_shared PROPERTIES OUTPUT_NAME cadet) target_link_libraries (libcadet_shared PUBLIC CADET::CompileOptions CADET::LibOptions PRIVATE CADET::AD libcadet_nonlinalg_static SUNDIALS::sundials_idas ${SUNDIALS_NVEC_TARGET} ${TBB_TARGET} ${EIGEN_TARGET}) - + list(APPEND LIBCADET_TARGETS libcadet_nonlinalg_static libcadet_object libcadet_static libcadet_shared) unset(LIB_LAPACK_DEFINE) @@ -289,7 +289,7 @@ endif () foreach(_TARGET IN LISTS LIBCADET_TARGETS) target_include_directories(${_TARGET} - PUBLIC $ $ + PUBLIC $ $ PRIVATE ${CMAKE_SOURCE_DIR}/src/libcadet ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) set_target_properties(${_TARGET} PROPERTIES DEBUG_POSTFIX _d VERSION ${CADET_VERSION} SOVERSION "0") diff --git a/src/libcadet/model/binding/ExternalFunctionTemplate.cpp b/src/libcadet/model/binding/ExternalFunctionTemplate.cpp.in similarity index 99% rename from src/libcadet/model/binding/ExternalFunctionTemplate.cpp rename to src/libcadet/model/binding/ExternalFunctionTemplate.cpp.in index b63f38daa..cf023318c 100644 --- a/src/libcadet/model/binding/ExternalFunctionTemplate.cpp +++ b/src/libcadet/model/binding/ExternalFunctionTemplate.cpp.in @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Template for external function support in binding models. * This file serves as a template for generating parameter handler classes that * hold, configure, and update parameters, which may also depend on external @@ -174,7 +174,7 @@ class {{ name }} : public cadet::model::ConstParamHandlerBase protected: inline bool validateConfig(unsigned int nComp, unsigned int const* nBoundStates); - + ConstParams _localParams; {% for p in parameters %} @@ -242,12 +242,12 @@ class {{ externalName }} : public cadet::model::ExternalParamHandlerBase {% endfor %} {% endif %} }; - + typedef VariableParams params_t; typedef ConstBufferedScalar ParamsHandle; static inline const char* identifier() CADET_NOEXCEPT; - + {{ externalName }}() CADET_NOEXCEPT {% if exists("constantParameters") %} : @@ -412,7 +412,7 @@ class {{ externalName }} : public cadet::model::ExternalParamHandlerBase protected: inline bool validateConfig(unsigned int nComp, unsigned int const* nBoundStates); - + ConstParams _constParams; {% for p in parameters %} diff --git a/src/libcadet/model/reaction/ExternalFunctionTemplate.cpp b/src/libcadet/model/reaction/ExternalFunctionTemplate.cpp.in similarity index 99% rename from src/libcadet/model/reaction/ExternalFunctionTemplate.cpp rename to src/libcadet/model/reaction/ExternalFunctionTemplate.cpp.in index 92e434465..1116f6d54 100644 --- a/src/libcadet/model/reaction/ExternalFunctionTemplate.cpp +++ b/src/libcadet/model/reaction/ExternalFunctionTemplate.cpp.in @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Template for external function support in reaction models. * This file serves as a template for generating parameter handler classes that * hold, configure, and update parameters, which may also depend on external @@ -242,12 +242,12 @@ class {{ externalName }} : public cadet::model::ExternalParamHandlerBase {% endfor %} {% endif %} }; - + typedef VariableParams params_t; typedef ConstBufferedScalar ParamsHandle; static inline const char* identifier() CADET_NOEXCEPT; - + {{ externalName }}() CADET_NOEXCEPT {% if exists("constantParameters") %} : From 388ba80bcb71f4c2392f47e8e12fff84669346f0 Mon Sep 17 00:00:00 2001 From: "r.jaepel" Date: Mon, 26 Aug 2024 16:51:34 +0200 Subject: [PATCH 4/4] Format all code with clang --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- .github/workflows/ci.yml | 1 - BUILD-OSX.md | 7 +- BUILD-WINDOWS.md | 14 +- CMakeLists.txt | 10 +- CMakeSettings.json | 2 +- CONTRIBUTORS.md | 1 - LICENSE-ThirdParty.txt | 186 +- README.rst | 4 +- cmake/Modules/FindCADET.cmake | 4 +- cmake/Modules/FindSUNDIALS.cmake | 4 +- cmake/Modules/FindSuperLU.cmake | 8 +- cmake/Modules/FindTBB.cmake | 58 +- cmake/Modules/FindUMFPACK.cmake | 24 +- cmake/Modules/MatlabTBBversion.cpp | 4 +- doc/README.md | 4 +- doc/_static/css/custom.css | 1 - doc/conf.py | 1 - doc/developer_guide/TemplateBinding.cpp | 318 +- doc/developer_guide/build_options.rst | 1 - .../cadet_core_architecture.rst | 9 +- doc/developer_guide/cadet_python.rst | 30 +- doc/developer_guide/examples/breakthrough.py | 14 +- doc/developer_guide/launch.vs.json | 2 +- doc/developer_guide/model_expansion.rst | 8 +- doc/examples/batch_chromatography.rst | 1 - doc/examples/load_wash_elute.rst | 1 - doc/examples/rtd.rst | 1 - doc/getting_started/index.rst | 2 +- doc/getting_started/installation.rst | 2 +- .../tutorials/_images/breakthrough_system.svg | 2 +- .../tutorials/breakthrough.rst | 6 +- doc/index.rst | 4 +- .../binding/bi_steric_mass_action.rst | 6 +- doc/interface/binding/freundlich_ldf.rst | 14 +- .../binding/generalized_ion_exchange.rst | 44 +- .../binding/hic_constant_water_activity.rst | 7 +- .../hic_water_on_hydrophobic_surfaces.rst | 7 +- doc/interface/binding/index.rst | 3 +- doc/interface/binding/linear.rst | 9 +- .../binding/multi_component_anti_langmuir.rst | 6 +- .../binding/multi_component_bi_langmuir.rst | 1 - .../binding/multi_component_colloidal.rst | 3 +- .../binding/multi_component_langmuir.rst | 4 +- .../binding/multi_component_langmuir_ldf.rst | 4 +- ...ti_component_langmuir_ldf_liquid_phase.rst | 4 +- doc/interface/binding/saska.rst | 3 - doc/interface/binding/self_association.rst | 1 - doc/interface/consistent_initialization.rst | 4 +- doc/interface/flux_reconstruction.rst | 7 +- doc/interface/index.rst | 3 - doc/interface/input_group.rst | 2 - doc/interface/introduction.rst | 3 +- doc/interface/meta_group.rst | 18 +- doc/interface/output_group.rst | 272 +- doc/interface/parameter_dependencies.rst | 10 +- doc/interface/reaction/mass_action_law.rst | 74 +- .../reaction/michaelis_menten_kinetics.rst | 12 +- doc/interface/return_data.rst | 143 +- doc/interface/sensitivities.rst | 42 +- doc/interface/solver.rst | 113 +- doc/interface/system.rst | 116 +- .../unit_operations/2d_general_rate_model.rst | 306 +- doc/interface/unit_operations/cstr.rst | 58 +- .../unit_operations/general_rate_model.rst | 188 +- doc/interface/unit_operations/index.rst | 3 +- doc/interface/unit_operations/inlet.rst | 26 +- .../lumped_rate_model_with_pores.rst | 166 +- .../lumped_rate_model_without_pores.rst | 88 +- .../multi_channel_transport_model.rst | 116 +- doc/interface/unit_operations/outlet.rst | 9 +- .../unit_operations/radial_flow_models.rst | 26 +- doc/literature.bib | 2 +- doc/modelling/binding/freundlich_ldf.rst | 17 +- .../binding/hic_constant_water_activity.rst | 1 - .../hic_water_on_hydrophobic_surfaces.rst | 2 +- doc/modelling/binding/index.rst | 1 - .../multi_component_bi_langmuir_ldf.rst | 2 +- .../binding/multi_component_colloidal.rst | 9 +- doc/modelling/index.rst | 2 +- doc/modelling/networks.rst | 11 +- doc/modelling/reaction/index.rst | 2 +- doc/modelling/reaction/mass_action_law.rst | 2 +- .../unit_operations/2d_general_rate_model.rst | 18 +- doc/modelling/unit_operations/cstr.rst | 2 - .../unit_operations/general_rate_model.rst | 36 +- doc/modelling/unit_operations/index.rst | 2 +- .../lumped_rate_model_without_pores.rst | 4 +- .../multi_channel_transport_model.rst | 12 +- doc/simulation/index.rst | 6 +- doc/zbibliography.rst | 6 +- include/ad/setfad.hpp | 1223 ++++--- include/ad/sfad-common.hpp | 116 +- include/ad/sfad.hpp | 1339 +++---- include/cadet/Exceptions.hpp | 26 +- include/cadet/ExternalFunction.hpp | 26 +- include/cadet/FactoryFuncs.hpp | 78 +- include/cadet/HashUtil.hpp | 368 +- include/cadet/InletProfile.hpp | 44 +- include/cadet/LibExportImport.hpp | 27 +- include/cadet/LibVersionInfo.hpp | 108 +- include/cadet/Logging.hpp | 253 +- include/cadet/Model.hpp | 21 +- include/cadet/ModelBuilder.hpp | 34 +- include/cadet/ModelSystem.hpp | 27 +- include/cadet/Notification.hpp | 18 +- include/cadet/ParameterId.hpp | 119 +- include/cadet/ParameterProvider.hpp | 35 +- include/cadet/Simulator.hpp | 198 +- include/cadet/SolutionExporter.hpp | 49 +- include/cadet/SolutionRecorder.hpp | 45 +- include/cadet/StringUtil.hpp | 404 ++- include/cadet/StrongTypes.hpp | 39 +- include/cadet/cadet.h | 26 +- include/cadet/cadet.hpp | 6 +- include/common/CompilerSpecific.hpp | 58 +- include/common/Driver.hpp | 111 +- include/common/JsonParameterProvider.hpp | 14 +- include/common/Logger.hpp | 121 +- include/common/LoggerBase.hpp | 837 +++-- include/common/OrderingConverter.hpp | 40 +- include/common/ParameterProviderImpl.hpp | 21 +- include/common/SolutionRecorderImpl.hpp | 557 ++- include/common/TclapUtils.hpp | 96 +- include/common/Timer.hpp | 519 +-- include/io/FileIO.hpp | 105 +- include/io/IOException.hpp | 12 +- include/io/hdf5/HDF5Base.hpp | 148 +- include/io/hdf5/HDF5Reader.hpp | 62 +- include/io/hdf5/HDF5Writer.hpp | 135 +- include/io/xml/XMLBase.hpp | 240 +- include/io/xml/XMLReader.hpp | 63 +- include/io/xml/XMLWriter.hpp | 138 +- src/build-tools/CMakeLists.txt | 5 +- src/build-tools/templateCodeGen.cpp | 20 +- src/cadet-cli/CMakeLists.txt | 6 +- src/cadet-cli/Logging.hpp | 99 +- src/cadet-cli/ProgressBar.cpp | 383 +- src/cadet-cli/ProgressBar.hpp | 20 +- src/cadet-cli/SignalHandler.cpp | 159 +- src/cadet-cli/SignalHandler.hpp | 30 +- src/cadet-cli/cadet-cli.cpp | 218 +- src/io/FileIO.cpp | 163 +- src/io/JsonParameterProvider.cpp | 100 +- src/libcadet/AdUtils.cpp | 198 +- src/libcadet/AdUtils.hpp | 173 +- src/libcadet/AutoDiff.cpp | 15 +- src/libcadet/AutoDiff.hpp | 366 +- src/libcadet/Benchmark.hpp | 53 +- src/libcadet/BindingModelFactory.cpp | 198 +- src/libcadet/BindingModelFactory.hpp | 105 +- src/libcadet/CompileTimeConfig.hpp.in | 4 +- src/libcadet/ConfigurationHelper.hpp | 23 +- src/libcadet/FactoryFuncs.cpp | 56 +- src/libcadet/LapackInterface.hpp | 261 +- src/libcadet/LocalVector.hpp | 245 +- src/libcadet/Logging.cpp | 136 +- src/libcadet/Logging.hpp | 126 +- src/libcadet/LoggingUtils.hpp | 188 +- src/libcadet/MathUtil.hpp | 25 +- src/libcadet/Memory.hpp | 1495 ++++---- src/libcadet/ModelBuilderImpl.cpp | 422 +-- src/libcadet/ModelBuilderImpl.hpp | 29 +- src/libcadet/ParallelSupport.hpp | 430 +-- src/libcadet/ParamIdUtil.hpp | 81 +- src/libcadet/ParamReaderHelper.hpp | 1106 +++--- src/libcadet/ParamReaderScopes.hpp | 220 +- src/libcadet/ParameterDependenceFactory.cpp | 195 +- src/libcadet/ParameterDependenceFactory.hpp | 198 +- src/libcadet/ReactionModelFactory.cpp | 105 +- src/libcadet/ReactionModelFactory.hpp | 105 +- src/libcadet/SensParamUtil.hpp | 30 +- src/libcadet/SimulatableModel.hpp | 205 +- src/libcadet/SimulationTypes.hpp | 47 +- src/libcadet/SimulatorImpl.cpp | 2587 ++++++------- src/libcadet/SimulatorImpl.hpp | 127 +- src/libcadet/SlicedVector.hpp | 120 +- src/libcadet/Stencil.hpp | 378 +- src/libcadet/SundialsVector.hpp | 50 +- src/libcadet/VersionInfo.cpp.in | 8 +- src/libcadet/Weno.cpp | 36 +- src/libcadet/Weno.hpp | 218 +- src/libcadet/api/CAPIv1.cpp | 656 ++-- src/libcadet/graph/GraphAlgos.cpp | 162 +- src/libcadet/graph/GraphAlgos.hpp | 62 +- src/libcadet/linalg/ActiveDenseMatrix.hpp | 123 +- src/libcadet/linalg/BandMatrix.cpp | 98 +- src/libcadet/linalg/BandMatrix.hpp | 391 +- .../linalg/BandedEigenSparseRowIterator.hpp | 104 +- .../linalg/CompressedSparseMatrix.cpp | 23 +- .../linalg/CompressedSparseMatrix.hpp | 377 +- src/libcadet/linalg/DenseMatrix.cpp | 45 +- src/libcadet/linalg/DenseMatrix.hpp | 1819 +++++----- src/libcadet/linalg/EigenSolverWrapper.cpp | 127 +- src/libcadet/linalg/EigenSolverWrapper.hpp | 183 +- src/libcadet/linalg/Gmres.cpp | 151 +- src/libcadet/linalg/Gmres.hpp | 107 +- src/libcadet/linalg/Norms.hpp | 280 +- src/libcadet/linalg/SparseMatrix.cpp | 9 +- src/libcadet/linalg/SparseMatrix.hpp | 80 +- .../linalg/SparseSolverInterface.hpp.in | 6 +- src/libcadet/linalg/Subset.hpp | 940 ++--- src/libcadet/linalg/SuperLUSparseMatrix.cpp | 44 +- src/libcadet/linalg/SuperLUSparseMatrix.hpp | 41 +- src/libcadet/linalg/UMFPackSparseMatrix.cpp | 92 +- src/libcadet/linalg/UMFPackSparseMatrix.hpp | 23 +- src/libcadet/model/BindingModel.hpp | 124 +- .../model/ExternalFunctionSupport.hpp | 336 +- .../GeneralRateModel-InitialConditions.cpp | 372 +- .../model/GeneralRateModel-LinearSolver.cpp | 403 ++- src/libcadet/model/GeneralRateModel.cpp | 1177 +++--- src/libcadet/model/GeneralRateModel.hpp | 573 ++- .../GeneralRateModel2D-InitialConditions.cpp | 563 ++- .../model/GeneralRateModel2D-LinearSolver.cpp | 398 +- src/libcadet/model/GeneralRateModel2D.cpp | 1037 ++++-- src/libcadet/model/GeneralRateModel2D.hpp | 519 ++- .../model/GeneralRateModelBuilder.cpp | 126 +- .../GeneralRateModelDG-InitialConditions.cpp | 474 ++- .../model/GeneralRateModelDG-LinearSolver.cpp | 117 +- src/libcadet/model/GeneralRateModelDG.cpp | 1011 ++++-- src/libcadet/model/GeneralRateModelDG.hpp | 2980 ++++++++++----- src/libcadet/model/InletModel.cpp | 122 +- src/libcadet/model/InletModel.hpp | 349 +- ...edRateModelWithPores-InitialConditions.cpp | 341 +- .../LumpedRateModelWithPores-LinearSolver.cpp | 431 +-- .../model/LumpedRateModelWithPores.cpp | 483 ++- .../model/LumpedRateModelWithPores.hpp | 536 ++- .../model/LumpedRateModelWithPoresBuilder.cpp | 126 +- ...RateModelWithPoresDG-InitialConditions.cpp | 2154 +++++------ ...umpedRateModelWithPoresDG-LinearSolver.cpp | 84 +- .../model/LumpedRateModelWithPoresDG.cpp | 491 ++- .../model/LumpedRateModelWithPoresDG.hpp | 641 +++- .../model/LumpedRateModelWithoutPores.cpp | 615 ++-- .../model/LumpedRateModelWithoutPores.hpp | 430 ++- .../LumpedRateModelWithoutPoresBuilder.cpp | 94 +- .../model/LumpedRateModelWithoutPoresDG.cpp | 3118 ++++++++-------- .../model/LumpedRateModelWithoutPoresDG.hpp | 1052 +++--- src/libcadet/model/ModelSystemImpl-Helper.hpp | 180 +- .../ModelSystemImpl-InitialConditions.cpp | 256 +- .../model/ModelSystemImpl-LinearSolver.cpp | 621 ++-- .../model/ModelSystemImpl-Residual.cpp | 259 +- src/libcadet/model/ModelSystemImpl.cpp | 407 ++- src/libcadet/model/ModelSystemImpl.hpp | 220 +- src/libcadet/model/ModelUtils.hpp | 37 +- ...hannelTransportModel-InitialConditions.cpp | 134 +- ...ultiChannelTransportModel-LinearSolver.cpp | 22 +- .../model/MultiChannelTransportModel.cpp | 526 +-- .../model/MultiChannelTransportModel.hpp | 354 +- src/libcadet/model/OutletModel.cpp | 73 +- src/libcadet/model/OutletModel.hpp | 354 +- src/libcadet/model/ParameterDependence.hpp | 234 +- src/libcadet/model/ParameterMultiplexing.cpp | 575 +-- src/libcadet/model/ParameterMultiplexing.hpp | 509 +-- src/libcadet/model/Parameters.hpp | 1100 ++++-- src/libcadet/model/ReactionModel.hpp | 148 +- src/libcadet/model/StirredTankModel.cpp | 381 +- src/libcadet/model/StirredTankModel.hpp | 357 +- src/libcadet/model/UnitOperation.hpp | 229 +- src/libcadet/model/UnitOperationBase.cpp | 27 +- src/libcadet/model/UnitOperationBase.hpp | 28 +- .../model/binding/AntiLangmuirBinding.cpp | 99 +- .../model/binding/BiLangmuirBinding.cpp | 133 +- .../model/binding/BiLangmuirLDFBinding.cpp | 590 +-- .../binding/BiStericMassActionBinding.cpp | 164 +- .../model/binding/BindingModelBase.cpp | 36 +- .../model/binding/BindingModelBase.hpp | 148 +- .../model/binding/BindingModelMacros.hpp | 126 +- .../model/binding/ColloidalBinding.cpp | 259 +- src/libcadet/model/binding/DummyBinding.cpp | 167 +- ...dedMobilePhaseModulatorLangmuirBinding.cpp | 152 +- .../model/binding/FreundlichLDFBinding.cpp | 270 +- .../binding/GeneralizedIonExchangeBinding.cpp | 255 +- .../HICConstantWaterActivityBinding.cpp | 122 +- .../HICWaterOnHydrophobicSurfacesBinding.cpp | 117 +- .../model/binding/KumarLangmuirBinding.cpp | 122 +- .../model/binding/LangmuirBinding.cpp | 93 +- .../model/binding/LangmuirLDFBinding.cpp | 92 +- .../model/binding/LangmuirLDFCBinding.cpp | 98 +- src/libcadet/model/binding/LinearBinding.cpp | 272 +- .../MobilePhaseModulatorLangmuirBinding.cpp | 129 +- .../MultiComponentSpreadingBinding.cpp | 149 +- .../MultiStateStericMassActionBinding.cpp | 221 +- .../model/binding/RefConcentrationSupport.hpp | 14 +- src/libcadet/model/binding/SaskaBinding.cpp | 88 +- .../model/binding/SelfAssociationBinding.cpp | 156 +- ...ifiedMultiStateStericMassActionBinding.cpp | 187 +- ...ifiedMultiStateStericMassActionBinding.hpp | 168 +- .../model/binding/StericMassActionBinding.cpp | 179 +- .../LinearInterpolationExternalFunction.cpp | 42 +- .../PiecewiseCubicPolyExternalFunction.cpp | 70 +- .../model/inlet/PiecewiseCubicPoly.cpp | 271 +- .../paramdep/DummyParameterDependence.cpp | 274 +- .../paramdep/IdentityParameterDependence.cpp | 146 +- .../LiquidSaltSolidParameterDependence.cpp | 431 ++- .../paramdep/ParameterDependenceBase.cpp | 36 +- .../paramdep/ParameterDependenceBase.hpp | 297 +- .../paramdep/PowerLawParameterDependence.cpp | 56 +- .../parts/AxialConvectionDispersionKernel.cpp | 30 +- .../parts/AxialConvectionDispersionKernel.hpp | 537 +-- .../model/parts/BindingCellKernel.hpp | 124 +- .../parts/ConvectionDispersionOperator.cpp | 478 ++- .../parts/ConvectionDispersionOperator.hpp | 374 +- .../parts/ConvectionDispersionOperatorDG.cpp | 432 ++- .../parts/ConvectionDispersionOperatorDG.hpp | 2669 ++++++++------ src/libcadet/model/parts/DGToolbox.cpp | 150 +- src/libcadet/model/parts/DGToolbox.hpp | 24 +- ...ltiChannelConvectionDispersionOperator.cpp | 896 +++-- ...ltiChannelConvectionDispersionOperator.hpp | 105 +- .../RadialConvectionDispersionKernel.cpp | 30 +- .../RadialConvectionDispersionKernel.hpp | 620 ++-- ...imensionalConvectionDispersionOperator.cpp | 991 ++--- ...imensionalConvectionDispersionOperator.hpp | 142 +- src/libcadet/model/reaction/DummyReaction.cpp | 215 +- .../model/reaction/MassActionLawReaction.cpp | 679 ++-- .../reaction/MichaelisMentenReaction.cpp | 185 +- .../model/reaction/ReactionModelBase.cpp | 22 +- .../model/reaction/ReactionModelBase.hpp | 402 +- .../nonlin/AdaptiveTrustRegionNewton.cpp | 51 +- .../nonlin/AdaptiveTrustRegionNewton.hpp | 802 ++-- src/libcadet/nonlin/CompositeSolver.cpp | 18 +- src/libcadet/nonlin/CompositeSolver.hpp | 56 +- src/libcadet/nonlin/LevenbergMarquardt.cpp | 20 +- src/libcadet/nonlin/LevenbergMarquardt.hpp | 452 +-- src/libcadet/nonlin/Solver.cpp | 36 +- src/libcadet/nonlin/Solver.hpp | 158 +- src/tools/CMakeLists.txt | 5 +- src/tools/FormatConverter.cpp | 26 +- src/tools/FormatConverter.hpp | 8 +- src/tools/ToolsHelper.hpp | 120 +- src/tools/convertFile.cpp | 48 +- src/tools/createConvBenchmark.cpp | 38 +- src/tools/createLWE.cpp | 1113 +++--- src/tools/createMCLin.cpp | 25 +- src/tools/createSCLang.cpp | 20 +- src/tools/createSCLin.cpp | 29 +- src/tools/createSCLinStep.cpp | 32 +- test/AD.cpp | 57 +- test/Approx.hpp | 322 +- test/BandMatrix.cpp | 40 +- test/BindingModelAutoJacobian.cpp | 168 +- test/BindingModelTests.cpp | 102 +- test/BindingModelTests.hpp | 350 +- test/BindingModels.cpp | 606 ++-- test/BindingModels.hpp | 369 +- test/CMakeLists.txt | 5 +- test/CSTR-Residual.cpp | 408 +-- test/CSTR-Simulation.cpp | 166 +- test/CellKernelTests.cpp | 275 +- test/ColumnTests.cpp | 3219 +++++++++-------- test/ColumnTests.hpp | 804 ++-- test/ConvectionDispersionOperator.cpp | 466 +-- test/DenseMatrix.cpp | 32 +- test/Dummies.hpp | 228 +- test/GeneralRateModel.cpp | 196 +- test/GeneralRateModel2D.cpp | 139 +- test/GeneralRateModelDG.cpp | 246 +- test/Graph.cpp | 372 +- test/Histogram.cpp | 163 +- test/JacobianHelper.hpp | 130 +- test/JsonTestModels.cpp | 228 +- test/JsonTestModels.hpp | 20 +- test/LogUtils.cpp | 5 +- test/LumpedRateModelWithPores.cpp | 198 +- test/LumpedRateModelWithPoresDG.cpp | 261 +- test/LumpedRateModelWithoutPores.cpp | 128 +- test/LumpedRateModelWithoutPoresDG.cpp | 123 +- test/MatrixHelper.hpp | 11 +- test/ModelSystem.cpp | 1556 ++++---- test/MultiChannelTransportModel.cpp | 532 +-- test/ParamDepTests.cpp | 46 +- test/ParamDepTests.hpp | 186 +- test/ParameterDependencies.cpp | 16 +- test/ParameterDependencies.hpp | 40 +- test/ParticleHelper.cpp | 638 ++-- test/ParticleHelper.hpp | 423 +-- test/Paths.cpp.in | 4 +- test/RadialGeneralRateModel.cpp | 180 +- test/RadialLumpedRateModelWithPores.cpp | 219 +- test/RadialLumpedRateModelWithoutPores.cpp | 97 +- test/ReactionModelTests.cpp | 798 ++-- test/ReactionModelTests.hpp | 338 +- test/ReactionModels.cpp | 45 +- test/SimHelper.cpp | 420 +-- test/SimHelper.hpp | 324 +- test/SparseFactorizableMatrix.cpp | 335 +- test/SparseMatrix.cpp | 142 +- test/StringHashing.cpp | 410 ++- test/Subset.cpp | 4 +- test/TimeIntegrator.cpp | 791 ++-- test/TimeIntegrator.hpp | 97 +- test/TwoDimConvectionDispersionOperator.cpp | 128 +- test/UnitOperationTests.cpp | 544 +-- test/UnitOperationTests.hpp | 123 +- test/Utils.hpp | 285 +- ...ation_CSTR_MichaelisMenten_benchmark1.json | 2 +- ...ation_CSTR_MichaelisMenten_benchmark2.json | 2 +- ...tion_CSTR_MicroKineticsSMA_benchmark1.json | 2 +- ...gence_GRM_dynLin_1comp_sensbenchmark1.json | 2 +- ...gence_GRM_reqSMA_4comp_sensbenchmark1.json | 2 +- ...ence_LRMP_dynLin_1comp_sensbenchmark1.json | 2 +- ...ence_LRMP_reqSMA_4comp_sensbenchmark1.json | 2 +- ...gence_LRM_dynLin_1comp_sensbenchmark1.json | 2 +- ...gence_LRM_reqSMA_4comp_sensbenchmark1.json | 2 +- ...ce_radGRM_dynLin_1comp_sensbenchmark1.json | 2 +- ...e_radLRMP_dynLin_1comp_sensbenchmark1.json | 2 +- ...ce_radLRM_dynLin_1comp_sensbenchmark1.json | 2 +- .../model_GRM_dynLin_1comp_benchmark1.json | 2 +- .../model_GRM_reqSMA_4comp_benchmark1.json | 2 +- .../model_LRMP_dynLin_1comp_benchmark1.json | 2 +- .../model_LRMP_reqSMA_4comp_benchmark1.json | 2 +- .../model_LRM_dynLin_1comp_benchmark1.json | 2 +- .../model_LRM_noBnd_1comp_MCTbenchmark.json | 2 +- .../model_LRM_reqSMA_4comp_benchmark1.json | 2 +- .../model_MCT1ch_noEx_noReac_benchmark1.json | 2 +- .../model_MCT1ch_noEx_reac_benchmark1.json | 2 +- test/data/model_MCT2ch_1comp_benchmark1.json | 2 +- ...model_MCT2ch_oneWayEx_reac_benchmark1.json | 2 +- ...odel_MCT3ch_twoWayExc_reac_benchmark1.json | 2 +- ...el_radGRM_dynLin_1comp_sensbenchmark1.json | 2 +- ...l_radLRMP_dynLin_1comp_sensbenchmark1.json | 2 +- ...el_radLRM_dynLin_1comp_sensbenchmark1.json | 2 +- test/smaNonlinearProblem.m | 8 +- test/testAdaptiveTRNewton.cpp | 109 +- test/testCAPIv1.cpp | 130 +- test/testCompileTimeMurmur.cpp | 303 +- test/testLogging.cpp | 12 +- test/testRadialKernel.cpp | 171 +- test/testRunner.cpp | 65 +- test/testSMANonlinearSolve.cpp | 105 +- 429 files changed, 53804 insertions(+), 40620 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index b578f71da..c0f925def 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -20,4 +20,4 @@ A clear and concise description of any alternative solutions or features you've Add any other context or screenshots about the feature request here. **Internal** -For CADET Team members: Please assign a label, an assignee, and a topic in the project. +For CADET Team members: Please assign a label, an assignee, and a topic in the project. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2431ba25b..d52ccd1ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -203,4 +203,3 @@ jobs: ${INSTALL_PREFIX}/bin/cadet-cli --version || true ${INSTALL_PREFIX}/bin/createLWE ${INSTALL_PREFIX}/bin/cadet-cli LWE.h5 || true - diff --git a/BUILD-OSX.md b/BUILD-OSX.md index b75222261..5912808d0 100644 --- a/BUILD-OSX.md +++ b/BUILD-OSX.md @@ -31,7 +31,7 @@ brew install eigen ### LAPACK -You can either use the native LAPACK implementation provided by Mac OS X (vecLib, Accelerate) +You can either use the native LAPACK implementation provided by Mac OS X (vecLib, Accelerate) or install the freely available [Intel MKL](https://software.intel.com/sites/campaigns/nest/) which is very fast and probably faster than Accelerate. ## Build CADET @@ -41,10 +41,9 @@ or install the freely available [Intel MKL](https://software.intel.com/sites/cam * If using Intel MKL, execute `export MKLROOT=/opt/intel/mkl` * Using standard LAPACK: Execute `cmake -DCMAKE_INSTALL_PREFIX="/install" ../` - + Using MKL (sequential): Execute `cmake -DCMAKE_INSTALL_PREFIX="/install" -DBLA_VENDOR=Intel10_64lp_seq ../` - + Using MKL (parallel): Execute `cmake -DCMAKE_INSTALL_PREFIX="/install" -DBLA_VENDOR=Intel10_64lp ../` * Execute `make` * Execute `make install` - diff --git a/BUILD-WINDOWS.md b/BUILD-WINDOWS.md index edfc4328a..b44e9e8f5 100644 --- a/BUILD-WINDOWS.md +++ b/BUILD-WINDOWS.md @@ -5,10 +5,10 @@ * Optional: Git * Optional but not generally recommended*: Intel OneAPI TBB -*For most use-cases it is more efficient to parallelize by running multiple CADET simulations instead +*For most use-cases it is more efficient to parallelize by running multiple CADET simulations instead of parallelizing within one CADET simulation. Including the parallelization code in CADET can lead to performance -losses, even if parallelization within CADET is not used. -Therefore, we recommend not including the parallelization library TBB +losses, even if parallelization within CADET is not used. +Therefore, we recommend not including the parallelization library TBB unless you know your simulations are large enough to benefit from it. Assumed directory structure: @@ -46,7 +46,7 @@ We are using Visual Studio because it is the easiest way to install all required ## Prepare CADET code -- Clone the CADET source code into a `CADET` folder: +- Clone the CADET source code into a `CADET` folder: - `git clone https://github.com/modsim/CADET.git CADET` - Create the directories `CADET\build` and `CADET\install` @@ -78,7 +78,7 @@ We are using Visual Studio because it is the easiest way to install all required execute `set TBBROOT="C:/Program Files (x86)/Intel/oneAPI/tbb/latest"` and - `cmake -DCMAKE_INSTALL_PREFIX=..\out\install\aRELEASE -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows-static -DENABLE_STATIC_LINK_LAPACK=ON -DENABLE_STATIC_LINK_DEPS=ON -DBLA_VENDOR=Intel10_64lp --fresh ../` - `msbuild.exe INSTALL.vcxproj /p:Configuration=Release;Platform=x64` - + - For PowerShell: - `cd CADET\build` - `vcpkg integrate install` (this only needs to be run _once_ and will require admin privileges) @@ -86,7 +86,7 @@ We are using Visual Studio because it is the easiest way to install all required - `cmake -DCMAKE_INSTALL_PREFIX="..\out\install\aRELEASE" -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$ENV:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows-static -DENABLE_STATIC_LINK_LAPACK=ON -DENABLE_STATIC_LINK_DEPS=ON -DBLA_VENDOR=Intel10_64lp_seq "../" --fresh` - If you want to use parallelization and have installed TBB, instead execute `$ENV:TBBROOT = "C:\Program Files (x86)\Intel\oneAPI\tbb\latest"` - and + and - `cmake -DCMAKE_INSTALL_PREFIX="..\out\install\aRELEASE" -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$ENV:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows-static -DENABLE_STATIC_LINK_LAPACK=ON -DENABLE_STATIC_LINK_DEPS=ON -DBLA_VENDOR=Intel10_64lp "../" --fresh` - `msbuild.exe INSTALL.vcxproj /p:Configuration="Release;Platform=x64"` - The binaries will be located in `CADET\out\install\aRELEASE\bin` @@ -99,4 +99,4 @@ We are using Visual Studio because it is the easiest way to install all required - `cadet-cli.exe LWE.h5` - And confirm the output of the LWE.h5 by opening it in HDF5view or loading it in CADET-Process. - If you get no printed return from the first command, run cadet-cli.exe by double-clicking it in the file explorer. -This raises error messages that are not raised from a cmd or PowerShell window. \ No newline at end of file +This raises error messages that are not raised from a cmd or PowerShell window. diff --git a/CMakeLists.txt b/CMakeLists.txt index 08d129a13..aa1bfe08d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,15 +1,15 @@ # ============================================================================= # CADET -# +# # Copyright © The CADET Authors # Please see the CONTRIBUTORS.md file. -# +# # All rights reserved. This program and the accompanying materials # are made available under the terms of the GNU Public License v3.0 (or, at # your option, any later version) which accompanies this distribution, and # is available at http://www.gnu.org/licenses/gpl.html # ============================================================================= - + # Require a fairly new cmake version cmake_minimum_required(VERSION 3.12) @@ -308,7 +308,7 @@ if (ENABLE_CADET_TOOLS OR ENABLE_CADET_CLI) set(HDF5_SUPPORT_LIBRARIES ${HDF5_C_LIBRARIES}) list(REMOVE_AT HDF5_SUPPORT_LIBRARIES 0) else() - set(HDF5_MAIN_LIBRARY ${HDF5_C_LIBRARIES}) + set(HDF5_MAIN_LIBRARY ${HDF5_C_LIBRARIES}) set(HDF5_SUPPORT_LIBRARIES) endif() @@ -546,7 +546,7 @@ set(CPACK_SOURCE_IGNORE_FILES message("") message("--------------------------- Feature Summary ---------------------------") - + feature_summary(WHAT ALL) # Summary diff --git a/CMakeSettings.json b/CMakeSettings.json index 90e80ca68..52671b657 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -189,4 +189,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index dfaf4259f..31d103a5c 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -22,4 +22,3 @@ Salah Azzouzi (Forschungszentrum Juelich GmbH, IBG-1: Biotechnology, Juelich, Ge * Johannes Schmölder has received support from the IMI2/ EU/EFPIA joint undertaking Inno4Vac (grant no. 101007799). Follow the lead, become a contributor by sending a pull request or a patch. - diff --git a/LICENSE-ThirdParty.txt b/LICENSE-ThirdParty.txt index 519d6c383..c5a0b542c 100644 --- a/LICENSE-ThirdParty.txt +++ b/LICENSE-ThirdParty.txt @@ -3,7 +3,7 @@ #################################################################################### -Copyright (c) 2002-2016, Lawrence Livermore National Security. +Copyright (c) 2002-2016, Lawrence Livermore National Security. Produced at the Lawrence Livermore National Laboratory. Written by A.C. Hindmarsh, D.R. Reynolds, R. Serban, C.S. Woodward, S.D. Cohen, A.G. Taylor, S. Peles, L.E. Banks, and D. Shumaker. @@ -13,9 +13,9 @@ UCRL-CODE-155950 (CVODES) UCRL-CODE-155952 (IDA) UCRL-CODE-237203 (IDAS) LLNL-CODE-665877 (KINSOL) -All rights reserved. +All rights reserved. -This file is part of SUNDIALS. For details, +This file is part of SUNDIALS. For details, see http://computation.llnl.gov/projects/sundials Redistribution and use in source and binary forms, with or without @@ -34,40 +34,40 @@ distribution. may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -LAWRENCE LIVERMORE NATIONAL SECURITY, LLC, THE U.S. DEPARTMENT OF -ENERGY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +LAWRENCE LIVERMORE NATIONAL SECURITY, LLC, THE U.S. DEPARTMENT OF +ENERGY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Additional BSD Notice --------------------- 1. This notice is required to be provided under our contract with the U.S. Department of Energy (DOE). This work was produced at -Lawrence Livermore National Laboratory under Contract +Lawrence Livermore National Laboratory under Contract No. DE-AC52-07NA27344 with the DOE. -2. Neither the United States Government nor Lawrence Livermore -National Security, LLC nor any of their employees, makes any warranty, +2. Neither the United States Government nor Lawrence Livermore +National Security, LLC nor any of their employees, makes any warranty, express or implied, or assumes any liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe privately-owned rights. -3. Also, reference herein to any specific commercial products, process, -or services by trade name, trademark, manufacturer or otherwise does -not necessarily constitute or imply its endorsement, recommendation, -or favoring by the United States Government or Lawrence Livermore -National Security, LLC. The views and opinions of authors expressed -herein do not necessarily state or reflect those of the United States -Government or Lawrence Livermore National Security, LLC, and shall +3. Also, reference herein to any specific commercial products, process, +or services by trade name, trademark, manufacturer or otherwise does +not necessarily constitute or imply its endorsement, recommendation, +or favoring by the United States Government or Lawrence Livermore +National Security, LLC. The views and opinions of authors expressed +herein do not necessarily state or reflect those of the United States +Government or Lawrence Livermore National Security, LLC, and shall not be used for advertising or product endorsement purposes. @@ -76,94 +76,94 @@ not be used for advertising or product endorsement purposes. #################################################################################### -Copyright Notice and License Terms for +Copyright Notice and License Terms for HDF5 (Hierarchical Data Format 5) Software Library and Utilities ----------------------------------------------------------------------------- HDF5 (Hierarchical Data Format 5) Software Library and Utilities -Copyright (c) 2006-2018, The HDF Group. +Copyright (c) 2006-2018, The HDF Group. NCSA HDF5 (Hierarchical Data Format 5) Software Library and Utilities -Copyright (c) 1998-2006, The Board of Trustees of the University of Illinois. +Copyright (c) 1998-2006, The Board of Trustees of the University of Illinois. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted for any purpose (including commercial purposes) +Redistribution and use in source and binary forms, with or without +modification, are permitted for any purpose (including commercial purposes) provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, +1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions, and the following disclaimer in the documentation +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions, and the following disclaimer in the documentation and/or materials provided with the distribution. -3. Neither the name of The HDF Group, the name of the University, nor the - name of any Contributor may be used to endorse or promote products derived - from this software without specific prior written permission from +3. Neither the name of The HDF Group, the name of the University, nor the + name of any Contributor may be used to endorse or promote products derived + from this software without specific prior written permission from The HDF Group, the University, or the Contributor, respectively. -DISCLAIMER: -THIS SOFTWARE IS PROVIDED BY THE HDF GROUP AND THE CONTRIBUTORS +DISCLAIMER: +THIS SOFTWARE IS PROVIDED BY THE HDF GROUP AND THE CONTRIBUTORS "AS IS" WITH NO WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED. IN NO EVENT SHALL THE HDF GROUP OR THE CONTRIBUTORS BE LIABLE FOR ANY DAMAGES SUFFERED BY THE USERS ARISING OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + You are under no obligation whatsoever to provide any bug fixes, patches, or upgrades to the features, functionality or performance of the source code ("Enhancements") to anyone; however, if you choose to make your Enhancements available either publicly, or directly to The HDF Group, without imposing a separate written license agreement for such Enhancements, then you hereby grant the following license: a non-exclusive, royalty-free perpetual license to install, use, modify, prepare derivative works, incorporate into other computer software, distribute, and sublicense such enhancements or derivative works thereof, in binary and source code form. ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- -Limited portions of HDF5 were developed by Lawrence Berkeley National +Limited portions of HDF5 were developed by Lawrence Berkeley National Laboratory (LBNL). LBNL's Copyright Notice and Licensing Terms can be -found here: COPYING_LBNL_HDF5 file in this directory or at -http://support.hdfgroup.org/ftp/HDF5/releases/COPYING_LBNL_HDF5. +found here: COPYING_LBNL_HDF5 file in this directory or at +http://support.hdfgroup.org/ftp/HDF5/releases/COPYING_LBNL_HDF5. ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- -Contributors: National Center for Supercomputing Applications (NCSA) at +Contributors: National Center for Supercomputing Applications (NCSA) at the University of Illinois, Fortner Software, Unidata Program Center (netCDF), The Independent JPEG Group (JPEG), Jean-loup Gailly and Mark Adler (gzip), and Digital Equipment Corporation (DEC). ----------------------------------------------------------------------------- - -Portions of HDF5 were developed with support from the Lawrence Berkeley -National Laboratory (LBNL) and the United States Department of Energy + +Portions of HDF5 were developed with support from the Lawrence Berkeley +National Laboratory (LBNL) and the United States Department of Energy under Prime Contract No. DE-AC02-05CH11231. ----------------------------------------------------------------------------- -Portions of HDF5 were developed with support from the University of -California, Lawrence Livermore National Laboratory (UC LLNL). -The following statement applies to those portions of the product and must -be retained in any redistribution of source code, binaries, documentation, +Portions of HDF5 were developed with support from the University of +California, Lawrence Livermore National Laboratory (UC LLNL). +The following statement applies to those portions of the product and must +be retained in any redistribution of source code, binaries, documentation, and/or accompanying materials: - This work was partially produced at the University of California, - Lawrence Livermore National Laboratory (UC LLNL) under contract - no. W-7405-ENG-48 (Contract 48) between the U.S. Department of Energy - (DOE) and The Regents of the University of California (University) + This work was partially produced at the University of California, + Lawrence Livermore National Laboratory (UC LLNL) under contract + no. W-7405-ENG-48 (Contract 48) between the U.S. Department of Energy + (DOE) and The Regents of the University of California (University) for the operation of UC LLNL. - DISCLAIMER: - This work was prepared as an account of work sponsored by an agency of - the United States Government. Neither the United States Government nor - the University of California nor any of their employees, makes any - warranty, express or implied, or assumes any liability or responsibility - for the accuracy, completeness, or usefulness of any information, - apparatus, product, or process disclosed, or represents that its use - would not infringe privately- owned rights. Reference herein to any - specific commercial products, process, or service by trade name, - trademark, manufacturer, or otherwise, does not necessarily constitute - or imply its endorsement, recommendation, or favoring by the United - States Government or the University of California. The views and - opinions of authors expressed herein do not necessarily state or reflect - those of the United States Government or the University of California, + DISCLAIMER: + This work was prepared as an account of work sponsored by an agency of + the United States Government. Neither the United States Government nor + the University of California nor any of their employees, makes any + warranty, express or implied, or assumes any liability or responsibility + for the accuracy, completeness, or usefulness of any information, + apparatus, product, or process disclosed, or represents that its use + would not infringe privately- owned rights. Reference herein to any + specific commercial products, process, or service by trade name, + trademark, manufacturer, or otherwise, does not necessarily constitute + or imply its endorsement, recommendation, or favoring by the United + States Government or the University of California. The views and + opinions of authors expressed herein do not necessarily state or reflect + those of the United States Government or the University of California, and shall not be used for advertising or product endorsement purposes. ----------------------------------------------------------------------------- -HDF5 is available with the SZIP compression library but SZIP is not part -of HDF5 and has separate copyright and license terms. See SZIP Compression +HDF5 is available with the SZIP compression library but SZIP is not part +of HDF5 and has separate copyright and license terms. See SZIP Compression in HDF Products (www.hdfgroup.org/doc_resource/SZIP/) for further details. ----------------------------------------------------------------------------- @@ -175,19 +175,19 @@ in HDF Products (www.hdfgroup.org/doc_resource/SZIP/) for further details. Copyright (c) 2003, The Regents of the University of California, through -Lawrence Berkeley National Laboratory (subject to receipt of any required -approvals from U.S. Dept. of Energy) +Lawrence Berkeley National Laboratory (subject to receipt of any required +approvals from U.S. Dept. of Energy) -All rights reserved. +All rights reserved. Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +modification, are permitted provided that the following conditions are met: (1) Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. +this list of conditions and the following disclaimer. (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. +and/or other materials provided with the distribution. (3) Neither the name of Lawrence Berkeley National Laboratory, U.S. Dept. of Energy nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. @@ -202,7 +202,7 @@ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #################################################################################### @@ -509,8 +509,8 @@ http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -implied. See the License for the specific language governing +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. See the License for the specific language governing permissions and limitations under the License. @@ -817,26 +817,26 @@ OTHER DEALINGS IN THE SOFTWARE. #################################################################################### -Copyright (c) 2003 Michael E. Smoot +Copyright (c) 2003 Michael E. Smoot -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, +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 +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 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/README.rst b/README.rst index 22d66434a..ae9ef0642 100644 --- a/README.rst +++ b/README.rst @@ -17,7 +17,7 @@ CADET - **Forum:** https://forum.cadet-web.de - **Source:** https://github.com/modsim/cadet - **Bug reports:** https://github.com/modsim/cadet/issues -- **Demo:** https://www.cadet-web.de +- **Demo:** https://www.cadet-web.de - **Newsletter:** https://cadet-web.de/newsletter/ Installation @@ -79,7 +79,7 @@ We do our best to provide you with a stable API. However, CADET is actively deve Bugs ---- -Please report any bugs that you find `here `_. Or, even better, fork the repository on `GitHub `_ and create a pull request (PR) with the fix. +Please report any bugs that you find `here `_. Or, even better, fork the repository on `GitHub `_ and create a pull request (PR) with the fix. Donations --------- diff --git a/cmake/Modules/FindCADET.cmake b/cmake/Modules/FindCADET.cmake index 66b97049e..6647513d6 100644 --- a/cmake/Modules/FindCADET.cmake +++ b/cmake/Modules/FindCADET.cmake @@ -1,9 +1,9 @@ # ============================================================================= # CADET -# +# # Copyright © The CADET Authors # Please see the CONTRIBUTORS.md file. -# +# # All rights reserved. This program and the accompanying materials # are made available under the terms of the GNU Public License v3.0 (or, at # your option, any later version) which accompanies this distribution, and diff --git a/cmake/Modules/FindSUNDIALS.cmake b/cmake/Modules/FindSUNDIALS.cmake index 31630d858..f5fe7cc5f 100644 --- a/cmake/Modules/FindSUNDIALS.cmake +++ b/cmake/Modules/FindSUNDIALS.cmake @@ -1,9 +1,9 @@ # ============================================================================= # CADET -# +# # Copyright © The CADET Authors # Please see the CONTRIBUTORS.md file. -# +# # All rights reserved. This program and the accompanying materials # are made available under the terms of the GNU Public License v3.0 (or, at # your option, any later version) which accompanies this distribution, and diff --git a/cmake/Modules/FindSuperLU.cmake b/cmake/Modules/FindSuperLU.cmake index 6f801018f..8e13b95d4 100644 --- a/cmake/Modules/FindSuperLU.cmake +++ b/cmake/Modules/FindSuperLU.cmake @@ -1,9 +1,9 @@ # ============================================================================= # CADET -# +# # Copyright © The CADET Authors # Please see the CONTRIBUTORS.md file. -# +# # All rights reserved. This program and the accompanying materials # are made available under the terms of the GNU Public License v3.0 (or, at # your option, any later version) which accompanies this distribution, and @@ -66,7 +66,7 @@ if (SUPERLU_INCLUDE_DIRS) string(REGEX REPLACE ".*#define SUPERLU_MINOR_VERSION [ \t\r\n]* ([0-9]+).*" "\\1" SUPERLU_VERSION_MINOR "${_SUPERLU_VERSION_FILE}") string(REGEX REPLACE ".*#define SUPERLU_PATCH_VERSION [ \t\r\n]* ([0-9]+).*" "\\1" SUPERLU_VERSION_PATCH "${_SUPERLU_VERSION_FILE}") set(SUPERLU_VERSION "${SUPERLU_VERSION_MAJOR}.${SUPERLU_VERSION_MINOR}.${SUPERLU_VERSION_PATCH}") - + # extract int type if (SUPERLU_VERSION_MAJOR GREATER_EQUAL 6) file(READ "${SUPERLU_INCLUDE_DIRS}/superlu_config.h" _SUPERLU_INTTYPE_FILE) @@ -108,7 +108,7 @@ find_library(SUPERLU_LIBRARY ENV SUPERLU_ROOT SUPERLU_ROOT_DIR - PATH_SUFFIXES + PATH_SUFFIXES lib lib64 Lib diff --git a/cmake/Modules/FindTBB.cmake b/cmake/Modules/FindTBB.cmake index 3babcd4c6..8c1f63272 100644 --- a/cmake/Modules/FindTBB.cmake +++ b/cmake/Modules/FindTBB.cmake @@ -3,17 +3,17 @@ # Copyright (c) 2021 Samuel Leweke, Bayer AG # Copyright (c) 2019 Samuel Leweke, Forschungszentrum Juelich GmbH # Copyright (c) 2015 Justus Calvin -# +# # 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 @@ -33,16 +33,16 @@ # find_package(TBB [major[.minor]] [EXACT] # [QUIET] [REQUIRED] # [[COMPONENTS] [components...]] -# [OPTIONAL_COMPONENTS components...]) +# [OPTIONAL_COMPONENTS components...]) # -# where the allowed components are tbbmalloc and tbb_preview. Users may modify +# where the allowed components are tbbmalloc and tbb_preview. Users may modify # the behavior of this module with the following variables: # # * TBB_ROOT_DIR - The base directory of the TBB installation. # * TBB_INCLUDE_DIR - The directory that contains the TBB headers files. # * TBB_LIBRARY - The directory that contains the TBB library files. -# * TBB__LIBRARY - The path of the TBB the corresponding TBB library. -# These libraries, if specified, override the +# * TBB__LIBRARY - The path of the TBB the corresponding TBB library. +# These libraries, if specified, override the # corresponding library search results, where # may be tbb, tbb_debug, tbbmalloc, tbbmalloc_debug, # tbb_preview, or tbb_preview_debug. @@ -53,7 +53,7 @@ # Users may modify the behavior of this module with the following environment # variables: # -# * TBB_INSTALL_DIR +# * TBB_INSTALL_DIR # * TBBROOT # * TBB_ROOT # * LIBRARY_PATH @@ -67,15 +67,15 @@ # * TBB_VERSION - The full version string # * TBB_VERSION_MAJOR - The major version # * TBB_VERSION_MINOR - The minor version -# * TBB_INTERFACE_VERSION - The interface version number defined in +# * TBB_INTERFACE_VERSION - The interface version number defined in # tbb/tbb_stddef.h. -# * TBB__LIBRARY_RELEASE - The path of the TBB release version of +# * TBB__LIBRARY_RELEASE - The path of the TBB release version of # , where may be tbb, tbb_debug, -# tbbmalloc, tbbmalloc_debug, tbb_preview, or +# tbbmalloc, tbbmalloc_debug, tbb_preview, or # tbb_preview_debug. -# * TBB__LIBRARY_DEGUG - The path of the TBB release version of +# * TBB__LIBRARY_DEGUG - The path of the TBB release version of # , where may be tbb, tbb_debug, -# tbbmalloc, tbbmalloc_debug, tbb_preview, or +# tbbmalloc, tbbmalloc_debug, tbb_preview, or # tbb_preview_debug. # # The following varibles should be used to build and link with TBB: @@ -91,7 +91,7 @@ # * TBB_DEFINITIONS_DEBUG - Definitions to use when compiling debug code that # uses TBB. # -# This module will also create the TBB::TBB, TBB::TBBpreview, and TBB::TBBmalloc +# This module will also create the TBB::TBB, TBB::TBBpreview, and TBB::TBBmalloc # targets that may be used when building executables and libraries. include(FindPackageHandleStandardArgs) @@ -101,7 +101,7 @@ if(NOT TBB_FOUND) ################################## # Check the build type ################################## - + if(NOT DEFINED TBB_USE_DEBUG_BUILD) if(CMAKE_BUILD_TYPE MATCHES "(Debug|DEBUG|debug|RelWithDebInfo|RELWITHDEBINFO|relwithdebinfo)") set(TBB_BUILD_TYPE DEBUG) @@ -117,7 +117,7 @@ if(NOT TBB_FOUND) if(NOT DEFINED TBB_PREFER_STATIC_LIBS) set(TBB_PREFER_STATIC_LIBS OFF) endif() - + # Prefer static libs by prioritizing .lib and .a suffixes in CMAKE_FIND_LIBRARY_SUFFIXES if(TBB_PREFER_STATIC_LIBS) set(_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) @@ -127,7 +127,7 @@ if(NOT TBB_FOUND) list(INSERT CMAKE_FIND_LIBRARY_SUFFIXES 0 .a) endif() endif() - + ################################## # Set the TBB search directories ################################## @@ -139,10 +139,10 @@ if(NOT TBB_FOUND) set(PKGCONFIG_TBB_INCLUDE_DIRS "") set(PKGCONFIG_TBB_LIBRARY_DIRS "") endif() - + # Define search paths based on user input and environment variables set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT} $ENV{TBB_ROOT}) - + # Define the search directories based on the current platform if(CMAKE_SYSTEM_NAME STREQUAL "Windows") set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB" @@ -176,7 +176,7 @@ if(NOT TBB_FOUND) # OS X set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb" "/usr/local/opt/tbb") - + # TODO: Check to see which C++ library is being used by the compiler. if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0) # The default C++ library on OS X 10.9 and later is libc++ @@ -187,7 +187,7 @@ if(NOT TBB_FOUND) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") # Linux set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") - + # TODO: Check compiler version to see the suffix should be /gcc4.1 or # /gcc4.1. For now, assume that the compiler is more recent than # gcc 4.4.x or later. @@ -197,11 +197,11 @@ if(NOT TBB_FOUND) set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.1" "lib/ia32/gcc4.4" "lib/ia32/gcc4.8") endif() endif() - + ################################## # Find the TBB include dir ################################## - + find_path(TBB_INCLUDE_DIRS tbb/tbb.h HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR} PATHS ${TBB_DEFAULT_SEARCH_DIR} ${PKGCONFIG_TBB_INCLUDE_DIRS} @@ -286,7 +286,7 @@ if(NOT TBB_FOUND) # via #pragma comment(lib, ...) on MS Windows when using MSVC set(TBB_DEFINITIONS_RELEASE "__TBB_NO_IMPLICIT_LINKAGE=1") set(TBB_DEFINITIONS_DEBUG "TBB_USE_DEBUG=1;__TBB_NO_IMPLICIT_LINKAGE=1") - + if(TBB_LIBRARIES_${TBB_BUILD_TYPE}) set(TBB_DEFINITIONS "${TBB_DEFINITIONS_${TBB_BUILD_TYPE}}") set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}") @@ -298,7 +298,7 @@ if(NOT TBB_FOUND) set(TBB_LIBRARIES "${TBB_LIBRARIES_DEBUG}") endif() - find_package_handle_standard_args(TBB + find_package_handle_standard_args(TBB REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES HANDLE_COMPONENTS VERSION_VAR TBB_VERSION) @@ -319,7 +319,7 @@ if(NOT TBB_FOUND) IMPORTED_LOCATION_MINSIZEREL "${TBB_tbb_LIBRARY_RELEASE}" ) elseif(TBB_LIBRARIES_RELEASE) - set_target_properties(TBB::TBB PROPERTIES + set_target_properties(TBB::TBB PROPERTIES INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS_RELEASE}" IMPORTED_LOCATION "${TBB_tbb_LIBRARY_RELEASE}" ) @@ -342,7 +342,7 @@ if(NOT TBB_FOUND) IMPORTED_LOCATION_MINSIZEREL "${TBB_tbb_preview_LIBRARY_RELEASE}" ) elseif(TBB_LIBRARIES_RELEASE) - set_target_properties(TBB::TBBpreview PROPERTIES + set_target_properties(TBB::TBBpreview PROPERTIES INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS_RELEASE}" IMPORTED_LOCATION "${TBB_tbb_preview_LIBRARY_RELEASE}" ) @@ -366,7 +366,7 @@ if(NOT TBB_FOUND) IMPORTED_LOCATION_MINSIZEREL "${TBB_tbb_malloc_LIBRARY_RELEASE}" ) elseif(TBB_LIBRARIES_RELEASE) - set_target_properties(TBB::TBBmalloc PROPERTIES + set_target_properties(TBB::TBBmalloc PROPERTIES INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS_RELEASE}" IMPORTED_LOCATION "${TBB_tbb_malloc_LIBRARY_RELEASE}" ) @@ -406,4 +406,4 @@ if(NOT TBB_FOUND) unset(TBB_LIB_PATH_SUFFIX) unset(TBB_DEFAULT_SEARCH_DIR) -endif() \ No newline at end of file +endif() diff --git a/cmake/Modules/FindUMFPACK.cmake b/cmake/Modules/FindUMFPACK.cmake index 399755eca..1213b1f26 100644 --- a/cmake/Modules/FindUMFPACK.cmake +++ b/cmake/Modules/FindUMFPACK.cmake @@ -1,9 +1,9 @@ # ============================================================================= # CADET -# +# # Copyright © The CADET Authors # Please see the CONTRIBUTORS.md file. -# +# # All rights reserved. This program and the accompanying materials # are made available under the terms of the GNU Public License v3.0 (or, at # your option, any later version) which accompanies this distribution, and @@ -93,7 +93,7 @@ find_library(UMFPACK_LIBRARY ENV UMFPACK_ROOT UMFPACK_ROOT_DIR - PATH_SUFFIXES + PATH_SUFFIXES lib lib64 Lib @@ -113,7 +113,7 @@ find_library(CHOLMOD_LIBRARY ENV UMFPACK_ROOT UMFPACK_ROOT_DIR - PATH_SUFFIXES + PATH_SUFFIXES lib lib64 Lib @@ -132,7 +132,7 @@ find_library(COLAMD_LIBRARY ENV UMFPACK_ROOT UMFPACK_ROOT_DIR - PATH_SUFFIXES + PATH_SUFFIXES lib lib64 Lib @@ -151,7 +151,7 @@ find_library(CCOLAMD_LIBRARY ENV UMFPACK_ROOT UMFPACK_ROOT_DIR - PATH_SUFFIXES + PATH_SUFFIXES lib lib64 Lib @@ -170,7 +170,7 @@ find_library(CAMD_LIBRARY ENV UMFPACK_ROOT UMFPACK_ROOT_DIR - PATH_SUFFIXES + PATH_SUFFIXES lib lib64 Lib @@ -189,7 +189,7 @@ find_library(AMD_LIBRARY ENV UMFPACK_ROOT UMFPACK_ROOT_DIR - PATH_SUFFIXES + PATH_SUFFIXES lib lib64 Lib @@ -208,7 +208,7 @@ find_library(CONF_LIBRARY ENV UMFPACK_ROOT UMFPACK_ROOT_DIR - PATH_SUFFIXES + PATH_SUFFIXES lib lib64 Lib @@ -233,7 +233,7 @@ find_library(METIS_LIBRARY ENV UMFPACK_ROOT UMFPACK_ROOT_DIR - PATH_SUFFIXES + PATH_SUFFIXES lib lib64 Lib @@ -257,7 +257,7 @@ find_library(GK_LIBRARY ENV UMFPACK_ROOT UMFPACK_ROOT_DIR - PATH_SUFFIXES + PATH_SUFFIXES lib lib64 Lib @@ -335,7 +335,7 @@ if (UMFPACK_FOUND AND NOT TARGET UMFPACK::UMFPACK) IMPORTED_LOCATION ${UMFPACK_LIBRARY} ) - target_link_libraries(UMFPACK::UMFPACK INTERFACE + target_link_libraries(UMFPACK::UMFPACK INTERFACE UMFPACK::COLAMD UMFPACK::CCOLAMD UMFPACK::AMD diff --git a/cmake/Modules/MatlabTBBversion.cpp b/cmake/Modules/MatlabTBBversion.cpp index 2a298dbba..c5e5fd770 100644 --- a/cmake/Modules/MatlabTBBversion.cpp +++ b/cmake/Modules/MatlabTBBversion.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and diff --git a/doc/README.md b/doc/README.md index a9ee5ff35..c14503ef6 100644 --- a/doc/README.md +++ b/doc/README.md @@ -10,12 +10,12 @@ pip install -r requirements.txt Then, in the `doc` folder run: -`sphinx-build -b html . build` +`sphinx-build -b html . build` The output is in the `build` directory and can be opened with any browser. To build the documentation for all releases and the master branch, run: -`sphinx-multiversion ./ ./build/`. +`sphinx-multiversion ./ ./build/`. Any changes to the documentation will automatically be pushed to the github-pages repository (https://github.com/cadet/cadet.github.io) using github actions. diff --git a/doc/_static/css/custom.css b/doc/_static/css/custom.css index 2c242be84..46bd98f32 100644 --- a/doc/_static/css/custom.css +++ b/doc/_static/css/custom.css @@ -9,4 +9,3 @@ div.sphinxsidebar { height: 100vh; overflow: auto; } - diff --git a/doc/conf.py b/doc/conf.py index 8b708082b..352f3d288 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -92,4 +92,3 @@ } html_style = 'css/custom.css' - diff --git a/doc/developer_guide/TemplateBinding.cpp b/doc/developer_guide/TemplateBinding.cpp index 882068e93..db8ff8f6f 100644 --- a/doc/developer_guide/TemplateBinding.cpp +++ b/doc/developer_guide/TemplateBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -24,7 +24,7 @@ #include /* In the following lines of code the interface to the binding model is defined. - In the interface the binding model specific parameters are defined. Please modify configuration name and + In the interface the binding model specific parameters are defined. Please modify configuration name and parameter names according to the naming convection explained in the tutorial.*/ /* @@ -50,176 +50,194 @@ namespace cadet { - namespace model +namespace model +{ + +inline const char* LangmuirParamHandler::identifier() CADET_NOEXCEPT +{ + return "MULTI_COMPONENT_LANGMUIR"; +} + +inline bool LangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) +{ + if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() < nComp)) + throw InvalidParameterException("MCL_KA, MCL_KD, and MCL_QMAX have to have the same size"); + + return true; +} + +inline const char* ExtLangmuirParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_MULTI_COMPONENT_LANGMUIR"; +} + +inline bool ExtLangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) +{ + if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() < nComp)) + throw InvalidParameterException("EXT_MCL_KA, EXT_MCL_KD, and EXT_MCL_QMAX have to have the same size"); + + return true; +} + +/** + * @brief Defines the multi component Langmuir binding model + * @details Implements the Langmuir adsorption model: \f[ \begin{align} + * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} q_{\text{max},i} \left( 1 - \sum_j + * \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} q_i \end{align} \f] Multiple bound states are not supported. + * Components without bound state (i.e., non-binding components) are supported. + * + * See @cite Langmuir1916. + * @tparam ParamHandler_t Type that can add support for external function dependence + */ +template class LangmuirBindingBase : public ParamHandlerBindingModelBase +{ +public: + LangmuirBindingBase() + { + } + virtual ~LangmuirBindingBase() CADET_NOEXCEPT { + } - inline const char* LangmuirParamHandler::identifier() CADET_NOEXCEPT { return "MULTI_COMPONENT_LANGMUIR"; } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - inline bool LangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } + + CADET_BINDINGMODELBASE_BOILERPLATE + +protected: + using ParamHandlerBindingModelBase::_paramHandler; + using ParamHandlerBindingModelBase::_reactionQuasistationarity; + using ParamHandlerBindingModelBase::_nComp; + using ParamHandlerBindingModelBase::_nBoundStates; + + // In the follwing the class method the binding model mass transfer behavior is implemented. + // Apart from the mentioned places in the tutorial, do not modify anything in this function. + template + int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + { + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + + // Protein fluxes: -k_{a,i} * c_{p,i} * q_{max,i} * (1 - \sum_j q_j / q_{max,j}) + k_{d,i} * q_i + ResidualType qSum = 1.0; + unsigned int bndIdx = 0; + for (int i = 0; i < _nComp; ++i) { - if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() < nComp)) - throw InvalidParameterException("MCL_KA, MCL_KD, and MCL_QMAX have to have the same size"); + // Skip components without bound states (bound state index bndIdx is not advanced) + if (_nBoundStates[i] == 0) + continue; - return true; - } + qSum -= y[bndIdx] / static_cast(p->qMax[i]); - inline const char* ExtLangmuirParamHandler::identifier() CADET_NOEXCEPT { return "EXT_MULTI_COMPONENT_LANGMUIR"; } + // Next bound component + ++bndIdx; + } - inline bool ExtLangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) + bndIdx = 0; + for (int i = 0; i < _nComp; ++i) { - if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() < nComp)) - throw InvalidParameterException("EXT_MCL_KA, EXT_MCL_KD, and EXT_MCL_QMAX have to have the same size"); + // Skip components without bound states (bound state index bndIdx is not advanced) + if (_nBoundStates[i] == 0) + continue; - return true; + // Residual + res[bndIdx] = static_cast(p->kD[i]) * y[bndIdx] - + static_cast(p->kA[i]) * yCp[i] * static_cast(p->qMax[i]) * qSum; + + // Next bound component + ++bndIdx; } + return 0; + } + + // In the follwing the class method the anlytical Jacobian of the binding model should be implemented. + // Apart from the mentioned places in the tutorial, do not modify anything in this function. + template + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + { + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); - /** - * @brief Defines the multi component Langmuir binding model - * @details Implements the Langmuir adsorption model: \f[ \begin{align} - * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} q_{\text{max},i} \left( 1 - \sum_j \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} q_i - * \end{align} \f] - * Multiple bound states are not supported. - * Components without bound state (i.e., non-binding components) are supported. - * - * See @cite Langmuir1916. - * @tparam ParamHandler_t Type that can add support for external function dependence - */ - template - class LangmuirBindingBase : public ParamHandlerBindingModelBase + // Protein fluxes: -k_{a,i} * c_{p,i} * q_{max,i} * (1 - \sum_j q_j / q_{max,j}) + k_{d,i} * q_i + double qSum = 1.0; + int bndIdx = 0; + for (int i = 0; i < _nComp; ++i) { - public: + // Skip components without bound states (bound state index bndIdx is not advanced) + if (_nBoundStates[i] == 0) + continue; - LangmuirBindingBase() { } - virtual ~LangmuirBindingBase() CADET_NOEXCEPT { } + qSum -= y[bndIdx] / static_cast(p->qMax[i]); - static const char* identifier() { return ParamHandler_t::identifier(); } + // Next bound component + ++bndIdx; + } - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + bndIdx = 0; + for (int i = 0; i < _nComp; ++i) + { + // Skip components without bound states (bound state index bndIdx is not advanced) + if (_nBoundStates[i] == 0) + continue; - CADET_BINDINGMODELBASE_BOILERPLATE + const double ka = static_cast(p->kA[i]); + const double kd = static_cast(p->kD[i]); - protected: - using ParamHandlerBindingModelBase::_paramHandler; - using ParamHandlerBindingModelBase::_reactionQuasistationarity; - using ParamHandlerBindingModelBase::_nComp; - using ParamHandlerBindingModelBase::_nBoundStates; + // dres_i / dc_{p,i} + jac[i - bndIdx - offsetCp] = -static_cast(p->kA[i]) * static_cast(p->qMax[i]) * qSum; + // Getting to c_{p,i}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0} and a +i to c_{p,i}. + // This means jac[i - bndIdx - offsetCp] corresponds to c_{p,i}. - // In the follwing the class method the binding model mass transfer behavior is implemented. - // Apart from the mentioned places in the tutorial, do not modify anything in this function. - template - int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + // Fill dres_i / dq_j + int bndIdx2 = 0; + for (int j = 0; j < _nComp; ++j) { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); - - // Protein fluxes: -k_{a,i} * c_{p,i} * q_{max,i} * (1 - \sum_j q_j / q_{max,j}) + k_{d,i} * q_i - ResidualType qSum = 1.0; - unsigned int bndIdx = 0; - for (int i = 0; i < _nComp; ++i) - { - // Skip components without bound states (bound state index bndIdx is not advanced) - if (_nBoundStates[i] == 0) - continue; - - qSum -= y[bndIdx] / static_cast(p->qMax[i]); - - // Next bound component - ++bndIdx; - } - - bndIdx = 0; - for (int i = 0; i < _nComp; ++i) - { - // Skip components without bound states (bound state index bndIdx is not advanced) - if (_nBoundStates[i] == 0) - continue; - - // Residual - res[bndIdx] = static_cast(p->kD[i]) * y[bndIdx] - static_cast(p->kA[i]) * yCp[i] * static_cast(p->qMax[i]) * qSum; - - // Next bound component - ++bndIdx; - } - - return 0; - } + // Skip components without bound states (bound state index bndIdx is not advanced) + if (_nBoundStates[j] == 0) + continue; - // In the follwing the class method the anlytical Jacobian of the binding model should be implemented. - // Apart from the mentioned places in the tutorial, do not modify anything in this function. - template - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const - { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); - - // Protein fluxes: -k_{a,i} * c_{p,i} * q_{max,i} * (1 - \sum_j q_j / q_{max,j}) + k_{d,i} * q_i - double qSum = 1.0; - int bndIdx = 0; - for (int i = 0; i < _nComp; ++i) - { - // Skip components without bound states (bound state index bndIdx is not advanced) - if (_nBoundStates[i] == 0) - continue; - - qSum -= y[bndIdx] / static_cast(p->qMax[i]); - - // Next bound component - ++bndIdx; - } - - bndIdx = 0; - for (int i = 0; i < _nComp; ++i) - { - // Skip components without bound states (bound state index bndIdx is not advanced) - if (_nBoundStates[i] == 0) - continue; - - const double ka = static_cast(p->kA[i]); - const double kd = static_cast(p->kD[i]); - - // dres_i / dc_{p,i} - jac[i - bndIdx - offsetCp] = -static_cast(p->kA[i]) * static_cast(p->qMax[i]) * qSum; - // Getting to c_{p,i}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0} and a +i to c_{p,i}. - // This means jac[i - bndIdx - offsetCp] corresponds to c_{p,i}. - - // Fill dres_i / dq_j - int bndIdx2 = 0; - for (int j = 0; j < _nComp; ++j) - { - // Skip components without bound states (bound state index bndIdx is not advanced) - if (_nBoundStates[j] == 0) - continue; - - // dres_i / dq_j - jac[bndIdx2 - bndIdx] = static_cast(p->kA[i]) * yCp[i] * static_cast(p->qMax[i]) / static_cast(p->qMax[j]); - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. - - ++bndIdx2; - } - - // Add to dres_i / dq_i - jac[0] += kd; - - // Advance to next flux and Jacobian row - ++bndIdx; - ++jac; - } + // dres_i / dq_j + jac[bndIdx2 - bndIdx] = static_cast(p->kA[i]) * yCp[i] * static_cast(p->qMax[i]) / + static_cast(p->qMax[j]); + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] + // corresponds to q_j. + + ++bndIdx2; } - }; - // Do not forget to make changes in the following lines of code according to the guidelines given in the tutorial. - typedef LangmuirBindingBase LangmuirBinding; - typedef LangmuirBindingBase ExternalLangmuirBinding; + // Add to dres_i / dq_i + jac[0] += kd; - namespace binding - { - void registerLangmuirModel(std::unordered_map>& bindings) - { - bindings[LangmuirBinding::identifier()] = []() { return new LangmuirBinding(); }; - bindings[ExternalLangmuirBinding::identifier()] = []() { return new ExternalLangmuirBinding(); }; - } - } // namespace binding + // Advance to next flux and Jacobian row + ++bndIdx; + ++jac; + } + } +}; + +// Do not forget to make changes in the following lines of code according to the guidelines given in the tutorial. +typedef LangmuirBindingBase LangmuirBinding; +typedef LangmuirBindingBase ExternalLangmuirBinding; + +namespace binding +{ +void registerLangmuirModel(std::unordered_map>& bindings) +{ + bindings[LangmuirBinding::identifier()] = []() { return new LangmuirBinding(); }; + bindings[ExternalLangmuirBinding::identifier()] = []() { return new ExternalLangmuirBinding(); }; +} +} // namespace binding - } // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/doc/developer_guide/build_options.rst b/doc/developer_guide/build_options.rst index 4706161ac..e650201fe 100644 --- a/doc/developer_guide/build_options.rst +++ b/doc/developer_guide/build_options.rst @@ -50,4 +50,3 @@ The following build arguments can be set in the cmakeSettings.json or from the c The following build arguments are exclusive to builds on MS windows: - ``DVCPKG_TARGET_TRIPLET``: We use ``vcpkg`` to manage our dependencies. This triplet specifies which version of the dependencies should be installed. It takes the form of ``architecture-os-linking``, so ``x64-windows-static`` for our use cases. - diff --git a/doc/developer_guide/cadet_core_architecture.rst b/doc/developer_guide/cadet_core_architecture.rst index 0c0674765..b35511405 100644 --- a/doc/developer_guide/cadet_core_architecture.rst +++ b/doc/developer_guide/cadet_core_architecture.rst @@ -11,13 +11,13 @@ To promote these goals, CADET-Core is implemented in C++, a highly performant, o Features and Capabilities ^^^^^^^^^^^^^^^^^^^^^^^^^ -CADET-Core +CADET-Core - Builds on Windows, Linux and MacOs - Allows to easily add new frontends as the core simulator is implemented in the library ``libcadet``, which can be called from different frontends. Currently, CADET-Core features the command line interface ``cadet-cli`` and CADET-Python, which includes a ``Cadet`` class that serves as a generic HDF5 frontend and calls the ``cadet-cli``. - The CADET-Process frontend is a separate software that wraps CADET-Python and supports additional pre- and post-processing functionality, as described in the `CADET-Process documentation `_. + The CADET-Process frontend is a separate software that wraps CADET-Python and supports additional pre- and post-processing functionality, as described in the `CADET-Process documentation `_. The Matlab frontend was deprecated in `commit 4b34e0d5 `_ due to its maintenance overhead. - Can compute parameter sensitivities via a custom Algorithmic Differentiation (AD) implementation. The AD infrastructure allows for easy extension of new models to support parameter sensitivities, as described in the AD section of the :ref:`model_expansion` chapter. @@ -34,7 +34,7 @@ Implementation Details Classes and realationships -.. _LibcadetClasses: +.. _LibcadetClasses: .. figure:: _images/architecture_libcadet_classes.png @@ -109,5 +109,4 @@ where: - :math:`\mathbf{y}_{in,n} \in \mathbb{R}^{N_{in, n} N_c,n}` is the inlet concentration of all ports and components, - :math:`\mathbf{y}_{n}` contains the state variables of the submodel, and :math:`\frac{\partial \mathbf{y}_{n}}{\partial t}` contains its time derivatives. -- The outlet concentration :math:`\mathbf{c}_{out,n,j} \ \colon (0, T_\text{end}) \mapsto \mathbb{R}^{N_{c,n}}` of port :math:`j\in\{1,\dots , N_{out,n}\}` of submodel :math:`n` is a linear function of its state vector :math:`\mathbf{y}_n`. - +- The outlet concentration :math:`\mathbf{c}_{out,n,j} \ \colon (0, T_\text{end}) \mapsto \mathbb{R}^{N_{c,n}}` of port :math:`j\in\{1,\dots , N_{out,n}\}` of submodel :math:`n` is a linear function of its state vector :math:`\mathbf{y}_n`. diff --git a/doc/developer_guide/cadet_python.rst b/doc/developer_guide/cadet_python.rst index 1da8c269b..ce6035e2b 100644 --- a/doc/developer_guide/cadet_python.rst +++ b/doc/developer_guide/cadet_python.rst @@ -7,10 +7,10 @@ The CADET-Python API can be very useful for developers as it allows to create an This is especially useful when new methods and models are added, which are not (yet) supported by the CADET-Process frontend. To demonstrate the use of CADET-Python, we set up a simulation in the following. -Performing a forward simulation comprises several steps: +Performing a forward simulation comprises several steps: * Setting up the model including all parameters and discretization options - * Defining sections and switches - * Setting up the simulator and actually running the simulation + * Defining sections and switches + * Setting up the simulator and actually running the simulation * Evaluating results (e.g., plotting) In this tutorial, we will build a simple forward simulation with a breakthrough of one component using the following system: @@ -18,7 +18,7 @@ In this tutorial, we will build a simple forward simulation with a breakthrough .. figure:: _images/breakthrough_system.png For this purpose, we use :ref:`CADET-Python `, a file based frontend for CADET. -CADET still must be downloaded (or built from source) as explained in the :ref:`installation guide `. +CADET still must be downloaded (or built from source) as explained in the :ref:`installation guide `. The Python frontend almost exactly maps to the documented :ref:`CADET file format ` except that all dataset names are lowercase. This simplifies using the frontend. The package includes a ``Cadet`` class which serves as a generic HDF5 frontend. @@ -64,7 +64,7 @@ This allows for creating arbitrary nested dictionaries using dot-notation. Although the order of the parameter specification does not matter, it is reasonable to first specify the number of unit operations before we select the models and define the parameters. .. literalinclude:: examples/breakthrough.py - :lines: 12 + :lines: 12 The available models are listed in the :ref:`unit operation chapter `. The units of the different parameters and quantities are given in the corresponding :ref:`file format ` of the respective unit operation. @@ -130,10 +130,10 @@ We choose ``20`` axial cells in the column ``ncol`` and ``5`` radial cells in th Note that the WENO scheme, which handles the advection, drastically reduces the required amount of cells compared to an upwind scheme. Moreover, we have to specify the number of bound states for each component. -Finally, we set some other options for the discretization, which usually do not need to be changed. +Finally, we set some other options for the discretization, which usually do not need to be changed. .. literalinclude:: examples/breakthrough.py - :lines: 48-66 + :lines: 48-66 Outlet Model ^^^^^^^^^^^^ @@ -143,7 +143,7 @@ The ``OUTLET`` is another pseudo unit operation that serves as sink for the syst .. note:: In this case, the outlet unit is actually not required. We could use the outlet concentration signal of the column model instead. .. literalinclude:: examples/breakthrough.py - :lines: 69-70 + :lines: 69-70 2. Setting up Time Sections and Connections @@ -166,9 +166,9 @@ If the transition is continuous, the time integrator can try to step over the tr If you are unsure about the continuity, just leave it at ``0``. .. literalinclude:: examples/breakthrough.py - :lines: 73-75 + :lines: 73-75 -As mentioned earlier, we now define the ``INLET`` profile using a piecewise cubic polynomial. +As mentioned earlier, we now define the ``INLET`` profile using a piecewise cubic polynomial. On each section :math:`[ t_i, t_{i+1} ]` a cubic polynomial :math:`p_i` is defined: .. math:: @@ -184,7 +184,7 @@ Since the column should be constantly fed with :math:`1.0 \cdot 10^{-3} mol / m^ Note that for more components, a vector of coefficients needs to be specified. .. literalinclude:: examples/breakthrough.py - :lines: 78-81 + :lines: 78-81 System Connectivity ^^^^^^^^^^^^^^^^^^^ @@ -203,7 +203,7 @@ Usually, ``Component from`` and ``Component to`` can be set to ``-1``, which wil Also, for the 2D General rate model inlet ports need to be speciefied. For more information on the parameters, see the :ref:`file format specification `. -In this case, we connect all components of ``unit_000`` to ``unit_001``, and from ``unit_001`` to ``unit_002``. +In this case, we connect all components of ``unit_000`` to ``unit_001``, and from ``unit_001`` to ``unit_002``. .. literalinclude:: examples/breakthrough.py :lines: 84-88 @@ -234,21 +234,21 @@ In this example, we want to write the concentration profile of the inlet and out In addition, we are interested in the concentration in the interstitial volume (bulk volume) of the column. .. literalinclude:: examples/breakthrough.py - :lines: 106-115 + :lines: 106-115 Finally, we have to set the time points at which we want to evaluate the solution. Note that the end time must not exceed the last section time specified in the model. If the time points are not set explicitly, the time integrator outputs the solution at arbitrary time points between ``0`` and ``section_times[-1]``. .. literalinclude:: examples/breakthrough.py - :lines: 117-118 + :lines: 117-118 The last remaining step is to actually run the simulation. For this, we have to specify a filename, save the configuration to ``H5``-format and call call the ``model``\ ’s ``run()`` function. We check if the simulation has completed successfully and load the results. .. literalinclude:: examples/breakthrough.py - :lines: 121-131 + :lines: 121-131 4. Plotting the Results ----------------------- diff --git a/doc/developer_guide/examples/breakthrough.py b/doc/developer_guide/examples/breakthrough.py index bf82951ed..8060490ee 100644 --- a/doc/developer_guide/examples/breakthrough.py +++ b/doc/developer_guide/examples/breakthrough.py @@ -26,11 +26,11 @@ model.root.input.model.unit_001.col_porosity = 0.37 # - model.root.input.model.unit_001.par_porosity = 0.33 # - model.root.input.model.unit_001.par_radius = 1e-6 # m - + ## Transport model.root.input.model.unit_001.col_dispersion = 1e-8 # m^2 / s (interstitial volume) model.root.input.model.unit_001.film_diffusion = [1e-5] # m / s -model.root.input.model.unit_001.par_diffusion = [1e-10,] # m^2 / s (mobile phase) +model.root.input.model.unit_001.par_diffusion = [1e-10,] # m^2 / s (mobile phase) model.root.input.model.unit_001.par_surfdiffusion = [0.0,] # m^2 / s (solid phase) ## Adsorption @@ -53,7 +53,7 @@ model.root.input.model.unit_001.discretization.nbound = [1] ### Other options -model.root.input.model.unit_001.discretization.par_disc_type = 'EQUIDISTANT_PAR' +model.root.input.model.unit_001.discretization.par_disc_type = 'EQUIDISTANT_PAR' model.root.input.model.unit_001.discretization.use_analytic_jacobian = 1 model.root.input.model.unit_001.discretization.reconstruction = 'WENO' model.root.input.model.unit_001.discretization.gs_type = 1 @@ -69,7 +69,7 @@ model.root.input.model.unit_002.unit_type = 'OUTLET' model.root.input.model.unit_002.ncomp = 1 -# Sections +# Sections model.root.input.solver.sections.nsec = 1 model.root.input.solver.sections.section_times = [0.0, 1200,] # s model.root.input.solver.sections.section_continuity = [] @@ -84,8 +84,8 @@ model.root.input.model.connections.nswitches = 1 model.root.input.model.connections.switch_000.section = 0 model.root.input.model.connections.switch_000.connections = [ - 0, 1, -1, -1, 60/1e6, # [unit_000, unit_001, all components, all components, Q/ m^3*s^-1 - 1, 2, -1, -1, 60/1e6] # [unit_001, unit_002, all components, all components, Q/ m^3*s^-1 + 0, 1, -1, -1, 60/1e6, # [unit_000, unit_001, all components, all components, Q/ m^3*s^-1 + 1, 2, -1, -1, 60/1e6] # [unit_001, unit_002, all components, all components, Q/ m^3*s^-1 # Solver settings model.root.input.model.solver.gs_type = 1 @@ -125,7 +125,7 @@ if data.returncode == 0: print("Simulation completed successfully") - model.load() + model.load() else: print(data) raise Exception("Simulation failed") diff --git a/doc/developer_guide/launch.vs.json b/doc/developer_guide/launch.vs.json index daad41d9c..885aaf808 100644 --- a/doc/developer_guide/launch.vs.json +++ b/doc/developer_guide/launch.vs.json @@ -12,4 +12,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/doc/developer_guide/model_expansion.rst b/doc/developer_guide/model_expansion.rst index 481c602a5..72cc65039 100644 --- a/doc/developer_guide/model_expansion.rst +++ b/doc/developer_guide/model_expansion.rst @@ -92,8 +92,8 @@ Most important functionality to be implemented: 3. residualImpl(): Implements the residual formulation (i.e. function :math:`F = 0`) of the equations. Triggers updates of the (possibly state dependent) system Jacobian. 4. System Jacobian: Owned by the unit operation. Defined given by :math:`J := \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}}`, i.e. both the state and state derivative Jacobian need to be implemented. 5. Linear solve: Solves the system :math:`J x = b` with given :math:`b`. -6. Algorithmic differentiation (AD): - a. Parameter sensitivities: Use ``ParamType`` for all parameters and ``ResidualType`` for the residual. +6. Algorithmic differentiation (AD): + a. Parameter sensitivities: Use ``ParamType`` for all parameters and ``ResidualType`` for the residual. b. Jacobian calculation via AD (can be used to verify the analytical implementation): Use ``StateType`` for the state and ``ResidualType`` for the residual. Additionally, you need to implement the following functions to enable the AD Jacobian: ``requiredADdirs()``, ``prepareADvectors``, ``extractJacobianFromAD()``, ``useAnalyticJacobian()``. For details please refer to `Pttmann et al. `_. Testing and Publication @@ -118,6 +118,6 @@ Directions can either be the parameter(s) whose sensitivity we want to calculate To use AD for a new binding model, you only need to use the template types properly: Use ``ParamType`` and ``ResidualType`` for parameters and residual ``res`` to enable parameter sensitivities; that is, all parameters must be defined as actives in the binding model and used as ParamType in the residual function. -Use ``StateType`` and ``ResidualType`` for the state ``y`` and residual ``res`` to enable the AD Jacobian. +Use ``StateType`` and ``ResidualType`` for the state ``y`` and residual ``res`` to enable the AD Jacobian. -To use AD for a new unit operation, you can either apply dense AD or, in case of a model with many states or spatial resolution, you need to think of the shape of the Jacobian and apply sparse AD. +To use AD for a new unit operation, you can either apply dense AD or, in case of a model with many states or spatial resolution, you need to think of the shape of the Jacobian and apply sparse AD. diff --git a/doc/examples/batch_chromatography.rst b/doc/examples/batch_chromatography.rst index 6f408bb3b..e04c50a8d 100644 --- a/doc/examples/batch_chromatography.rst +++ b/doc/examples/batch_chromatography.rst @@ -1,3 +1,2 @@ Batch chromatography ==================== - diff --git a/doc/examples/load_wash_elute.rst b/doc/examples/load_wash_elute.rst index d811f9944..31d967554 100644 --- a/doc/examples/load_wash_elute.rst +++ b/doc/examples/load_wash_elute.rst @@ -1,3 +1,2 @@ Load Wash Elute =============== - diff --git a/doc/examples/rtd.rst b/doc/examples/rtd.rst index 591d27ffe..f567c88ca 100644 --- a/doc/examples/rtd.rst +++ b/doc/examples/rtd.rst @@ -1,3 +1,2 @@ Residence time distributions ============================ - diff --git a/doc/getting_started/index.rst b/doc/getting_started/index.rst index d9a39fcaf..5ad8cae13 100644 --- a/doc/getting_started/index.rst +++ b/doc/getting_started/index.rst @@ -3,7 +3,7 @@ Getting started =============== -This section details the steps to install CADET, as pre-built binaries or building from scratch, and also provides the series of tutorials that will help in building complete model from scratch in CADET. +This section details the steps to install CADET, as pre-built binaries or building from scratch, and also provides the series of tutorials that will help in building complete model from scratch in CADET. .. toctree:: :maxdepth: 1 diff --git a/doc/getting_started/installation.rst b/doc/getting_started/installation.rst index 3c05ffbb5..6b331e278 100644 --- a/doc/getting_started/installation.rst +++ b/doc/getting_started/installation.rst @@ -53,4 +53,4 @@ If you want to use ``CADET-Python``, open an `anaconda shell` or `mamba shell` a .. code-block:: bash - pip install CADET-Python \ No newline at end of file + pip install CADET-Python diff --git a/doc/getting_started/tutorials/_images/breakthrough_system.svg b/doc/getting_started/tutorials/_images/breakthrough_system.svg index 6dc4691d8..93d43fee2 100644 --- a/doc/getting_started/tutorials/_images/breakthrough_system.svg +++ b/doc/getting_started/tutorials/_images/breakthrough_system.svg @@ -1,3 +1,3 @@ -
Column
Column
Inlet
Inlet
Outlet
Outlet
Text is not SVG - cannot display
\ No newline at end of file +
Column
Column
Inlet
Inlet
Outlet
Outlet
Text is not SVG - cannot display
diff --git a/doc/getting_started/tutorials/breakthrough.rst b/doc/getting_started/tutorials/breakthrough.rst index f0cabaf76..41010933f 100644 --- a/doc/getting_started/tutorials/breakthrough.rst +++ b/doc/getting_started/tutorials/breakthrough.rst @@ -1,10 +1,10 @@ CADET Introduction ================== -Performing a forward simulation comprises several steps: +Performing a forward simulation comprises several steps: * Setting up the model including all parameters * Defining connectivity and dynamic events - * Setting up the simulator and actually running the simulation + * Setting up the simulator and actually running the simulation * Evaluating results (e.g., plotting) In this tutorial, we will build a simple forward simulation with a breakthrough of one component using the following system: @@ -12,7 +12,7 @@ In this tutorial, we will build a simple forward simulation with a breakthrough .. figure:: _images/breakthrough_system.svg For this purpose, we use `CADET-Process `_, an object oriented Python frontend for CADET. -CADET still must be downloaded (or built from source) as explained in the :ref:`installation guide `. +CADET still must be downloaded (or built from source) as explained in the :ref:`installation guide `. .. figure:: _images/cadet_architecture_overview.png diff --git a/doc/index.rst b/doc/index.rst index fb1b7907d..56afe7861 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -68,7 +68,7 @@ For non-developers, it is recommended to upgrade from release to release instead Bugs ---- -Please report any bugs that you find `here `_. Or, even better, fork the repository on `GitHub `_ and create a pull request (PR) with the fix. +Please report any bugs that you find `here `_. Or, even better, fork the repository on `GitHub `_ and create a pull request (PR) with the fix. Donations --------- @@ -106,5 +106,3 @@ Please refer to the `list of contributors - - diff --git a/doc/interface/binding/bi_steric_mass_action.rst b/doc/interface/binding/bi_steric_mass_action.rst index 4ece990b1..fcfdf0867 100644 --- a/doc/interface/binding/bi_steric_mass_action.rst +++ b/doc/interface/binding/bi_steric_mass_action.rst @@ -65,7 +65,7 @@ For information on model equations, refer to :ref:`bi_steric_mass_action_model`. **Unit:** :math:`mol~m_{SP}^{-3}` =================== ========================= =============================== -**Type:** double **Range:** :math:`\ge 0` **Length:** NSTATES +**Type:** double **Range:** :math:`\ge 0` **Length:** NSTATES =================== ========================= =============================== ``BISMA_REFC0`` @@ -75,7 +75,7 @@ For information on model equations, refer to :ref:`bi_steric_mass_action_model`. **Unit:** :math:`mol~m_{MP}^{-3}` =================== ========================= =============================== -**Type:** double **Range:** :math:`\gt 0` **Length:** {1,NSTATES} +**Type:** double **Range:** :math:`\gt 0` **Length:** {1,NSTATES} =================== ========================= =============================== ``BISMA_REFQ`` @@ -85,5 +85,5 @@ For information on model equations, refer to :ref:`bi_steric_mass_action_model`. **Unit:** :math:`mol~m_{SP}^{-3}` =================== ========================= =============================== -**Type:** double **Range:** :math:`\gt 0` **Length:** {1,NSTATES} +**Type:** double **Range:** :math:`\gt 0` **Length:** {1,NSTATES} =================== ========================= =============================== diff --git a/doc/interface/binding/freundlich_ldf.rst b/doc/interface/binding/freundlich_ldf.rst index f145624ca..ebb0ff561 100644 --- a/doc/interface/binding/freundlich_ldf.rst +++ b/doc/interface/binding/freundlich_ldf.rst @@ -16,7 +16,7 @@ For information on model equations, refer to :ref:`freundlich_ldf_model`. =================== ========================= ================================== **Type:** int **Range:** {0,1} **Length:** 1/NTOTALBND -=================== ========================= ================================== +=================== ========================= ================================== ``FLDF_KKIN`` Driving force coefficient for each component @@ -26,7 +26,7 @@ For information on model equations, refer to :ref:`freundlich_ldf_model`. =================== ========================= ================================== **Type:** double **Range:** :math:`\ge 0` **Length:** 1/NTOTALBND -=================== ========================= ================================== +=================== ========================= ================================== ``FLDF_KF`` @@ -36,7 +36,7 @@ For information on model equations, refer to :ref:`freundlich_ldf_model`. =================== ========================= ================================== **Type:** double **Range:** :math:`\ge 0` **Length:** 1/NTOTALBND -=================== ========================= ================================== +=================== ========================= ================================== ``FLDF_N`` Freundlich exponent for each component @@ -45,10 +45,4 @@ For information on model equations, refer to :ref:`freundlich_ldf_model`. =================== ========================= ================================== **Type:** double **Range:** :math:`> 0` **Length:** 1/NTOTALBND -=================== ========================= ================================== - - - - - - +=================== ========================= ================================== diff --git a/doc/interface/binding/generalized_ion_exchange.rst b/doc/interface/binding/generalized_ion_exchange.rst index 9c6688562..eb2dff7ed 100644 --- a/doc/interface/binding/generalized_ion_exchange.rst +++ b/doc/interface/binding/generalized_ion_exchange.rst @@ -33,9 +33,9 @@ For information on model equations, refer to :ref:`generalized_ion_exchange_mode **Unit:** :math:`\text{[Mod]}^{-1}` -=================== ========================= +=================== ========================= **Type:** double **Length:** NCOMP -=================== ========================= +=================== ========================= ``GIEX_KA_QUAD`` Coefficient of quadratic dependence of adsorption rate constant on @@ -43,17 +43,17 @@ For information on model equations, refer to :ref:`generalized_ion_exchange_mode **Unit:** :math:`\text{[Mod]}^{-2}` -=================== ========================= +=================== ========================= **Type:** double **Length:** NCOMP -=================== ========================= +=================== ========================= ``GIEX_KA_SALT`` Salt coefficient of adsorption rate constants; difference of water-protein and salt-protein interactions -=================== ========================= +=================== ========================= **Type:** double **Length:** NCOMP -=================== ========================= +=================== ========================= ``GIEX_KA_PROT`` Protein coefficient of adsorption rate constants; difference of @@ -61,9 +61,9 @@ For information on model equations, refer to :ref:`generalized_ion_exchange_mode **Unit:** :math:`m_{MP}^{3} mol^{-1}` -=================== ========================= +=================== ========================= **Type:** double **Length:** NCOMP -=================== ========================= +=================== ========================= ``GIEX_KD`` Base value of desorption rate constant @@ -80,9 +80,9 @@ For information on model equations, refer to :ref:`generalized_ion_exchange_mode **Unit:** :math:`\text{[Mod]}^{-1}` -=================== ========================= +=================== ========================= **Type:** double **Length:** NCOMP -=================== ========================= +=================== ========================= ``GIEX_KD_QUAD`` Coefficient of quadratic dependence of desorption rate constant on @@ -90,17 +90,17 @@ For information on model equations, refer to :ref:`generalized_ion_exchange_mode **Unit:** :math:`\text{[Mod]}^{-2}` -=================== ========================= +=================== ========================= **Type:** double **Length:** NCOMP -=================== ========================= +=================== ========================= ``GIEX_KD_SALT`` Salt coefficient of desorption rate constants; difference of water-protein and salt-protein interactions -=================== ========================= +=================== ========================= **Type:** double **Length:** NCOMP -=================== ========================= +=================== ========================= ``GIEX_KD_PROT`` Protein coefficient of desorption rate constants; difference of @@ -108,18 +108,18 @@ For information on model equations, refer to :ref:`generalized_ion_exchange_mode **Unit:** :math:`m_{MP}^{3} mol^{-1}` -=================== ========================= +=================== ========================= **Type:** double **Length:** NCOMP -=================== ========================= +=================== ========================= ``GIEX_NU`` Base value for characteristic charges of the protein; The number of sites :math:`\nu` that the protein interacts with on the resin surface -=================== ========================= +=================== ========================= **Type:** double **Length:** NCOMP -=================== ========================= +=================== ========================= ``GIEX_NU_LIN`` Coefficient of linear dependence of characteristic charge on modifier @@ -127,9 +127,9 @@ For information on model equations, refer to :ref:`generalized_ion_exchange_mode **Unit:** :math:`\text{[Mod]}^{-1}` -=================== ========================= +=================== ========================= **Type:** double **Length:** NCOMP -=================== ========================= +=================== ========================= ``GIEX_NU_QUAD`` Coefficient of quadratic dependence of characteristic charge on @@ -137,9 +137,9 @@ For information on model equations, refer to :ref:`generalized_ion_exchange_mode **Unit:** :math:`\text{[Mod]}^{-2}` -=================== ========================= +=================== ========================= **Type:** double **Length:** NCOMP -=================== ========================= +=================== ========================= ``GIEX_SIGMA`` Steric factors of the protein; The number of sites :math:`\sigma` on diff --git a/doc/interface/binding/hic_constant_water_activity.rst b/doc/interface/binding/hic_constant_water_activity.rst index 753f4ff87..5cb99dbcc 100644 --- a/doc/interface/binding/hic_constant_water_activity.rst +++ b/doc/interface/binding/hic_constant_water_activity.rst @@ -57,8 +57,8 @@ For information on model equations, refer to :ref:`hic_constant_water_activity_m ``HICCWA_BETA0`` - Parameters describing the number of highly ordered water molecules - that stabilize the hydrophobic surfaces at infinitely diluted + Parameters describing the number of highly ordered water molecules + that stabilize the hydrophobic surfaces at infinitely diluted salt concentration **Unit: [-]** @@ -68,7 +68,7 @@ For information on model equations, refer to :ref:`hic_constant_water_activity_m =================== ========================= ========================================= ``HICCWA_BETA1`` - Parameters describing the change in the number of highly ordered + Parameters describing the change in the number of highly ordered water molecules that stabilize the hydrophobic surfaces with respect to changes in the salt concentration @@ -77,4 +77,3 @@ For information on model equations, refer to :ref:`hic_constant_water_activity_m =================== ========================= ========================================= **Type:** double **Range:** :math:`\ge 0` **Length:** 1 =================== ========================= ========================================= - diff --git a/doc/interface/binding/hic_water_on_hydrophobic_surfaces.rst b/doc/interface/binding/hic_water_on_hydrophobic_surfaces.rst index 31ff02484..8ebb826a7 100644 --- a/doc/interface/binding/hic_water_on_hydrophobic_surfaces.rst +++ b/doc/interface/binding/hic_water_on_hydrophobic_surfaces.rst @@ -57,8 +57,8 @@ For information on model equations, refer to :ref:`hic_water_on_hydrophobic_surf ``HICWHS_BETA0`` - Parameters describing the number of highly ordered water molecules - that stabilize the hydrophobic surfaces at infinitely diluted + Parameters describing the number of highly ordered water molecules + that stabilize the hydrophobic surfaces at infinitely diluted salt concentration **Unit: [-]** @@ -68,7 +68,7 @@ For information on model equations, refer to :ref:`hic_water_on_hydrophobic_surf =================== ========================= ========================================= ``HICWHS_BETA1`` - Parameters describing the change in the number of highly ordered + Parameters describing the change in the number of highly ordered water molecules that stabilize the hydrophobic surfaces with respect to changes in the salt concentration @@ -77,4 +77,3 @@ For information on model equations, refer to :ref:`hic_water_on_hydrophobic_surf =================== ========================= ========================================= **Type:** double **Range:** :math:`\ge 0` **Length:** 1 =================== ========================= ========================================= - diff --git a/doc/interface/binding/index.rst b/doc/interface/binding/index.rst index a4e6e2c74..5c451d546 100644 --- a/doc/interface/binding/index.rst +++ b/doc/interface/binding/index.rst @@ -9,7 +9,7 @@ Externally dependent binding models Some binding models have a variant that can use external sources as specified in :ref:`/input/model/external/` (also see Section :ref:`dependence-on-external-function_bind` for more information, and Section :ref:`binding_model_feature` on which binding models support this feature). For the sake of brevity, only the standard variant of those binding models is specified below. In order to obtain the format for the externally dependent variant, first replace the binding model name ``XXX`` by ``EXT_XXX``. -Each parameter :math:`p` (except for reference concentrations ``XXX_REFC0`` and ``XXX_REFQ``) depends on a (possibly distinct) external source in a polynomial way: +Each parameter :math:`p` (except for reference concentrations ``XXX_REFC0`` and ``XXX_REFQ``) depends on a (possibly distinct) external source in a polynomial way: .. math:: @@ -71,4 +71,3 @@ This group also takes precedence over a possibly existing ``/input/model/unit_XX hic_water_on_hydrophobic_surfaces hic_constant_water_activity multi_component_colloidal - diff --git a/doc/interface/binding/linear.rst b/doc/interface/binding/linear.rst index 30d80cc86..b7fc1f776 100644 --- a/doc/interface/binding/linear.rst +++ b/doc/interface/binding/linear.rst @@ -26,7 +26,7 @@ For information on model equations, refer to :ref:`linear_model`. =================== ========================= ================================== **Type:** double **Range:** :math:`\ge 0` **Length:** 1/NTOTALBND -=================== ========================= ================================== +=================== ========================= ================================== ``LIN_KD`` @@ -36,9 +36,4 @@ For information on model equations, refer to :ref:`linear_model`. =================== ========================= ================================== **Type:** double **Range:** :math:`\ge 0` **Length:** 1/NTOTALBND -=================== ========================= ================================== - - - - - +=================== ========================= ================================== diff --git a/doc/interface/binding/multi_component_anti_langmuir.rst b/doc/interface/binding/multi_component_anti_langmuir.rst index 1263b71da..52825ae33 100644 --- a/doc/interface/binding/multi_component_anti_langmuir.rst +++ b/doc/interface/binding/multi_component_anti_langmuir.rst @@ -34,7 +34,7 @@ For information on model equations, refer to :ref:`multi_component_anti_langmuir =================== ========================= ================================== **Type:** double **Range:** :math:`\ge 0` **Length:** NCOMP -=================== ========================= ================================== +=================== ========================= ================================== ``MCAL_QMAX`` Maximum adsorption capacities @@ -43,7 +43,7 @@ For information on model equations, refer to :ref:`multi_component_anti_langmuir =================== ========================= ================================== **Type:** double **Range:** :math:`\gt 0` **Length:** NCOMP -=================== ========================= ================================== +=================== ========================= ================================== ``MCAL_ANTILANGMUIR`` Anti-Langmuir coefficients (optional) @@ -52,4 +52,4 @@ For information on model equations, refer to :ref:`multi_component_anti_langmuir =================== ========================= ================================== **Type:** double **Range:** {-1,1} **Length:** NCOMP -=================== ========================= ================================== +=================== ========================= ================================== diff --git a/doc/interface/binding/multi_component_bi_langmuir.rst b/doc/interface/binding/multi_component_bi_langmuir.rst index 5a6a1bcbb..1bb752833 100644 --- a/doc/interface/binding/multi_component_bi_langmuir.rst +++ b/doc/interface/binding/multi_component_bi_langmuir.rst @@ -44,4 +44,3 @@ For information on model equations, refer to :ref:`multi_component_bi_langmuir_m =================== ========================= ========================================= **Type:** double **Range:** :math:`\ge 0` **Length:** NSTATES :math:`\cdot` NCOMP =================== ========================= ========================================= - diff --git a/doc/interface/binding/multi_component_colloidal.rst b/doc/interface/binding/multi_component_colloidal.rst index 9f1c353af..0bfbdf4a2 100644 --- a/doc/interface/binding/multi_component_colloidal.rst +++ b/doc/interface/binding/multi_component_colloidal.rst @@ -123,7 +123,7 @@ Multi Component Colloidal =================== ========================= ========================================= ``COL_BPP_SALT_EXPARGMULT`` - Protein-protein interaction :math:`b_{pp,i}`: Constant power factor :math:`b_{d,i}` for salt in e-function + Protein-protein interaction :math:`b_{pp,i}`: Constant power factor :math:`b_{d,i}` for salt in e-function =================== ========================= ========================================= **Type:** double **Range:** :math:`\ge 0` **Length:** NTOTALBND @@ -160,4 +160,3 @@ Multi Component Colloidal =================== ========================= ========================================= **Type:** int **Range:** {0,1} **Length:** 1 =================== ========================= ========================================= - diff --git a/doc/interface/binding/multi_component_langmuir.rst b/doc/interface/binding/multi_component_langmuir.rst index 174846c21..5fd18e3ab 100644 --- a/doc/interface/binding/multi_component_langmuir.rst +++ b/doc/interface/binding/multi_component_langmuir.rst @@ -34,7 +34,7 @@ For information on model equations, refer to :ref:`multi_component_langmuir_mode =================== ========================= ================================== **Type:** double **Range:** :math:`\ge 0` **Length:** NCOMP -=================== ========================= ================================== +=================== ========================= ================================== ``MCL_QMAX`` Maximum adsorption capacities @@ -43,4 +43,4 @@ For information on model equations, refer to :ref:`multi_component_langmuir_mode =================== ========================= ================================== **Type:** double **Range:** :math:`\gt 0` **Length:** NCOMP -=================== ========================= ================================== +=================== ========================= ================================== diff --git a/doc/interface/binding/multi_component_langmuir_ldf.rst b/doc/interface/binding/multi_component_langmuir_ldf.rst index f703731be..7aa48a9bc 100644 --- a/doc/interface/binding/multi_component_langmuir_ldf.rst +++ b/doc/interface/binding/multi_component_langmuir_ldf.rst @@ -34,7 +34,7 @@ For information on model equations, refer to :ref:`multi_component_langmuir_ldf_ =================== ========================= ================================== **Type:** double **Range:** :math:`\ge 0` **Length:** NCOMP -=================== ========================= ================================== +=================== ========================= ================================== ``MCLLDF_QMAX`` Maximum adsorption capacities @@ -43,4 +43,4 @@ For information on model equations, refer to :ref:`multi_component_langmuir_ldf_ =================== ========================= ================================== **Type:** double **Range:** :math:`\gt 0` **Length:** NCOMP -=================== ========================= ================================== +=================== ========================= ================================== diff --git a/doc/interface/binding/multi_component_langmuir_ldf_liquid_phase.rst b/doc/interface/binding/multi_component_langmuir_ldf_liquid_phase.rst index 1bf25bbb8..4d2d888a0 100644 --- a/doc/interface/binding/multi_component_langmuir_ldf_liquid_phase.rst +++ b/doc/interface/binding/multi_component_langmuir_ldf_liquid_phase.rst @@ -34,7 +34,7 @@ For information on model equations, refer to :ref:`multi_component_langmuir_ldf_ =================== ========================= ================================== **Type:** double **Range:** :math:`\ge 0` **Length:** NCOMP -=================== ========================= ================================== +=================== ========================= ================================== ``MCLLDFC_QMAX`` Maximum adsorption capacities @@ -43,4 +43,4 @@ For information on model equations, refer to :ref:`multi_component_langmuir_ldf_ =================== ========================= ================================== **Type:** double **Range:** :math:`\gt 0` **Length:** NCOMP -=================== ========================= ================================== +=================== ========================= ================================== diff --git a/doc/interface/binding/saska.rst b/doc/interface/binding/saska.rst index cd4da6899..676b5623c 100644 --- a/doc/interface/binding/saska.rst +++ b/doc/interface/binding/saska.rst @@ -37,6 +37,3 @@ For information on model equations, refer to :ref:`saska_model`. =================== ================================ ========================================= **Type:** double **Range:** :math:`\mathbb {R}` **Length:** :math:`\text{NCOMP}^2` =================== ================================ ========================================= - - - diff --git a/doc/interface/binding/self_association.rst b/doc/interface/binding/self_association.rst index febe7773b..cccfe38be 100644 --- a/doc/interface/binding/self_association.rst +++ b/doc/interface/binding/self_association.rst @@ -90,4 +90,3 @@ For information on model equations, refer to :ref:`self_association_model`. =================== ========================= ========================================= **Type:** double **Range:** :math:`\gt 0` **Length:** 1 =================== ========================= ========================================= - diff --git a/doc/interface/consistent_initialization.rst b/doc/interface/consistent_initialization.rst index 6635338f4..e03c51bf5 100644 --- a/doc/interface/consistent_initialization.rst +++ b/doc/interface/consistent_initialization.rst @@ -11,7 +11,7 @@ Group /input/model/unit_XXX/discretization/consistency_solver - Nonlinear consis Name of the solver. Available solvers are ``LEVMAR``, ``ATRN_RES``, ``ATRN_ERR``, and ``COMPOSITE``. ================== ======================= - **Type:** string **Length:** :math:`1` + **Type:** string **Length:** :math:`1` ================== ======================= ``INIT_DAMPING`` @@ -35,5 +35,5 @@ Minimal damping factor (default is :math:`0.0001`; ignored by ``LEVMAR``) Vector with names of solvers for the composite solver (only required for composite solver). See ``SOLVER_NAME`` for available solvers. ================== ========================== - **Type:** string **Length:** :math:`\gt 1` + **Type:** string **Length:** :math:`\gt 1` ================== ========================== diff --git a/doc/interface/flux_reconstruction.rst b/doc/interface/flux_reconstruction.rst index 5e56477eb..6fb4d11cc 100644 --- a/doc/interface/flux_reconstruction.rst +++ b/doc/interface/flux_reconstruction.rst @@ -12,7 +12,7 @@ Group /input/model/unit_XXX/discretization/weno - WENO Parameters 0. Lower WENO order (stable) 1. Zero weights (unstable for small :math:`D_{\mathrm{ax}}`) 2. Zero weights for :math:`p \neq 0` (less stable) - + ============= ============================== ============= **Type:** int **Range:** :math:`\{0, 1, 2\}` **Length:** 1 ============= ============================== ============= @@ -20,7 +20,7 @@ Group /input/model/unit_XXX/discretization/weno - WENO Parameters ``WENO_EPS`` WENO :math:`\varepsilon` - + ================ ================================== ============= **Type:** double **Range:** :math:`\mathbb{R}^{>0}` **Length:** 1 ================ ================================== ============= @@ -32,8 +32,7 @@ Group /input/model/unit_XXX/discretization/weno - WENO Parameters 1. Standard upwind scheme (order 1) 2. WENO 2 (order 3) 3. WENO 3 (order 5) - + ============= ============================== ============= **Type:** int **Range:** :math:`\{1, 2, 3\}` **Length:** 1 ============= ============================== ============= - diff --git a/doc/interface/index.rst b/doc/interface/index.rst index 5ef57e6fb..adbf98d6e 100644 --- a/doc/interface/index.rst +++ b/doc/interface/index.rst @@ -23,6 +23,3 @@ In this section the general layout and structure of the file format is described input_group output_group meta_group - - - diff --git a/doc/interface/input_group.rst b/doc/interface/input_group.rst index 9859c0a0d..07eb9172b 100644 --- a/doc/interface/input_group.rst +++ b/doc/interface/input_group.rst @@ -17,5 +17,3 @@ Input Group sensitivities solver parameter_dependencies - - diff --git a/doc/interface/introduction.rst b/doc/interface/introduction.rst index 48de6a113..704bf58ce 100644 --- a/doc/interface/introduction.rst +++ b/doc/interface/introduction.rst @@ -138,6 +138,5 @@ Note that all components of component dependent datasets have to be section depe - ✓ - ✓ * - VELOCITY - - + - - ✓ - diff --git a/doc/interface/meta_group.rst b/doc/interface/meta_group.rst index 0aba45435..586c71213 100644 --- a/doc/interface/meta_group.rst +++ b/doc/interface/meta_group.rst @@ -6,41 +6,41 @@ Meta Group ``FILE_FORMAT`` Version of the file format (defaults to 040000 = 4.0.0 if omitted) with two digits per part (Major.Minor.Patch) - + ================ ========================= **In/out:** In **Type:** int ================ ========================= - + ``CADET_VERSION`` Version of the executed :math:`\texttt{CADET}` simulator - + ================ ========================= **In/out:** Out **Type:** string ================ ========================= - + ``CADET_COMMIT`` Git commit SHA1 from which the :math:`\texttt{CADET}` simulator was built - + ================ ========================= **In/out:** Out **Type:** string ================ ========================= - + ``CADET_BRANCH`` Git branch from which the :math:`\texttt{CADET}` simulator was built - + ================ ========================= **In/out:** Out **Type:** string ================ ========================= - + ``TIME_SIM`` Time that the time integration took (excluding any preparations and postprocessing) **Unit:** :math:`\mathrm{s}` - + ================ ========================= **In/out:** Out **Type:** double ================ ========================= diff --git a/doc/interface/output_group.rst b/doc/interface/output_group.rst index 9ba4ca6eb..42c0e9cf2 100644 --- a/doc/interface/output_group.rst +++ b/doc/interface/output_group.rst @@ -9,27 +9,27 @@ Group /output ``LAST_STATE_Y`` Full state vector at the last time point of the time integrator if :math:`\texttt{WRITE_SOLUTION_LAST}` in :math:`\texttt{/input/return}` is enabled - + **Type:** double - + ``LAST_STATE_YDOT`` Full time derivative state vector at the last time point of the time integrator if :math:`\texttt{WRITE_SOLUTION_LAST}` in :math:`\texttt{/input/return}` is enabled - + **Type:** double - + ``LAST_STATE_SENSY_XXX`` Full state vector of the ``XXX`` th sensitivity system at the last time point of the time integrator if :math:`\texttt{WRITE_SENS_LAST}` in :math:`\texttt{/input/return}` is enabled - + **Type:** double - + ``LAST_STATE_SENSYDOT_XXX`` Full time derivative state vector of the ``XXX`` th sensitivity system at the last time point of the time integrator if :math:`\texttt{WRITE_SENS_LAST}` in :math:`\texttt{/input/return}` is enabled - + **Type:** double - + Group /output/solution ---------------------- @@ -38,9 +38,9 @@ Group /output/solution Time points at which the solution is written if :math:`\texttt{WRITE_SOLUTION_TIMES}` in :math:`\texttt{/input/return}` is enabled **Unit:** :math:`\mathrm{s}` - + **Type:** double - + Group /output/solution/unit_XXX ------------------------------- @@ -50,9 +50,9 @@ Group /output/solution/unit_XXX Interstitial solution as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}` - + **Type:** double - + ``SOLUTION_PARTICLE`` Mobile phase solution inside the particles as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if just one particle type is defined. @@ -66,25 +66,25 @@ Group /output/solution/unit_XXX Mobile phase solution inside the particles of type ``XXX`` as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if more than one particle type is defined. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{MP}}^{-3}` - + **Type:** double - + ``SOLUTION_SOLID`` Solid phase solution inside the particles as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if just one particle type is defined. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{SP}}^{-3}` - + **Type:** double - + ``SOLUTION_SOLID_PARTYPE_XXX`` Solid phase solution inside the particles of type ``XXX`` as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if more than one particle type is defined. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{SP}}^{-3}` - + **Type:** double - + ``SOLUTION_FLUX`` Flux solution as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage @@ -92,211 +92,211 @@ Group /output/solution/unit_XXX **Unit:** :math:`\mathrm{mol}\,\mathrm{m}^{-2}\,\mathrm{s}^{-1}` **Type:** double - + ``SOLUTION_VOLUME`` Volume solution **Unit:** :math:`\mathrm{m}^{3}` - + **Type:** double - + ``SOLUTION_OUTLET`` Tensor of solutions at the unit operation outlet with components as columns in time-port-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are both disabled. If the unit operation only has a single port, the port-dimension is removed if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}` - + **Type:** double - + ``SOLUTION_INLET`` Tensor of solutions at the unit operation inlet with components as columns in time-port-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are both disabled. If the unit operation only has a single port, the port-dimension is removed if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}` - + **Type:** double - + ``SOLUTION_OUTLET_COMP_XXX`` Component ``XXX`` of the solution at all outlet ports of the unit operation as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is enabled and :math:`\texttt{SPLIT_PORTS_DATA}` is disabled. If the unit operation only has a single port, a vector (1D array) is returned instead of a matrix if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}` - + **Type:** double - + ``SOLUTION_INLET_COMP_XXX`` Component ``XXX`` of the solution at all inlet ports of the unit operation inlet as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is enabled and :math:`\texttt{SPLIT_PORTS_DATA}` is disabled. If the unit operation only has a single port, a vector (1D array) is returned instead of a matrix if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}` - + **Type:** double - + ``SOLUTION_OUTLET_PORT_XXX`` All components at outlet port ``XXX`` of the solution of the unit operation as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is disabled and :math:`\texttt{SPLIT_PORTS_DATA}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}` - + **Type:** double - + ``SOLUTION_INLET_PORT_XXX`` All components at inlet port ``XXX`` of the solution of the unit operation inlet as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is disabled and :math:`\texttt{SPLIT_PORTS_DATA}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}` - + **Type:** double - + ``SOLUTION_OUTLET_PORT_XXX_COMP_YYY`` Component ``YYY`` at outlet port ``XXX`` of the solution of the unit operation. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are enabled, and the unit operation has multiple outlet ports. If the unit operation only has a single port, the field is created if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}` - + **Type:** double - + ``SOLUTION_INLET_PORT_XXX_COMP_YYY`` Component ``YYY`` at inlet port ``XXX`` of the solution of the unit operation. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are enabled, and the unit operation has multiple inlet ports. If the unit operation only has a single port, the field is created if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}` - + **Type:** double - + ``SOLDOT_BULK`` Interstitial solution time derivative as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}` - + **Type:** double - + ``SOLDOT_PARTICLE`` Mobile phase solution time derivative inside the particles as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if just one particle type is defined. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{MP}}^{-3}\,\mathrm{s}^{-1}` - + **Type:** double - + ``SOLDOT_PARTICLE_PARTYPE_XXX`` Mobile phase solution time derivative inside the particles of type ``XXX`` as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if more than one particle type is defined. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{MP}}^{-3}\,\mathrm{s}^{-1}` - + **Type:** double - + ``SOLDOT_SOLID`` Solid phase solution time derivative inside the particles as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if just one particle type is defined. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{MP}}^{-3}\,\mathrm{s}^{-1}` - + **Type:** double - + ``SOLDOT_SOLID_PARTYPE_XXX`` Solid phase solution time derivative inside the particles of type ``XXX`` as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if more than one particle type is defined. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{SP}}^{-3}\,\mathrm{s}^{-1}` - + **Type:** double - + ``SOLDOT_FLUX`` Flux solution time derivative as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage **Unit:** :math:`\mathrm{mol}\,\mathrm{m}^{-2}\,\mathrm{s}^{-2}` - + **Type:** double - + ``SOLDOT_VOLUME`` Volume solution time derivative **Unit:** :math:`\mathrm{m}^{3}\,\mathrm{s}^{-1}` - + **Type:** double - + ``SOLDOT_OUTLET`` Tensor of solution time derivatives at the unit operation outlet with components as columns in time-port-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are both disabled. If the unit operation only has a single port, the port-dimension is removed if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}` - + **Type:** double - + ``SOLDOT_INLET`` Tensor of solution time derivatives at the unit operation inlet with components as columns in time-port-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are both disabled. If the unit operation only has a single port, the port-dimension is removed if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}` - + **Type:** double - + ``SOLDOT_OUTLET_COMP_XXX`` Component ``XXX`` of the solution time derivative at all outlet ports of the unit operation as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is enabled and :math:`\texttt{SPLIT_PORTS_DATA}` is disabled. If the unit operation only has a single port, a vector (1D array) is returned instead of a matrix if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}` - + **Type:** double - + ``SOLDOT_INLET_COMP_XXX`` Component ``XXX`` of the solution time derivative at all inlet ports of the unit operation inlet as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is enabled and :math:`\texttt{SPLIT_PORTS_DATA}` is disabled. If the unit operation only has a single port, a vector (1D array) is returned instead of a matrix if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}` - + **Type:** double - + ``SOLDOT_OUTLET_PORT_XXX`` All components at outlet port ``XXX`` of the solution time derivative of the unit operation as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is disabled and :math:`\texttt{SPLIT_PORTS_DATA}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}` - + **Type:** double - + ``SOLDOT_INLET_PORT_XXX`` All components at inlet port ``XXX`` of the solution time derivative of the unit operation inlet as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is disabled and :math:`\texttt{SPLIT_PORTS_DATA}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}` - + **Type:** double - + ``SOLDOT_OUTLET_PORT_XXX_COMP_YYY`` Component ``YYY`` at outlet port ``XXX`` of the solution time derivative of the unit operation. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are enabled, and the unit operation has multiple outlet ports. If the unit operation only has a single port, the field is created if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}` - + **Type:** double - + ``SOLDOT_INLET_PORT_XXX_COMP_YYY`` Component ``YYY`` at inlet port ``XXX`` of the solution time derivative of the unit operation. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are enabled, and the unit operation has multiple inlet ports. If the unit operation only has a single port, the field is created if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}` - + **Type:** double - + ``LAST_STATE_Y`` State vector of this unit at the last time point of the time integrator if :math:`\texttt{WRITE_SOLUTION_LAST_UNIT}` in :math:`\texttt{/input/return/unit_XXX}` is enabled. Note that the vector includes the dedicated inlet DOFs at the beginning (length: :math:`\texttt{NCOMP} \cdot \texttt{NPORT}`). - + **Type:** double - + ``LAST_STATE_YDOT`` Time derivative state vector of this unit at the last time point of the time integrator if :math:`\texttt{WRITE_SOLUTION_LAST_UNIT}` in :math:`\texttt{/input/return/unit_XXX}` is enabled. Note that the vector includes the dedicated inlet DOFs at the beginning (length: :math:`\texttt{NCOMP} \cdot \texttt{NPORT}`). - + **Type:** double @@ -308,241 +308,241 @@ Group /output/sensitivity/param_XXX/unit_YYY Interstitial sensitivity as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENS_PARTICLE`` Mobile phase sensitivity inside the particles as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if just one particle type is defined. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{MP}}^{-3}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENS_PARTICLE_PARTYPE_XXX`` Mobile phase sensitivity inside the particles of type ``XXX`` as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if more than one particle type is defined. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{MP}}^{-3}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENS_SOLID`` Solid phase sensitivity inside the particles as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if just one particle type is defined. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{MP}}^{-3}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENS_SOLID_PARTYPE_XXX`` Solid phase sensitivity inside the particles of type ``XXX`` as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if more than one particle type is defined. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{SP}}^{-3}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENS_FLUX`` Flux sensitivity as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage **Unit:** :math:`\mathrm{mol}\,\mathrm{m}^{-2}\,\mathrm{s}^{-1}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENS_VOLUME`` Volume sensitivity **Unit:** :math:`\mathrm{m}^{3}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENS_OUTLET`` Tensor of sensitivities at the unit operation outlet with components as columns in time-port-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are both disabled. If the unit operation only has a single port, the port-dimension is removed if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENS_INLET`` Tensor of sensitivities at the unit operation inlet with components as columns in time-port-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are both disabled. If the unit operation only has a single port, the port-dimension is removed if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENS_OUTLET_COMP_XXX`` Component ``XXX`` of the sensitivity at all outlet ports of the unit operation as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is enabled and :math:`\texttt{SPLIT_PORTS_DATA}` is disabled. If the unit operation only has a single port, a vector (1D array) is returned instead of a matrix if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENS_INLET_COMP_XXX`` Component ``XXX`` of the sensitivity at all inlet ports of the unit operation inlet as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is enabled and :math:`\texttt{SPLIT_PORTS_DATA}` is disabled. If the unit operation only has a single port, a vector (1D array) is returned instead of a matrix if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENS_OUTLET_PORT_XXX`` All components at outlet port ``XXX`` of the sensitivity of the unit operation as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is disabled and :math:`\texttt{SPLIT_PORTS_DATA}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENS_INLET_PORT_XXX`` All components at inlet port ``XXX`` of the sensitivity of the unit operation inlet as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is disabled and :math:`\texttt{SPLIT_PORTS_DATA}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENS_OUTLET_PORT_XXX_COMP_YYY`` Component ``YYY`` at outlet port ``XXX`` of the sensitivity of the unit operation. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are enabled, and the unit operation has multiple outlet ports. If the unit operation only has a single port, the field is created if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENS_INLET_PORT_XXX_COMP_YYY`` Component ``YYY`` at inlet port ``XXX`` of the sensitivity of the unit operation. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are enabled, and the unit operation has multiple inlet ports. If the unit operation only has a single port, the field is created if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENSDOT_BULK`` Interstitial sensitivity time derivative as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENSDOT_PARTICLE`` Mobile phase sensitivity time derivative inside the particles as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if just one particle type is defined. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{MP}}^{-3}\,\mathrm{s}^{-1}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENSDOT_PARTICLE_PARTYPE_XXX`` Mobile phase sensitivity time derivative inside the particles of type ``XXX`` as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if more than one particle type is defined. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{MP}}^{-3}\,\mathrm{s}^{-1}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENSDOT_SOLID`` Solid phase sensitivity time derivative inside the particles as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if just one particle type is defined. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{MP}}^{-3}\,\mathrm{s}^{-1}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENSDOT_SOLID_PARTYPE_XXX`` Solid phase sensitivity time derivative inside the particles of type ``XXX`` as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage. Only present if more than one particle type is defined. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{SP}}^{-3}\,\mathrm{s}^{-1}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENSDOT_FLUX`` Flux sensitivity time derivative as :math:`n_{\text{Time}} \times \texttt{UNITOPORDERING}` tensor in row-major storage **Unit:** :math:`\mathrm{mol}\,\mathrm{m}^{-2}\,\mathrm{s}^{-2}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENSDOT_VOLUME`` Volume sensitivity time derivative **Unit:** :math:`^{3}\,\mathrm{s}\mathrm{m}^{-1}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENSDOT_OUTLET`` Tensor of sensitivity time derivatives at the unit operation outlet with components as columns in time-port-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are both disabled. If the unit operation only has a single port, the port-dimension is removed if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENSDOT_INLET`` Tensor of sensitivity time derivatives at the unit operation inlet with components as columns in time-port-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are both disabled. If the unit operation only has a single port, the port-dimension is removed if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENSDOT_OUTLET_COMP_XXX`` Component ``XXX`` of the sensitivity time derivative at all outlet ports of the unit operation as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is enabled and :math:`\texttt{SPLIT_PORTS_DATA}` is disabled. If the unit operation only has a single port, a vector (1D array) is returned instead of a matrix if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENSDOT_INLET_COMP_XXX`` Component ``XXX`` of the sensitivity time derivative at all inlet ports of the unit operation inlet as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is enabled and :math:`\texttt{SPLIT_PORTS_DATA}` is disabled. If the unit operation only has a single port, a vector (1D array) is returned instead of a matrix if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is disabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENSDOT_OUTLET_PORT_XXX`` All components at outlet port ``XXX`` of the sensitivity time derivative of the unit operation as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is disabled and :math:`\texttt{SPLIT_PORTS_DATA}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENSDOT_INLET_PORT_XXX`` All components at inlet port ``XXX`` of the sensitivity time derivative of the unit operation inlet as matrix in time-major storage. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` is disabled and :math:`\texttt{SPLIT_PORTS_DATA}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENSDOT_OUTLET_PORT_XXX_COMP_YYY`` Component ``YYY`` at outlet port ``XXX`` of the sensitivity time derivative of the unit operation. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are enabled, and the unit operation has multiple outlet ports. If the unit operation only has a single port, the field is created if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}\,[\mathrm{Param}]^{-1}` - + **Type:** double - + ``SENSDOT_INLET_PORT_XXX_COMP_YYY`` Component ``YYY`` at inlet port ``XXX`` of the sensitivity time derivative of the unit operation. Only present if :math:`\texttt{SPLIT_COMPONENTS_DATA}` and :math:`\texttt{SPLIT_PORTS_DATA}` are enabled, and the unit operation has multiple inlet ports. If the unit operation only has a single port, the field is created if :math:`\texttt{SINGLE_AS_MULTI_PORT}` is enabled. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}\,[\mathrm{Param}]^{-1}` - - **Type:** double - + + **Type:** double + /output/coordinates/unit_XXX ------------------------------ @@ -552,27 +552,27 @@ Group /output/sensitivity/param_XXX/unit_YYY Axial coordinates of the bulk discretization nodes **Unit:** :math:`\mathrm{m}` - + ================ ================================= **Type:** double **Length:** :math:`\texttt{NCOL}` ================ ================================= - + ``RADIAL_COORDINATES`` Radial coordinates of the bulk discretization nodes (only for 2D unit operations) **Unit:** :math:`\mathrm{m}` - + ================ ================================= **Type:** double **Length:** :math:`\texttt{NRAD}` ================ ================================= - + ``PARTICLE_COORDINATES_XXX`` Coordinates of the particle discretization nodes in particles of type ``XXX`` **Unit:** :math:`\mathrm{m}` - + ================ ================================= **Type:** double **Length:** :math:`\texttt{NPAR}` ================ ================================= diff --git a/doc/interface/parameter_dependencies.rst b/doc/interface/parameter_dependencies.rst index c632de17a..69b13cfb7 100644 --- a/doc/interface/parameter_dependencies.rst +++ b/doc/interface/parameter_dependencies.rst @@ -15,7 +15,7 @@ Group /input/model/unit_XXX ``COL_DISPERSION_DEP`` Parameter dependence of column dispersion on the interstitial velocity. Available for the LRM, LRMP and GRM units (with FV discretization only at the moment) - + ================ ===================================== ============= **Type:** string **Range:** :math:`\texttt{POWER_LAW}` **Length:** 1 ================ ===================================== ============= @@ -23,7 +23,7 @@ Group /input/model/unit_XXX ``FILM_DIFFUSION_DEP`` Parameter dependence of film diffusion on the interstitial velocity. Available for the LRMP unit (with FV discretization only at the moment) - + ================ ===================================== ============= **Type:** string **Range:** :math:`\texttt{POWER_LAW}` **Length:** 1 ================ ===================================== ============= @@ -48,7 +48,7 @@ Here, :math:`p_{dep}` is the dependent parameter and :math:`p_{on}` is the param ``COL_DISPERSION_DEP_BASE`` Base :math:`b` of the power law parameter dependence. Optional, defaults to :math:`1.0` - + ================ ============================= ============= **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 ================ ============================= ============= @@ -56,7 +56,7 @@ Here, :math:`p_{dep}` is the dependent parameter and :math:`p_{on}` is the param ``COL_DISPERSION_DEP_EXPONENT`` Exponent :math:`x` of the power law parameter dependence - + ================ ============================= ============= **Type:** double **Range:** :math:`\mathbb{R}` **Length:** 1 ================ ============================= ============= @@ -64,7 +64,7 @@ Here, :math:`p_{dep}` is the dependent parameter and :math:`p_{on}` is the param ``COL_DISPERSION_DEP_ABS`` Specifies whether or not the absolute value should be computed. Optional, defaults to :math:`1` - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= diff --git a/doc/interface/reaction/mass_action_law.rst b/doc/interface/reaction/mass_action_law.rst index 26c20b89f..b80931556 100644 --- a/doc/interface/reaction/mass_action_law.rst +++ b/doc/interface/reaction/mass_action_law.rst @@ -8,151 +8,151 @@ Mass Action Law ``MAL_KFWD_BULK`` Forward rate constants for bulk volume reactions (available for external functions) - + ================ ========================= =================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NREACT}` ================ ========================= =================================== - + ``MAL_KBWD_BULK`` Backward rate constants for bulk volume reactions (available for external functions) - + ================ ========================= =================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NREACT}` ================ ========================= =================================== - + ``MAL_KFWD_LIQUID`` Forward rate constants for particle liquid phase reactions (available for external functions) - + ================ ========================= =================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NREACT}` ================ ========================= =================================== - + ``MAL_KBWD_LIQUID`` Backward rate constants for particle liquid phase reactions (available for external functions) - + ================ ========================= =================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NREACT}` ================ ========================= =================================== - + ``MAL_KFWD_SOLID`` Forward rate constants for particle solid phase reactions (available for external functions) - + ================ ========================= =================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NREACT}` ================ ========================= =================================== - + ``MAL_KBWD_SOLID`` Backward rate constants for particle solid phase reactions (available for external functions) - + ================ ========================= =================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NREACT}` ================ ========================= =================================== - + ``MAL_STOICHIOMETRY_BULK`` Stoichiometric matrix of bulk volume reactions as :math:`\texttt{NCOMP} \times \texttt{NREACT}` matrix in row-major storage - + ================ ======================================================== **Type:** double **Length:** :math:`\texttt{NCOMP} \cdot \texttt{NREACT}` ================ ======================================================== - + ``MAL_EXPONENTS_BULK_FWD`` Forward exponent matrix of bulk volume reactions as :math:`\texttt{NCOMP} \times \texttt{NREACT}` matrix in row-major storage (optional, calculated from :math:`\texttt{MAL_STOICHIOMETRY_BULK}` by default) - + ================ ======================================================== **Type:** double **Length:** :math:`\texttt{NCOMP} \cdot \texttt{NREACT}` ================ ======================================================== - + ``MAL_EXPONENTS_BULK_BWD`` Backward exponent matrix of bulk volume reactions as :math:`\texttt{NCOMP} \times \texttt{NREACT}` matrix in row-major storage (optional, calculated from :math:`\texttt{MAL_STOICHIOMETRY_BULK}` by default) - + ================ ======================================================== **Type:** double **Length:** :math:`\texttt{NCOMP} \cdot \texttt{NREACT}` ================ ======================================================== - + ``MAL_STOICHIOMETRY_LIQUID`` Stoichiometric matrix of particle liquid phase reactions as :math:`\texttt{NCOMP} \times \texttt{NREACT}` matrix in row-major storage - + ================ ======================================================== **Type:** double **Length:** :math:`\texttt{NCOMP} \cdot \texttt{NREACT}` ================ ======================================================== - + ``MAL_EXPONENTS_LIQUID_FWD`` Forward exponent matrix of particle liquid phase reactions as :math:`\texttt{NCOMP} \times \texttt{NREACT}` matrix in row-major storage (optional, calculated from :math:`\texttt{MAL_STOICHIOMETRY_LIQUID}` by default) - + ================ ======================================================== **Type:** double **Length:** :math:`\texttt{NCOMP} \cdot \texttt{NREACT}` ================ ======================================================== - + ``MAL_EXPONENTS_LIQUID_BWD`` Backward exponent matrix of particle liquid phase reactions as :math:`\texttt{NCOMP} \times \texttt{NREACT}` matrix in row-major storage (optional, calculated from :math:`\texttt{MAL_STOICHIOMETRY_LIQUID}` by default) - + ================ ======================================================== **Type:** double **Length:** :math:`\texttt{NCOMP} \cdot \texttt{NREACT}` ================ ======================================================== - + ``MAL_EXPONENTS_LIQUID_FWD_MODSOLID`` Forward solid phase modifier exponent matrix of particle liquid phase reactions as :math:`\texttt{NTOTALBND} \times \texttt{NREACT}` matrix in row-major storage (optional, defaults to all 0) - + ================ ============================================================ **Type:** double **Length:** :math:`\texttt{NTOTALBND} \cdot \texttt{NREACT}` ================ ============================================================ - + ``MAL_EXPONENTS_LIQUID_BWD_MODSOLID`` Backward solid phase modifier exponent matrix of particle liquid phase reactions as :math:`\texttt{NTOTALBND} \times \texttt{NREACT}` matrix in row-major storage (optional, defaults to all 0) - + ================ ============================================================ **Type:** double **Length:** :math:`\texttt{NTOTALBND} \cdot \texttt{NREACT}` ================ ============================================================ - + ``MAL_STOICHIOMETRY_SOLID`` Stoichiometric matrix of particle solid phase reactions as :math:`\texttt{NTOTALBND} \times \texttt{NREACT}` matrix in row-major storage - + ================ ============================================================ **Type:** double **Length:** :math:`\texttt{NTOTALBND} \cdot \texttt{NREACT}` ================ ============================================================ - + ``MAL_EXPONENTS_SOLID_FWD`` Forward exponent matrix of particle solid phase reactions as :math:`\texttt{NTOTALBND} \times \texttt{NREACT}` matrix in row-major storage (optional, calculated from :math:`\texttt{MAL_STOICHIOMETRY_SOLID}` by default) - + ================ ============================================================ **Type:** double **Length:** :math:`\texttt{NTOTALBND} \cdot \texttt{NREACT}` ================ ============================================================ - + ``MAL_EXPONENTS_SOLID_BWD`` Backward exponent matrix of particle solid phase reactions as :math:`\texttt{NTOTALBND} \times \texttt{NREACT}` matrix in row-major storage (optional, calculated from :math:`\texttt{MAL_STOICHIOMETRY_SOLID}` by default) - + ================ ============================================================ **Type:** double **Length:** :math:`\texttt{NTOTALBND} \cdot \texttt{NREACT}` ================ ============================================================ - + ``MAL_EXPONENTS_SOLID_FWD_MODLIQUID`` Forward liquid phase modifier exponent matrix of particle solid phase reactions as :math:`\texttt{NCOMP} \times \texttt{NREACT}` matrix in row-major storage (optional, defaults to all 0) - + ================ ======================================================== **Type:** double **Length:** :math:`\texttt{NCOMP} \cdot \texttt{NREACT}` ================ ======================================================== - + ``MAL_EXPONENTS_SOLID_BWD_MODLIQUID`` Backward liquid phase modifier exponent matrix of particle solid phase reactions as :math:`\texttt{NCOMP} \times \texttt{NREACT}` matrix in row-major storage (optional, defaults to all 0) - + ================ ======================================================== **Type:** double **Length:** :math:`\texttt{NCOMP} \cdot \texttt{NREACT}` ================ ======================================================== diff --git a/doc/interface/reaction/michaelis_menten_kinetics.rst b/doc/interface/reaction/michaelis_menten_kinetics.rst index 76884ea2e..40e33a128 100644 --- a/doc/interface/reaction/michaelis_menten_kinetics.rst +++ b/doc/interface/reaction/michaelis_menten_kinetics.rst @@ -10,15 +10,15 @@ Michaelis Menten kinetics Stochiometric matrix :math:`S`. The substrate component :math:`c_S` is identified by the index of the first negative entry in the stoichiometry of the corresponding reaction. Input as reaction index major. - + ================ ============================= ======================================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NREACT} \cdot \texttt{NCOMP}` ================ ============================= ======================================================== - + ``MM_VMAX`` Limiting rate :math:`\mu_{\mathrm{max},j}` at saturation. - + ================ ============================= =================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NREACT}` ================ ============================= =================================== @@ -26,7 +26,7 @@ Michaelis Menten kinetics ``MM_KMM`` Michaelis constant :math:`k_{\mathrm{MM},j}`. - + ================ ============================= =================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NREACT}` ================ ============================= =================================== @@ -35,7 +35,7 @@ Michaelis Menten kinetics Inhibition constant :math:`k_{\mathrm{I},j,i}` w.r.t component :math:`i` and reaction :math:`j`. If :math:`k_{\mathrm{I},j,i} <= 0`, the component does not inhibit the reaction. Input as reaction index major. - + ================ ============================= ======================================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NREACT} \cdot \texttt{NCOMP}` - ================ ============================= ======================================================== \ No newline at end of file + ================ ============================= ======================================================== diff --git a/doc/interface/return_data.rst b/doc/interface/return_data.rst index 6426a3a95..b23217146 100644 --- a/doc/interface/return_data.rst +++ b/doc/interface/return_data.rst @@ -9,51 +9,51 @@ Group /input/return ``WRITE_SOLUTION_TIMES`` Write times at which a solution was produced (optional, defaults to 1) - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLUTION_LAST`` Write full solution state vector at last time point (optional, defaults to 0) - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SENS_LAST`` Write full sensitivity state vectors at last time point (optional, defaults to 0) - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``SPLIT_COMPONENTS_DATA`` Determines whether a joint dataset (matrix or tensor) for all components is created or if each component is put in a separate dataset (:math:`\texttt{XXX_COMP_000}`, :math:`\texttt{XXX_COMP_001}`, etc.) (optional, defaults to 1) - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``SPLIT_PORTS_DATA`` Determines whether a joint dataset (matrix or tensor) for all inlet/outlet ports is created or if each port is put in a separate dataset (:math:`\texttt{XXX_PORT_000}`, :math:`\texttt{XXX_PORT_001}`, etc.) (optional, defaults to 1) - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``SINGLE_AS_MULTI_PORT`` Determines whether single port unit operations are treated as multi port unit operations in the output naming scheme (i.e., :math:`\texttt{_PORT_XYZ_}` is added to the name) (optional, defaults to 0) - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + Group /input/return/unit_XXX ---------------------------- @@ -61,240 +61,239 @@ Group /input/return/unit_XXX ``WRITE_COORDINATES`` Write coordinates of discretization nodes - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLUTION_INLET`` Write solutions at unit operation inlet :math:`c^l_i(t,0)` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLUTION_OUTLET`` Write solutions at unit operation outlet (chromatograms) :math:`c^l_i(t,L)` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLUTION_BULK`` Write solutions of the bulk volume :math:`c^l_i` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLUTION_PARTICLE`` Write solutions of the particle mobile phase :math:`c^p_{j,i}` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLUTION_SOLID`` Write solutions of the solid phase :math:`c^s_{j,i,m_{j,i}}` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLUTION_FLUX`` Write solutions of the bead fluxes :math:`j_{f,i}` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLUTION_VOLUME`` Write solutions of the volume V - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLDOT_INLET`` Write solution time derivatives at unit operation inlet :math:`\partial c^l_i(t,0) / \partial t` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLDOT_OUTLET`` Write solution time derivatives at unit operation outlet (chromatograms) :math:`\partial c^l_i(t,L) / \partial t` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLDOT_BULK`` Write solution time derivatives of the bulk volume :math:`\partial c^l_i / \partial t` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLDOT_PARTICLE`` Write solution time derivatives of the particle mobile phase :math:`\partial c^p_{j,i} / \partial t` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLDOT_SOLID`` Write solution time derivatives of the solid phase :math:`\partial c^s_{j,i,m_{j,i}} / \partial t` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLDOT_FLUX`` Write solution time derivatives of the bead fluxes :math:`\partial j_{f,i} / \partial t` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLDOT_VOLUME`` Write solution time derivatives of the volume :math:`\partial V / \partial t` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SENS_INLET`` Write sensitivities at unit operation inlet :math:`\partial c^l_i(t,0) / \partial p` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SENS_OUTLET`` Write sensitivities at unit operation outlet (chromatograms) :math:`\partial c^l_i(t,L) / \partial p` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SENS_BULK`` Write sensitivities of the bulk volume :math:`\partial c^l_i / \partial p` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SENS_PARTICLE`` Write sensitivities of the particle mobile phase :math:`\partial c^p_{j,i} / \partial p` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SENS_SOLID`` Write sensitivities of the solid phase :math:`\partial c^s_{j,i,m_{j,i}} / \partial p` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SENS_FLUX`` Write sensitivities of the bead fluxes :math:`\partial j_{f,i} / \partial p` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SENS_VOLUME`` Write sensitivities of the volume :math:`\partial V / \partial p` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SENSDOT_INLET`` Write sensitivity time derivatives at unit operation inlet :math:`\partial^2 c^l_i(t,0) / (\partial p, \partial t)` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SENSDOT_OUTLET`` Write sensitivity time derivatives at unit operation outlet (chromatograms) :math:`\partial^2 c^l_i(t,L) / (\partial p, \partial t)` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SENSDOT_BULK`` Write sensitivity time derivatives of the bulk volume :math:`\partial^2 c^l_i / (\partial p, \partial t)` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SENSDOT_PARTICLE`` Write sensitivity time derivatives of the particle mobile phase :math:`\partial^2 c^p_{j,i} / (\partial p, \partial t)` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SENSDOT_SOLID`` Write sensitivity time derivatives of the solid phase :math:`\partial^2 c^s_{j,i,m_{j,i}} / (\partial p, \partial t)` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SENSDOT_FLUX`` Write sensitivity time derivatives of the bead fluxes :math:`\partial^2 j_{f,i} / (\partial p, \partial t)` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SENSDOT_VOLUME`` Write sensitivity time derivatives of the volume :math:`\partial^2 V / (\partial p, \partial t)` - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - + ``WRITE_SOLUTION_LAST_UNIT`` Write solution state vector of this unit at last time point - + ============= ========================== **Type:** int **Range:** :math:`\{0,1\}` ============= ========================== - diff --git a/doc/interface/sensitivities.rst b/doc/interface/sensitivities.rst index e179ae8b9..22b5e45e1 100644 --- a/doc/interface/sensitivities.rst +++ b/doc/interface/sensitivities.rst @@ -11,19 +11,19 @@ Group /input/sensitivity ``NSENS`` Number of sensitivities to be computed - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 0` **Length:** 1 ============= ========================= ============= - + ``SENS_METHOD`` Method used for computation of sensitivities (algorithmic differentiation) - + ================ =============================== ============= **Type:** string **Range:** :math:`\texttt{ad1}` **Length:** 1 ================ =============================== ============= - + .. _FFSensitivityParam: Group /input/sensitivity/param_XXX @@ -32,71 +32,71 @@ Group /input/sensitivity/param_XXX ``SENS_UNIT`` Unit operation index - + ============= ========================= ========================== **Type:** int **Range:** :math:`\geq 0` **Length:** :math:`\geq 1` ============= ========================= ========================== - + ``SENS_NAME`` Name of the parameter (Note that ``PAR_RADIUS`` and ``PAR_CORE_RADIUS`` sensitivities are only available for Finite Volume discretization) - + ================ =========================== **Type:** string **Length:** :math:`\geq 1` ================ =========================== - + ``SENS_COMP`` Component index (:math:`-1` if parameter is independent of components) - + ============= ========================== ============================ **Type:** int **Range:** :math:`\geq -1` **Length:** :math:`\geq 1` ============= ========================== ============================ - + ``SENS_PARTYPE`` Particle type index (:math:`-1` if parameter is independent of particle types) - + ============= ========================== =========================== **Type:** int **Range:** :math:`\geq -1` **Length:** :math:`\geq 1` ============= ========================== =========================== - + ``SENS_REACTION`` Reaction index (:math:`-1` if parameter is independent of reactions) - + ============= ========================== =========================== **Type:** int **Range:** :math:`\geq -1` **Length:** :math:`\geq 1` ============= ========================== =========================== - + ``SENS_BOUNDPHASE`` Bound phase index (:math:`-1` if parameter is independent of bound phases) - + ============= ========================== ========================== **Type:** int **Range:** :math:`\geq -1` **Length:** :math:`\geq 1` ============= ========================== ========================== - + ``SENS_SECTION`` Section index (:math:`-1` if parameter is independent of sections) - + ============= ========================== ========================== **Type:** int **Range:** :math:`\geq -1` **Length:** :math:`\geq 1` ============= ========================== ========================== - + ``SENS_ABSTOL`` Absolute tolerance used in the computation of the sensitivities (optional). Rule of thumb: :math:`\texttt{ABSTOL} / \texttt{PARAM_VALUE}` - + ================ =========================== ========================== **Type:** double **Range:** :math:`\geq 0.0` **Length:** :math:`\geq 1` ================ =========================== ========================== - + ``SENS_FACTOR`` Linear factor of the combined sensitivity (optional, taken as :math:`1.0` if left out) - + ================ ============================= ========================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\geq 1` ================ ============================= ========================== diff --git a/doc/interface/solver.rst b/doc/interface/solver.rst index d700f50cb..a39949ffb 100644 --- a/doc/interface/solver.rst +++ b/doc/interface/solver.rst @@ -9,55 +9,55 @@ Group /input/solver ``NTHREADS`` Number of used threads - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``USER_SOLUTION_TIMES`` Vector with timepoints at which the solution is evaluated **Unit:** :math:`\mathrm{s}` - + ================ ========================= ===================== **Type:** double **Range:** :math:`\geq 0` **Length:** Arbitrary ================ ========================= ===================== - + ``CONSISTENT_INIT_MODE`` - Consistent initialization mode (optional, defaults to :math:`1`). Valid values are: - - 0. None - 1. Full - 2. Once, full - 3. Lean - 4. Once, lean - 5. Full once, then lean - 6. None once, then full - 7. None once, then lean - + Consistent initialization mode (optional, defaults to :math:`1`). Valid values are: + + 0. None + 1. Full + 2. Once, full + 3. Lean + 4. Once, lean + 5. Full once, then lean + 6. None once, then full + 7. None once, then lean + ============= =================================== ============= **Type:** int **Range:** :math:`\{ 0, \dots, 7\}` **Length:** 1 ============= =================================== ============= - + ``CONSISTENT_INIT_MODE_SENS`` Consistent initialization mode for parameter sensitivities (optional, defaults to :math:`1`). Valid values are: - 0. None - 1. Full - 2. Once, full - 3. Lean - 4. Once, lean - 5. Full once, then lean - 6. None once, then full - 7. None once, then lean - + 0. None + 1. Full + 2. Once, full + 3. Lean + 4. Once, lean + 5. Full once, then lean + 6. None once, then full + 7. None once, then lean + ============= =================================== ============= **Type:** int **Range:** :math:`\{ 0, \dots, 7\}` **Length:** 1 ============= =================================== ============= - + .. _FFSolverTime: Group /solver/time_integrator @@ -66,7 +66,7 @@ Group /solver/time_integrator ``USE_MODIFIED_NEWTON`` Specifies whether modified or full Newton method should be used (optional, defaults to :math:`0`) - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= @@ -74,103 +74,103 @@ Group /solver/time_integrator ``ABSTOL`` Absolute tolerance in the solution of the original system - + ================ ====================== ============= **Type:** double **Range:** :math:`> 0` **Length:** 1 ================ ====================== ============= - + ``RELTOL`` Relative tolerance in the solution of the original system - + ================ ========================= ============= **Type:** double **Range:** :math:`\geq 0` **Length:** 1 ================ ========================= ============= - + ``ALGTOL`` Tolerance in the solution of the nonlinear consistency equations - + ================ ====================== ============= **Type:** double **Range:** :math:`> 0` **Length:** 1 ================ ====================== ============= - + ``RELTOL_SENS`` Relative tolerance in the solution of the sensitivity systems - + ================ ========================= ============= **Type:** double **Range:** :math:`\geq 0` **Length:** 1 ================ ========================= ============= - + ``INIT_STEP_SIZE`` Initial time integrator step size for each section or one value for all sections (0.0: IDAS default value), see IDAS guide 4.5, p.\ 36f. **Unit:** :math:`\mathrm{s}` - + ================ ========================= ===================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`1 / \texttt{NSEC}` ================ ========================= ===================================== - + ``MAX_STEPS`` Maximum number of timesteps taken by IDAS (0: IDAS default = 500), see IDAS guide Sec.~4.5 - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 0` **Length:** 1 ============= ========================= ============= - + ``MAX_STEP_SIZE`` Maximum size of timesteps taken by IDAS (optional, defaults to unlimited 0.0), see IDAS guide Sec.~4.5 **Unit:** :math:`\mathrm{s}` - + ================ ========================= ============= **Type:** double **Range:** :math:`\geq 0` **Length:** 1 ================ ========================= ============= - + ``ERRORTEST_SENS`` Determines whether (forward) sensitivities take part in local error test (optional, defaults to 1) - + ============= ========================== ============= **Type:** int **Range:** :math:`\{0,1\}` **Length:** 1 ============= ========================== ============= - + ``MAX_NEWTON_ITER`` Maximum number of Newton iterations in time step (optional, defaults to 4 (IDAS default)) - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 0` **Length:** 1 ============= ========================= ============= - + ``MAX_ERRTEST_FAIL`` Maximum number of local error test failures in time step (optional, defaults to 10 (IDAS default)) - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 0` **Length:** 1 ============= ========================= ============= - + ``MAX_CONVTEST_FAIL`` Maximum number of Newton convergence test failures (optional, defaults to 10 (IDAS default)) - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 0` **Length:** 1 ============= ========================= ============= - + ``MAX_NEWTON_ITER_SENS`` Maximum number of Newton iterations in forward sensitivity time step (optional, defaults to 4 (IDAS default)) - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 0` **Length:** 1 ============= ========================= ============= - + .. _FFSolverSections: Group /solver/sections @@ -179,26 +179,25 @@ Group /solver/sections ``NSEC`` Number of sections - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``SECTION_TIMES`` Simulation times at which the model changes or behaves discontinously; including start and end times **Unit:** :math:`\mathrm{s}` - + ================ ========================= =================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NSEC}+1` ================ ========================= =================================== - + ``SECTION_CONTINUITY`` Continuity indicator for each section transition: 0 (discontinuous) or 1 (continuous). - + ============= ========================== =================================== **Type:** int **Range:** :math:`\{0,1\}` **Length:** :math:`\texttt{NSEC}-1` ============= ========================== =================================== - diff --git a/doc/interface/system.rst b/doc/interface/system.rst index 035e5e3e5..dd8bb1cc5 100644 --- a/doc/interface/system.rst +++ b/doc/interface/system.rst @@ -9,51 +9,51 @@ Group /input/model ``NUNITS`` Number of unit operations in the system - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``INIT_STATE_Y`` Initial full state vector (optional, unit operation specific initial data is ignored) - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``INIT_STATE_YDOT`` Initial full time derivative state vector (optional, unit operation specific initial data is ignored) - + ================ ================================== **Type:** double **Length:** :math:`\texttt{NDOF}` ================ ================================== - + ``INIT_STATE_SENSY_XXX`` Number of unit operations in the system - + ================ ================================== **Type:** double **Length:** :math:`\texttt{NDOF}` ================ ================================== - + ``INIT_STATE_SENSYDOT_XXX`` Initial full state vector of the :math:`\texttt{XXX}` th sensitivity system (optional, unit operation specific initial data is ignored) - + ================ ================================== **Type:** double **Length:** :math:`\texttt{NDOF}` ================ ================================== - + ``NUNITS`` Initial full time derivative state vector of the :math:`\texttt{XXX}` th sensitivity system (optional, unit operation specific initial data is ignored) - + ================ ================================== **Type:** double **Length:** :math:`\texttt{NDOF}` ================ ================================== - + .. _FFModelSystemConnections: Group /input/model/connections @@ -62,23 +62,23 @@ Group /input/model/connections ``NSWITCHES`` Number of valve switches - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``CONNECTIONS_INCLUDE_PORTS`` Determines whether the :math:`\texttt{CONNECTIONS}` table includes ports (:math:`1`) or not (:math:`0`). Optional, defaults to 0 unless a unit operation model with multiple ports is present. - + ============= ============================ ============= **Type:** int **Range:** :math:`\{ 0,1 \}` **Length:** 1 ============= ============================ ============= - + ``CONNECTIONS_INCLUDE_DYNAMIC_FLOW`` Determines whether the :math:`\texttt{CONNECTIONS}` table includes linear, quadratic, and cubic flow rate coefficients (1) or not (0). Optional, defaults to 0. - + ============= ============================ ============= **Type:** int **Range:** :math:`\{ 0,1 \}` **Length:** 1 ============= ============================ ============= @@ -92,16 +92,16 @@ Group /input/model/connections/switch_XXX ``SECTION`` Index of the section that activates this connection set - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 0` **Length:** 1 ============= ========================= ============= - + ``CONNECTIONS`` - Matrix with list of connections in row-major storage. Columns are *UnitOpID from*, *UnitOpID to*, *Port from*, *Port to*, *Component from*, *Component to*, *volumetric flow rate*, *linear flow rate coefficient*, *quadratic flow rate coefficient*, *cubic flow rate coefficient*. - If both port indices are :math:`-1`, all ports are connected. - If both component indices are :math:`-1`, all components are connected. + Matrix with list of connections in row-major storage. Columns are *UnitOpID from*, *UnitOpID to*, *Port from*, *Port to*, *Component from*, *Component to*, *volumetric flow rate*, *linear flow rate coefficient*, *quadratic flow rate coefficient*, *cubic flow rate coefficient*. + If both port indices are :math:`-1`, all ports are connected. + If both component indices are :math:`-1`, all components are connected. The flow rate is a cubic function of time, @@ -110,24 +110,24 @@ Group /input/model/connections/switch_XXX where :math:`t_s` is the beginning of the section that activates the switch (i.e., :math:`\texttt{SECTION_TIMES}` at index :math:`\texttt{SECTION}`). - The port indices are left out if :math:`\texttt{CONNECTIONS_INCLUDE_PORTS}` is set to :math:`0` and no unit operation with multiple ports is present in the system. If a unit operation with multiple ports is present, :math:`\texttt{CONNECTIONS_INCLUDE_PORTS}` is ignored and port indices are mandatory. + The port indices are left out if :math:`\texttt{CONNECTIONS_INCLUDE_PORTS}` is set to :math:`0` and no unit operation with multiple ports is present in the system. If a unit operation with multiple ports is present, :math:`\texttt{CONNECTIONS_INCLUDE_PORTS}` is ignored and port indices are mandatory. The last three flow rate coefficients are left out if :math:`\texttt{CONNECTIONS_INCLUDE_DYNAMIC_FLOW}` is set to :math:`0`. Contrary to the constant coefficient, which has the parameter name :math:`\texttt{CONNECTION}`, the other coefficients are named :math:`\texttt{CONNECTION_LIN}`, :math:`\texttt{CONNECTION_QUAD}`, and :math:`\texttt{CONNECTION_CUB}`, respectively. For addressing the flow rates as a parameter senstivity, the mapping is as follows: - - :math:`\texttt{SENS_UNIT}` Unused, always set to :math:`-1` - - :math:`\texttt{SENS_BOUNDPHASE}` *UnitOpID from* - - :math:`\texttt{SENS_REACTION}` *UnitOpID to* - - :math:`\texttt{SENS_COMP}` *Port from* - - :math:`\texttt{SENS_PARTYPE}` *Port to* - - :math:`\texttt{SENS_SECTION}` :math:`\texttt{SECTION}` that activates the valve switch - + - :math:`\texttt{SENS_UNIT}` Unused, always set to :math:`-1` + - :math:`\texttt{SENS_BOUNDPHASE}` *UnitOpID from* + - :math:`\texttt{SENS_REACTION}` *UnitOpID to* + - :math:`\texttt{SENS_COMP}` *Port from* + - :math:`\texttt{SENS_PARTYPE}` *Port to* + - :math:`\texttt{SENS_SECTION}` :math:`\texttt{SECTION}` that activates the valve switch + ================ ========================== ============================================================ **Type:** double **Range:** :math:`\geq -1` **Length:** :math:`\{5,7,8,10\} \cdot \texttt{NCONNECTIONS}` ================ ========================== ============================================================ - + .. _FFModelExternalSourceLinInterp: Group /input/model/external/source_XXX - EXTFUN_TYPE = LINEAR_INTERP_DATA @@ -139,30 +139,30 @@ Group /input/model/external/source_XXX - EXTFUN_TYPE = LINEAR_INTERP_DATA The velocity is normalized to a column with length 1, hence the unit :math:`\mathrm{s}^{-1}`. **Unit:** :math:`\mathrm{s}^{-1}` - + ================ ========================= ============= **Type:** double **Range:** :math:`\geq 0` **Length:** 1 ================ ========================= ============= - + ``DATA`` Function values :math:`T` at the data points **Unit:** :math:`[\mathrm{Ext}]` - + ================ ============================= ===================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** Arbitrary ================ ============================= ===================== - + ``TIME`` Time of the data points **Unit:** :math:`\mathrm{s}` - + ================ =========================== ========================================= **Type:** double **Range:** :math:`\geq 0.0` **Length:** Same as :math:`\texttt{DATA}` ================ =========================== ========================================= - + .. _FFModelExternalSourcePieceCubicPoly: @@ -175,61 +175,61 @@ Group /input/model/external/source_XXX - EXTFUN_TYPE = PIECEWISE_CUBIC_POLY The velocity is normalized to a column with length 1, hence the unit :math:`\mathrm{s}^{-1}`. **Unit:** :math:`\mathrm{s}^{-1}` - + ================ ========================= ============= **Type:** double **Range:** :math:`\geq 0` **Length:** 1 ================ ========================= ============= - + ``CONST_COEFF`` Constant coefficients of piecewise cubic polynomial **Unit:** :math:`[\mathrm{Ext}]` - + ================ ============================= ===================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** Arbitrary ================ ============================= ===================== - + ``LIN_COEFF`` Linear coefficients of piecewise cubic polynomial **Unit:** :math:`[\mathrm{Ext}]\,\mathrm{s}^{-1}` - + ================ ============================= ================================================ **Type:** double **Range:** :math:`\mathbb{R}` **Length:** Same as :math:`\texttt{CONST_COEFF}` ================ ============================= ================================================ - + ``QUAD_COEFF`` Quadratic coefficients of piecewise cubic polynomial **Unit:** :math:`[\mathrm{Ext}]\,\mathrm{s}^{-2}` - + ================ ============================= ================================================ **Type:** double **Range:** :math:`\mathbb{R}` **Length:** Same as :math:`\texttt{CONST_COEFF}` ================ ============================= ================================================ - + ``CUBE_COEFF`` Cubic coefficients of piecewise cubic polynomial **Unit:** :math:`[\mathrm{Ext}]\,\mathrm{s}^{-3}` - + ================ ============================= ================================================ **Type:** double **Range:** :math:`\mathbb{R}` **Length:** Same as :math:`\texttt{CONST_COEFF}` ================ ============================= ================================================ - + ``SECTION_TIMES`` Simulation times at which a new piece begins (breaks of the piecewise polynomial) **Unit:** :math:`\mathrm{s}` - + ================ =========================== ========================================== **Type:** double **Range:** :math:`\geq 0.0` **Length:** :math:`\texttt{CONST_COEFF}+1` ================ =========================== ========================================== - + .. _FFModelSolver: Group /input/model/solver @@ -238,39 +238,39 @@ Group /input/model/solver ``GS_TYPE`` Type of Gram-Schmidt orthogonalization, see IDAS guide Section~4.5.7.3, p.~41f. A value of :math:`0` enables classical Gram-Schmidt, a value of 1 uses modified Gram-Schmidt. - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= - + ``MAX_KRYLOV`` Defines the size of the Krylov subspace in the iterative linear GMRES solver (0: :math:`\texttt{MAX_KRYLOV} = \texttt{NDOF}`) - + ============= ============================================== ============= **Type:** int **Range:** :math:`\{0, \dots, \texttt{NDOF}\}` **Length:** 1 ============= ============================================== ============= - + ``MAX_RESTARTS`` Maximum number of restarts in the GMRES algorithm. If lack of memory is not an issue, better use a larger Krylov space than restarts. - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 0` **Length:** 1 ============= ========================= ============= - + ``SCHUR_SAFETY`` Schur safety factor; Influences the tradeoff between linear iterations and nonlinear error control; see IDAS guide Section~2.1 and 5. - + ================ ========================= ============= **Type:** double **Range:** :math:`\geq 0` **Length:** 1 ================ ========================= ============= - + ``LINEAR_SOLUTION_MODE`` Determines whether the system of models is solved in parallel (1) or sequentially (2). A sequential solution is only possible for systems without cyclic connections. The setting can be chosen automatically (0) based on a heuristic (less than 25 unit operations and acyclic network selects sequential mode). Optional, defaults to automatic (0). - + ============= ============================== ============= **Type:** int **Range:** :math:`\{ 0,1,2 \}` **Length:** 1 ============= ============================== ============= diff --git a/doc/interface/unit_operations/2d_general_rate_model.rst b/doc/interface/unit_operations/2d_general_rate_model.rst index ab81c7eb5..4e90d15fe 100644 --- a/doc/interface/unit_operations/2d_general_rate_model.rst +++ b/doc/interface/unit_operations/2d_general_rate_model.rst @@ -12,171 +12,171 @@ For information on model equations, refer to :ref:`2d_general_rate_model_model`. ``UNIT_TYPE`` Specifies the type of unit operation model - + ================ ================================================= ============= **Type:** string **Range:** :math:`\texttt{GENERAL_RATE_MODEL_2D}` **Length:** 1 ================ ================================================= ============= - + ``NCOMP`` Number of chemical components in the chromatographic medium - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``ADSORPTION_MODEL`` Specifies the type of binding model of each particle type (or of all particle types if length is 1) - + ================ ========================================== ========================================== **Type:** string **Range:** See Section :ref:`FFAdsorption` **Length:** :math:`1 / \texttt{NPARTYPE}` ================ ========================================== ========================================== - + ``ADSORPTION_MODEL_MULTIPLEX`` Multiplexing mode of :math:`\texttt{ADSORPTION_MODEL}`. If set to 0, each particle type has a different binding model and the length of :math:`\texttt{ADSORPTION_MODEL}` is :math:`\texttt{NPARTYPE}`. If set to 1, all particle types share the same binding model and the length of :math:`\texttt{ADSORPTION_MODEL}` is 1. This field is optional and inferred from the length of :math:`\texttt{ADSORPTION_MODEL}` if left out. - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= - + ``NBOUND`` Number of bound states for each component in each particle type in type-major ordering - + ============= ========================= ========================================================================== **Type:** int **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NCOMP} / \texttt{NPARTYPE} \cdot \texttt{NCOMP}` ============= ========================= ========================================================================== - + ``REACTION_MODEL_BULK`` Specifies the type of reaction model of the bulk volume. The model is configured in the subgroup :math:`\texttt{reaction_bulk}`. - + ================ ======================================== ============= **Type:** string **Range:** See Section :ref:`FFReaction` **Length:** 1 ================ ======================================== ============= - + ``REACTION_MODEL_PARTICLES`` Specifies the type of reaction model of each particle type (or of all particle types if length is 1). The model is configured in the subgroup :math:`\texttt{reaction_particle}`, or :math:`\texttt{reaction_particle_XXX}` in case of disabled multiplexing. - + ================ ======================================== ========================================== **Type:** string **Range:** See Section :ref:`FFReaction` **Length:** :math:`1 / \texttt{NPARTYPE}` ================ ======================================== ========================================== - + ``REACTION_MODEL_PARTICLES_MULTIPLEX`` Multiplexing mode of :math:`\texttt{REACTION_MODEL_PARTICLES}`. If set to 0, each particle type has a different reaction model and the length of :math:`\texttt{REACTION_MODEL_PARTICLES}` is :math:`\texttt{NPARTYPE}`. If set to 1, all particle types share the same reaction model and the length of :math:`\texttt{REACTION_MODEL_PARTICLES}` is 1. This field is optional and inferred from the length of :math:`\texttt{REACTION_MODEL_PARTICLES}` if left out. - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= - + ``INIT_C`` Initial concentrations for each component in all radial zones the bulk mobile phase (length :math:`\texttt{NCOMP}`), or for each component in each radial zone (length :math:`\texttt{NCOMP} \cdot \texttt{NRAD}`, ordering radial-major) **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}` - + ================ ========================= ========================================================================= **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NCOMP} / \texttt{NCOMP} \cdot \texttt{NRAD}` ================ ========================= ========================================================================= - + ``INIT_CP`` Initial concentrations for each component in the bead liquid phase (optional, :math:`\texttt{INIT_C}` is used if left out). The length of this field can be :math:`\texttt{NCOMP}` (same values for each radial zone and particle type), :math:`\texttt{NPARTYPE} \cdot \texttt{NCOMP}` (same values for each radial zone), :math:`\texttt{RAD} \cdot \texttt{NCOMP}` (same values for each particle type), or :math:`\texttt{NRAD} \cdot \texttt{NPARTYPE} \cdot \texttt{NCOMP}`. The ordering is radial-type-major. Values for each particle type can only be given when :math:`\texttt{ADSORPTION_MODEL_MULTIPLEX}` is 0. In the radial-inhomogeneous case, the :math:`\texttt{SENS_REACTION}` field is used for indexing the radial zone when specifying parameter sensitivities. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{MP}}^{-3}` - + ================ ========================= **Type:** double **Range:** :math:`\geq 0` ================ ========================= - + ``INIT_Q`` Initial concentrations for each bound state of each component in the bead solid phase. If :math:`\texttt{ADSORPTION_MODEL_MULTIPLEX}` is 0, values for each particle type are required in type-component-major ordering (length is :math:`\texttt{NTOTALBND}`). If :math:`\texttt{ADSORPTION_MODEL_MULTIPLEX}` is 1, values for one particle type are required in component-major ordering (length is :math:`\sum_{i = 0}^{\texttt{NCOMP} - 1} \texttt{NBND}_i`). Alternatively, values for each radial zone can be supplied. If :math:`\texttt{ADSORPTION_MODEL_MULTIPLEX}` is 0, values for each radial zone and each particle type are required in radial-type-component-major ordering (length is :math:`\texttt{NRAD} \cdot \texttt{NTOTALBND}`). If :math:`\texttt{ADSORPTION_MODEL_MULTIPLEX}` is 1, values for each radial zone and all particle types are required in radial-component-major ordering (length is :math:`\texttt{NRAD} \cdot \sum_{i = 0}^{\texttt{NCOMP} - 1} \texttt{NBND}_i`). In the radial-inhomogeneous case, the :math:`\texttt{SENS_REACTION}` field is used for indexing the radial zone when specifying parameter sensitivities. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{SP}}^{-3}` - + ================ ========================= **Type:** double **Range:** :math:`\geq 0` ================ ========================= - + ``INIT_STATE`` Full state vector for initialization (optional, :math:`\texttt{INIT_C}`, :math:`\texttt{INIT_CP}`, and :math:`\texttt{INIT_Q}` will be ignored; if length is :math:`2\texttt{NDOF}`, then the second half is used for time derivatives) **Unit:** :math:`various` - + ================ ============================= ================================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NDOF} / 2\texttt{NDOF}` ================ ============================= ================================================== - + ``COL_DISPERSION`` Axial dispersion coefficient. In case of a spatially inhomogeneous setting, the :math:`\texttt{SENS_PARTYPE}` field is used for indexing the radial zone when specifying parameter sensitivities. **Unit:** :math:`\mathrm{m}_{\mathrm{IV}}^{2}\,\mathrm{s}^{-1}` - + ================ ========================= ========================================================= **Type:** double **Range:** :math:`\geq 0` **Length:** see :math:`\texttt{COL_DISPERSION_MULTIPLEX}` ================ ========================= ========================================================= - + ``COL_DISPERSION_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{COL_DISPERSION}`. Determines whether :math:`\texttt{COL_DISPERSION}` is treated as component-, radial-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{COL_DISPERSION}`. Valid modes are: - - 0. Component-independent, radial-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is 1 - 1. Component-independent, radial-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NRAD}` - 2. Component-dependent, radial-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP}` - 3. Component-dependent, radial-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NRAD}`; ordering is radial-major - 4. Component-independent, radial-independent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NSEC}` - 5. Component-independent, radial-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NRAD} \cdot \texttt{NSEC}`; ordering is section-major - 6. Component-dependent, radial-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - 7. Component-dependent, radial-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NRAD} \cdot \texttt{NSEC}`; ordering is section-radial-major - + Multiplexing mode of :math:`\texttt{COL_DISPERSION}`. Determines whether :math:`\texttt{COL_DISPERSION}` is treated as component-, radial-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{COL_DISPERSION}`. Valid modes are: + + 0. Component-independent, radial-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is 1 + 1. Component-independent, radial-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NRAD}` + 2. Component-dependent, radial-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP}` + 3. Component-dependent, radial-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NRAD}`; ordering is radial-major + 4. Component-independent, radial-independent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NSEC}` + 5. Component-independent, radial-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NRAD} \cdot \texttt{NSEC}`; ordering is section-major + 6. Component-dependent, radial-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major + 7. Component-dependent, radial-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NRAD} \cdot \texttt{NSEC}`; ordering is section-radial-major + ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 7 \}` **Length:** 1 ============= =================================== ============= - + ``COL_DISPERSION_RADIAL`` Radial dispersion coefficient. In case of a spatially inhomogeneous setting, the :math:`\texttt{SENS_PARTYPE}` field is used for indexing the radial zone when specifying parameter sensitivities. **Unit:** :math:`\mathrm{m}_{\mathrm{IV}}^{2}\,\mathrm{s}^{-1}` - + ================ ========================= ================================================================ **Type:** double **Range:** :math:`\geq 0` **Length:** see :math:`\texttt{COL_DISPERSION_RADIAL_MULTIPLEX}` ================ ========================= ================================================================ - + ``COL_DISPERSION_RADIAL_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{COL_DISPERSION_RADIAL}`. Determines whether :math:`\texttt{COL_DISPERSION_RADIAL}` is treated as component-, radial-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{COL_DISPERSION_RADIAL}`. Valid modes are: - - 0. Component-independent, radial-independent, section-independent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is 1 - 1. Component-independent, radial-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is :math:`\texttt{NRAD}` - 2. Component-dependent, radial-independent, section-independent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is :math:`\texttt{NCOMP}` - 3. Component-dependent, radial-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is :math:`\texttt{NCOMP} \cdot \texttt{NRAD}`; ordering is radial-major - 4. Component-independent, radial-independent, section-dependent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is :math:`\texttt{NSEC}` - 5. Component-independent, radial-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is :math:`\texttt{NRAD} \cdot \texttt{NSEC}`; ordering is section-major - 6. Component-dependent, radial-independent, section-independent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - 7. Component-dependent, radial-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is :math:`\texttt{NCOMP} \cdot \texttt{NRAD} \cdot \texttt{NSEC}`; ordering is section-radial-major - + Multiplexing mode of :math:`\texttt{COL_DISPERSION_RADIAL}`. Determines whether :math:`\texttt{COL_DISPERSION_RADIAL}` is treated as component-, radial-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{COL_DISPERSION_RADIAL}`. Valid modes are: + + 0. Component-independent, radial-independent, section-independent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is 1 + 1. Component-independent, radial-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is :math:`\texttt{NRAD}` + 2. Component-dependent, radial-independent, section-independent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is :math:`\texttt{NCOMP}` + 3. Component-dependent, radial-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is :math:`\texttt{NCOMP} \cdot \texttt{NRAD}`; ordering is radial-major + 4. Component-independent, radial-independent, section-dependent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is :math:`\texttt{NSEC}` + 5. Component-independent, radial-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is :math:`\texttt{NRAD} \cdot \texttt{NSEC}`; ordering is section-major + 6. Component-dependent, radial-independent, section-independent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major + 7. Component-dependent, radial-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION_RADIAL}` is :math:`\texttt{NCOMP} \cdot \texttt{NRAD} \cdot \texttt{NSEC}`; ordering is section-radial-major + ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 7 \}` **Length:** 1 ============= =================================== ============= - + ``COL_LENGTH`` Column length **Unit:** :math:`\mathrm{m}` - + ================ ====================== ============= **Type:** double **Range:** :math:`> 0` **Length:** 1 ================ ====================== ============= - + ``COL_RADIUS`` Column radius. This parameter is optional if ``CROSS_SECTION_AREA`` is provided. @@ -196,88 +196,88 @@ For information on model equations, refer to :ref:`2d_general_rate_model_model`. ================ ===================== ============= **Type:** double **Range:** :math:`>0` **Length:** 1 ================ ===================== ============= - + ``COL_POROSITY`` Column porosity, either constant (length is 1) or for each radial zone (length is :math:`\texttt{NRAD}`). In case of a spatially inhomogeneous setting, the :math:`\texttt{SENS_PARTYPE}` field is used for indexing the radial zone when specifying parameter sensitivities. - + ================ ======================== ===================================== **Type:** double **Range:** :math:`(0,1]` **Length:** :math:`1 / \texttt{NRAD}` ================ ======================== ===================================== - + ``FILM_DIFFUSION`` Film diffusion coefficients for each component of each particle type **Unit:** :math:`\mathrm{m}\,\mathrm{s}^{-1}` - + ================ ========================= ======================================================= **Type:** double **Range:** :math:`\geq 0` **Length:** see :math:`\texttt{FILM_DIFFUSION_MULTIPLEX}` ================ ========================= ======================================================= - + ``FILM_DIFFUSION_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{FILM_DIFFUSION}`. Determines whether :math:`\texttt{FILM_DIFFUSION}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{FILM_DIFFUSION}`. Valid modes are: + Multiplexing mode of :math:`\texttt{FILM_DIFFUSION}`. Determines whether :math:`\texttt{FILM_DIFFUSION}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{FILM_DIFFUSION}`. Valid modes are: + + 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP}` + 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major + 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major + 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major - 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP}` - 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major - 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major - ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 3 \}` **Length:** 1 ============= =================================== ============= - + ``PAR_POROSITY`` Particle porosity of all particle types or for each particle type - + ================ ======================== ========================================= **Type:** double **Range:** :math:`(0,1]` **Length:** :math:`1 / \texttt{NPARTYPE}` ================ ======================== ========================================= - + ``PAR_RADIUS`` Particle radius of all particle types or for each particle type **Unit:** :math:`\mathrm{m}` - + ================ ===================== ========================================= **Type:** double **Range:** :math:`>0` **Length:** :math:`1 / \texttt{NPARTYPE}` ================ ===================== ========================================= - + ``PAR_CORERADIUS`` Particle core radius of all particle types or for each particle type (optional, defaults to :math:`0~m`) **Unit:** :math:`\mathrm{m}` - + ================ =========================================== ========================================= **Type:** double **Range:** :math:`[0, \texttt{PAR_RADIUS})` **Length:** :math:`1 / \texttt{NPARTYPE}` ================ =========================================== ========================================= - + ``PORE_ACCESSIBILITY`` Pore accessibility factor of each component in each particle type (optional, defaults to 1). Note: Should not be used in combination with any binding model! - + ================ ========================= ============================================================= **Type:** double **Range:** :math:`(0, 1]` **Length:** see :math:`\texttt{PORE_ACCESSIBILITY_MULTIPLEX}` ================ ========================= ============================================================= - + ``PORE_ACCESSIBILITY_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{PORE_ACCESSIBILITY}`. Determines whether :math:`\texttt{PORE_ACCESSIBILITY}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PORE_ACCESSIBILITY}`. Valid modes are: + Multiplexing mode of :math:`\texttt{PORE_ACCESSIBILITY}`. Determines whether :math:`\texttt{PORE_ACCESSIBILITY}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PORE_ACCESSIBILITY}`. Valid modes are: + + 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP}` + 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major + 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major + 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major - 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP}` - 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major - 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major - ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 3 \}` **Length:** 1 ============= =================================== ============= - + ``PAR_DIFFUSION`` Effective particle diffusion coefficients of each component in each particle type @@ -287,101 +287,101 @@ For information on model equations, refer to :ref:`2d_general_rate_model_model`. ================ ========================= ======================================================== **Type:** double **Range:** :math:`\geq 0` **Length:** see :math:`\texttt{PAR_DIFFUSION_MULTIPLEX}` ================ ========================= ======================================================== - + ``PAR_DIFFUSION_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{PAR_DIFFUSION}`. Determines whether :math:`\texttt{PAR_DIFFUSION}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PAR_DIFFUSION}`. Valid modes are: + Multiplexing mode of :math:`\texttt{PAR_DIFFUSION}`. Determines whether :math:`\texttt{PAR_DIFFUSION}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PAR_DIFFUSION}`. Valid modes are: + + 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP}` + 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major + 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major + 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major - 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP}` - 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major - 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major - ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 3 \}` **Length:** 1 ============= =================================== ============= - + ``PAR_SURFDIFFUSION`` Particle surface diffusion coefficients of each bound state of each component in each particle type (optional, defaults to all :math:`0~m_{SP}^2 s^{-1}`) **Unit:** :math:`\mathrm{m}_{\mathrm{SP}}^{2}\,\mathrm{s}^{-1}` - + ================ ========================= ============================================================ **Type:** double **Range:** :math:`\geq 0` **Length:** see :math:`\texttt{PAR_SURFDIFFUSION_MULTIPLEX}` ================ ========================= ============================================================ - + ``PAR_SURFDIFFUSION_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{PAR_SURFDIFFUSION}`. Determines whether :math:`\texttt{PAR_SURFDIFFUSION}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PAR_SURFDIFFUSION}`. Valid modes are: + Multiplexing mode of :math:`\texttt{PAR_SURFDIFFUSION}`. Determines whether :math:`\texttt{PAR_SURFDIFFUSION}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PAR_SURFDIFFUSION}`. Valid modes are: + + 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NBND}`; ordering is component-major + 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NBND} \cdot \texttt{NSEC}`; ordering is section-component-major + 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NTOTALBND}`; ordering is type-component-major + 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NTOTALBND} \cdot \texttt{NSEC}`; ordering is section-type-component-major - 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NBND}`; ordering is component-major - 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NBND} \cdot \texttt{NSEC}`; ordering is section-component-major - 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NTOTALBND}`; ordering is type-component-major - 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NTOTALBND} \cdot \texttt{NSEC}`; ordering is section-type-component-major - ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 3 \}` **Length:** 1 ============= =================================== ============= - + ``VELOCITY`` Indicates flow direction in each radial zone (forward if value is positive, backward if value is negative), see Section :ref:`MUOPGRMflow2D`). In case of a spatially inhomogeneous setting, the :math:`\texttt{SENS_PARTYPE}` field is used for indexing the radial cell when specifying parameter sensitivities. - + ================ ============================= =================================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** see :math:`\texttt{VELOCITY_MULTIPLEX}` ================ ============================= =================================================== - + ``VELOCITY_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{VELOCITY}`. Determines whether :math:`\texttt{VELOCITY}` is treated as radial- and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{VELOCITY}`. Valid modes are: + Multiplexing mode of :math:`\texttt{VELOCITY}`. Determines whether :math:`\texttt{VELOCITY}` is treated as radial- and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{VELOCITY}`. Valid modes are: + + 0. Radial-independent, section-independent; length of :math:`\texttt{VELOCITY}` is 1 + 1. Radial-dependent, section-independent; length of :math:`\texttt{VELOCITY}` is :math:`\texttt{NRAD}` + 2. Section-dependent; length of :math:`\texttt{VELOCITY}` is :math:`\texttt{NSEC}` + 3. Radial-dependent, section-dependent; length of :math:`\texttt{VELOCITY}` is :math:`\texttt{NRAD} \cdot \texttt{NSEC}`; ordering is section-major - 0. Radial-independent, section-independent; length of :math:`\texttt{VELOCITY}` is 1 - 1. Radial-dependent, section-independent; length of :math:`\texttt{VELOCITY}` is :math:`\texttt{NRAD}` - 2. Section-dependent; length of :math:`\texttt{VELOCITY}` is :math:`\texttt{NSEC}` - 3. Radial-dependent, section-dependent; length of :math:`\texttt{VELOCITY}` is :math:`\texttt{NRAD} \cdot \texttt{NSEC}`; ordering is section-major - ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 3 \}` **Length:** 1 ============= =================================== ============= - + ``NPARTYPE`` Number of particle types. Optional, inferred from the length of :math:`\texttt{NPAR}` or :math:`\texttt{NBOUND}` if left out. - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``PAR_GEOM`` Specifies the particle geometry for all or each particle type. Valid values are :math:`\texttt{SPHERE}`, :math:`\texttt{CYLINDER}`, :math:`\texttt{SLAB}`. Optional, defaults to :math:`\texttt{SPHERE}`. - + ================ ================================================= **Type:** string **Length:** :math:`1` / :math:`\texttt{NPARTYPE}` ================ ================================================= - + ``PAR_TYPE_VOLFRAC`` Volume fractions of the particle types. The volume fractions can be set homogeneous or individually along both axes. For each cell, the volume fractions have to sum to 1. In case of a spatially inhomogeneous setting, the :math:`\texttt{SENS_SECTION}` field is used for indexing the axial cell and the :math:`\texttt{SENS_REACTION}` field is used for indexing the radial cell when specifying parameter sensitivities. This field is optional in case of only one particle type. - + ================ ======================== =========================================================== **Type:** double **Range:** :math:`[0,1]` **Length:** see :math:`\texttt{PAR_TYPE_VOLFRAC_MULTIPLEX}` ================ ======================== =========================================================== - + ``PAR_TYPE_VOLFRAC_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{PAR_TYPE_VOLFRAC}`. Determines whether :math:`\texttt{PAR_TYPE_VOLFRAC}` is treated as radial- and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PAR_TYPE_VOLFRAC}`. Valid modes are: + Multiplexing mode of :math:`\texttt{PAR_TYPE_VOLFRAC}`. Determines whether :math:`\texttt{PAR_TYPE_VOLFRAC}` is treated as radial- and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PAR_TYPE_VOLFRAC}`. Valid modes are: + + 0. Radial-independent, axial-independent; length of :math:`\texttt{PAR_TYPE_VOLFRAC}` is :math:`\texttt{NPARTYPE}` + 1. Radial-dependent, axial-independent; length of :math:`\texttt{PAR_TYPE_VOLFRAC}` is :math:`\texttt{NRAD} \cdot \texttt{NPARTYPE}`; ordering is radial-major + 2. Axial-dependent; length of :math:`\texttt{PAR_TYPE_VOLFRAC}` is :math:`\texttt{NCOL} \cdot \texttt{NPARTYPE}`; ordering is axial-major + 3. Radial-dependent, axial-dependent; length of :math:`\texttt{PAR_TYPE_VOLFRAC}` is :math:`\texttt{NCOL} \cdot \texttt{NRAD} \cdot \texttt{NPARTYPE}`; ordering is axial-radial-major - 0. Radial-independent, axial-independent; length of :math:`\texttt{PAR_TYPE_VOLFRAC}` is :math:`\texttt{NPARTYPE}` - 1. Radial-dependent, axial-independent; length of :math:`\texttt{PAR_TYPE_VOLFRAC}` is :math:`\texttt{NRAD} \cdot \texttt{NPARTYPE}`; ordering is radial-major - 2. Axial-dependent; length of :math:`\texttt{PAR_TYPE_VOLFRAC}` is :math:`\texttt{NCOL} \cdot \texttt{NPARTYPE}`; ordering is axial-major - 3. Radial-dependent, axial-dependent; length of :math:`\texttt{PAR_TYPE_VOLFRAC}` is :math:`\texttt{NCOL} \cdot \texttt{NRAD} \cdot \texttt{NPARTYPE}`; ordering is axial-radial-major - ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 3 \}` **Length:** 1 ============= =================================== ============= - + Group /input/model/unit_XXX/discretization - UNIT_TYPE - GENERAL_RATE_MODEL_2D ------------------------------------------------------------------------------ @@ -389,125 +389,125 @@ Group /input/model/unit_XXX/discretization - UNIT_TYPE - GENERAL_RATE_MODEL_2D ``NCOL`` Number of axial column discretization cells - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``NRAD`` Number of radial column discretization cells - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``NPAR`` Number of particle (radial) discretization cells for each particle type - + ============= ========================= ========================================= **Type:** int **Range:** :math:`\geq 1` **Length:** :math:`1 / \texttt{NPARTYPE}` ============= ========================= ========================================= - + ``RADIAL_DISC_TYPE`` Specifies the radial discretization scheme. Valid values are :math:`\texttt{EQUIDISTANT}`, :math:`\texttt{EQUIVOLUME}`, and :math:`\texttt{USER_DEFINED}`. - + ================ ============= **Type:** string **Length:** 1 ================ ============= - + ``RADIAL_COMPARTMENTS`` Coordinates for the radial compartment boundaries (ignored if :math:`\texttt{RADIAL_DISC_TYPE} \neq \texttt{USER_DEFINED}`). The coordinates are absolute and have to include the endpoints 0 and :math:`\texttt{COLUMN_RADIUS}`. The values are expected in ascending order (i.e., 0 is the first and :math:`\texttt{COLUMN_RADIUS}` the last value in the array). **Unit:** :math:`\mathrm{m}` - + ================ ============================================= ==================================== **Type:** double **Range:** :math:`[0,\texttt{COLUMN_RADIUS}]` **Length:** :math:`\texttt{NRAD} + 1` ================ ============================================= ==================================== - + ``PAR_DISC_TYPE`` Specifies the discretization scheme inside the particles for all or each particle type. Valid values are :math:`\texttt{EQUIDISTANT_PAR}`, :math:`\texttt{EQUIVOLUME_PAR}`, and :math:`\texttt{USER_DEFINED_PAR}`. - + ================ ========================================= **Type:** string **Length:** :math:`1 / \texttt{NPARTYPE}` ================ ========================================= - + ``PAR_DISC_VECTOR`` Node coordinates for the cell boundaries (ignored if :math:`\texttt{PAR_DISC_TYPE} \neq \texttt{USER_DEFINED_PAR}`). The coordinates are relative and have to include the endpoints 0 and 1. They are later linearly mapped to the true radial range :math:`[r_{c,j}, r_{p,j}]`. The coordinates for each particle type are appended to one long vector in type-major ordering. - + ================ ======================== =============================================== **Type:** double **Range:** :math:`[0,1]` **Length:** :math:`sum_i (\texttt{NPAR}_i + 1)` ================ ======================== =============================================== - + ``PAR_BOUNDARY_ORDER`` Order of accuracy of outer particle boundary condition. Optional, defaults to 2. - + ============= ============================ ============= **Type:** int **Range:** :math:`\{ 1,2 \}` **Length:** 1 ============= ============================ ============= - + ``USE_ANALYTIC_JACOBIAN`` Determines whether analytically computed Jacobian matrix (faster) is used (value is 1) instead of Jacobians generated by algorithmic differentiation (slower, value is 0) - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= - + ``LINEAR_SOLVER_BULK`` - Linear solver used for the sparse column bulk block. This field is optional, the best available method is selected (i.e., sparse direct solver if possible). Valid values are: + Linear solver used for the sparse column bulk block. This field is optional, the best available method is selected (i.e., sparse direct solver if possible). Valid values are: + + - :math:`\texttt{DENSE}` Converts the sparse matrix into a banded matrix and uses regular LAPACK. Slow and memory intensive, but always available. + - :math:`\texttt{UMFPACK}` Uses the UMFPACK sparse direct solver (LU decomposition) from SuiteSparse. Fast, but has to be enabled when compiling and requires UMFPACK library. + - :math:`\texttt{SUPERLU}` Uses the SuperLU sparse direct solver (LU decomposition). Fast, but has to be enabled when compiling and requires SuperLU library. - - :math:`\texttt{DENSE}` Converts the sparse matrix into a banded matrix and uses regular LAPACK. Slow and memory intensive, but always available. - - :math:`\texttt{UMFPACK}` Uses the UMFPACK sparse direct solver (LU decomposition) from SuiteSparse. Fast, but has to be enabled when compiling and requires UMFPACK library. - - :math:`\texttt{SUPERLU}` Uses the SuperLU sparse direct solver (LU decomposition). Fast, but has to be enabled when compiling and requires SuperLU library. - ================ ======================================================================= ============= **Type:** string **Range:** :math:`\{\texttt{DENSE},\texttt{UMFPACK},\texttt{SUPERLU}\}` **Length:** 1 ================ ======================================================================= ============= - + ``RECONSTRUCTION`` Type of reconstruction method for fluxes - + ================ ================================ ============= **Type:** string **Range:** :math:`\texttt{WENO}` **Length:** 1 ================ ================================ ============= - + ``GS_TYPE`` Type of Gram-Schmidt orthogonalization, see IDAS guide Section~4.5.7.3, p.~41f. A value of 0 enables classical Gram-Schmidt, a value of 1 uses modified Gram-Schmidt. - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= - + ``MAX_KRYLOV`` Defines the size of the Krylov subspace in the iterative linear GMRES solver (0: :math:`\texttt{MAX_KRYLOV} = \texttt{NCOL} \cdot \texttt{NRAD} \cdot \texttt{NCOMP} \cdot \texttt{NPARTYPE}`) - + ============= ================================================================================================================ ============= **Type:** int **Range:** :math:`\{0, \dots, \texttt{NCOL} \cdot \texttt{NRAD} \cdot \texttt{NCOMP} \cdot \texttt{NPARTYPE} \}` **Length:** 1 ============= ================================================================================================================ ============= - + ``MAX_RESTARTS`` Maximum number of restarts in the GMRES algorithm. If lack of memory is not an issue, better use a larger Krylov space than restarts. - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 0` **Length:** 1 ============= ========================= ============= - + ``SCHUR_SAFETY`` Schur safety factor; Influences the tradeoff between linear iterations and nonlinear error control; see IDAS guide Section~2.1 and 5. - + ================ ========================= ============= **Type:** double **Range:** :math:`\geq 0` **Length:** 1 ================ ========================= ============= diff --git a/doc/interface/unit_operations/cstr.rst b/doc/interface/unit_operations/cstr.rst index 123a86880..30e95c8bb 100644 --- a/doc/interface/unit_operations/cstr.rst +++ b/doc/interface/unit_operations/cstr.rst @@ -12,129 +12,129 @@ For information on model equations, refer to :ref:`cstr_model`. ``UNIT_TYPE`` Specifies the type of unit operation model - + ================ ================================ ============= **Type:** string **Range:** :math:`\texttt{CSTR}` **Length:** 1 ================ ================================ ============= - + ``NCOMP`` Number of chemical components in the chromatographic medium - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``USE_ANALYTIC_JACOBIAN`` Determines whether analytically computed Jacobian matrix (faster) is used (value is 1) instead of Jacobians generated by algorithmic differentiation (slower, value is 0) - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= - + ``ADSORPTION_MODEL`` Specifies the type of binding model of each particle type - + ================ ========================================== ===================================== **Type:** string **Range:** See Section :ref:`FFAdsorption` **Length:** :math:`\texttt{NPARTYPE}` ================ ========================================== ===================================== - + ``NBOUND`` Number of bound states for each component in each particle type in type-major ordering (optional, defaults to all 0) - + ============= ========================= =========================================================== **Type:** int **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NPARTYPE} \cdot \texttt{NCOMP}` ============= ========================= =========================================================== - + ``REACTION_MODEL`` Specifies the type of reaction model of the bulk volume. The model is configured in the subgroup :math:`\texttt{reaction_bulk}`. - + ================ ======================================== ============= **Type:** string **Range:** See Section :ref:`FFReaction` **Length:** 1 ================ ======================================== ============= - + ``REACTION_MODEL_PARTICLES`` Specifies the type of reaction model of each particle type (or of all particle types if length is 1). The model is configured in the subgroup :math:`\texttt{reaction_particle}`, or :math:`\texttt{reaction_particle_XXX}` in case of disabled multiplexing. - + ================ ======================================== ========================================= **Type:** string **Range:** See Section :ref:`FFReaction` **Length:** :math:`1 / \texttt{NPARTYPE}` ================ ======================================== ========================================= - + ``REACTION_MODEL_PARTICLES_MULTIPLEX`` Multiplexing mode of :math:`\texttt{REACTION_MODEL_PARTICLES}`. If set to 0, each particle type has a different reaction model and the length of :math:`\texttt{REACTION_MODEL_PARTICLES}` is :math:`\texttt{NPARTYPE}`. If set to 1, all particle types share the same reaction model and the length of :math:`\texttt{REACTION_MODEL_PARTICLES}` is 1. This field is optional and inferred from the length of :math:`\texttt{REACTION_MODEL_PARTICLES}` if left out. - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= - + ``INIT_C`` Initial concentrations for each component in the mobile phase **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}` - + ================ ========================= ================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NCOMP}` ================ ========================= ================================== - + ``INIT_VOLUME`` Initial tank volume **Unit:** :math:`\mathrm{m}^{3}` - + ================ ========================= ============= **Type:** double **Range:** :math:`\geq 0` **Length:** 1 ================ ========================= ============= - + ``INIT_Q`` Initial concentrations for each bound state of each component in the bead solid phase of each particle type in type-component-major ordering. This field is optional and defaults to all 0. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{SP}}^{-3}` - + ================ ========================= ======================================= **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NTOTALBND}` ================ ========================= ======================================= - + ``INIT_STATE`` Full state vector for initialization (optional, :math:`\texttt{INIT_C}`, :math:`\texttt{INIT_Q}`, and :math:`\texttt{INIT_VOLUME}` will be ignored; if length is :math:`2\texttt{NDOF}`, then the second half is used for time derivatives) **Unit:** :math:`various` - + ================ ============================= ==================================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NDOF} / 2\texttt{NDOF}` ================ ============================= ==================================================== - + ``POROSITY`` Porosity :math:`\varepsilon` (defaults to 1) - + ================ ======================== ============= **Type:** double **Range:** :math:`(0,1]` **Length:** 1 ================ ======================== ============= - + ``FLOWRATE_FILTER`` Flow rate of pure liquid without components (optional, defaults to :math:`\mathrm{m}^{3}\,\mathrm{s}^{-1}`) **Unit:** :math:`\mathrm{m}^{3}\,\mathrm{s}^{-1}` - + ================ ========================= ====================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`1 / \texttt{NSEC}` ================ ========================= ====================================== - + ``PAR_TYPE_VOLFRAC`` Volume fractions of the particle types, have to sum to 1 - + ================ ======================== ===================================== **Type:** double **Range:** :math:`[0,1]` **Length:** :math:`\texttt{NPARTYPE}` ================ ======================== ===================================== diff --git a/doc/interface/unit_operations/general_rate_model.rst b/doc/interface/unit_operations/general_rate_model.rst index 354718226..eed084c1e 100644 --- a/doc/interface/unit_operations/general_rate_model.rst +++ b/doc/interface/unit_operations/general_rate_model.rst @@ -12,7 +12,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. ``UNIT_TYPE`` Specifies the type of unit operation model - + ================ ============================================== ============= **Type:** string **Range:** :math:`\texttt{GENERAL_RATE_MODEL}` **Length:** 1 ================ ============================================== ============= @@ -20,7 +20,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. ``NCOMP`` Number of chemical components in the chromatographic medium - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= @@ -28,7 +28,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. ``ADSORPTION_MODEL`` Specifies the type of binding model of each particle type (or of all particle types if length is :math:`1`) - + ================ ============================== ========================================= **Type:** string **Range:** :ref:`FFAdsorption` **Length:** :math:`1 / \texttt{NPARTYPE}` ================ ============================== ========================================= @@ -36,7 +36,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. ``ADSORPTION_MODEL_MULTIPLEX`` Multiplexing mode of :math:`\texttt{ADSORPTION_MODEL}`. If set to :math:`0`, each particle type has a different binding model and the length of :math:`\texttt{ADSORPTION_MODEL}` is :math:`\texttt{NPARTYPE}`. If set to :math:`1`, all particle types share the same binding model and the length of :math:`\texttt{ADSORPTION_MODEL}` is :math:`1`. This field is optional and inferred from the length of :math:`\texttt{ADSORPTION_MODEL}` if left out. - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= @@ -44,7 +44,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. ``NBOUND`` Number of bound states for each component in each particle type in type-major ordering - + ============= ========================= =================================================================================== **Type:** int **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NCOMP}` / :math:`\texttt{NPARTYPE} \cdot \texttt{NCOMP}` ============= ========================= =================================================================================== @@ -52,7 +52,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. ``REACTION_MODEL_BULK`` Specifies the type of reaction model of the bulk volume. The model is configured in the subgroup :math:`\texttt{reaction_bulk}`. - + ================ ======================================== ============= **Type:** string **Range:** See Section :ref:`FFReaction` **Length:** 1 ================ ======================================== ============= @@ -60,7 +60,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. ``REACTION_MODEL_PARTICLES`` Specifies the type of reaction model of each particle type (or of all particle types if length is :math:`1`). The model is configured in the subgroup :math:`\texttt{reaction_particle}`, or :math:`\texttt{reaction_particle_XXX}` in case of disabled multiplexing. - + ================ ======================================== ========================================= **Type:** string **Range:** See Section :ref:`FFReaction` **Length:** :math:`1 / \texttt{NPARTYPE}` ================ ======================================== ========================================= @@ -68,7 +68,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. ``REACTION_MODEL_PARTICLES_MULTIPLEX`` Multiplexing mode of :math:`\texttt{REACTION_MODEL_PARTICLES}`. If set to :math:`0`, each particle type has a different reaction model and the length of :math:`\texttt{REACTION_MODEL_PARTICLES}` is :math:`\texttt{NPARTYPE}`. If set to :math:`1`, all particle types share the same reaction model and the length of :math:`\texttt{REACTION_MODEL_PARTICLES}` is :math:`1`. This field is optional and inferred from the length of :math:`\texttt{REACTION_MODEL_PARTICLES}` if left out. - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= @@ -78,7 +78,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Initial concentrations for each component in the bulk mobile phase **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}` - + ================ ========================= ================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NCOMP}` ================ ========================= ================================== @@ -88,7 +88,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Initial concentrations for each component in the bead liquid phase (optional, :math:`\texttt{INIT_C}` is used if left out). The length of this field can be :math:`\texttt{NCOMP}` (same values for each particle type) or :math:`\texttt{NPARTYPE} \cdot \texttt{NCOMP}` Values for each particle type can only be given when :math:`\texttt{ADSORPTION_MODEL_MULTIPLEX}` is :math:`0`. The ordering is type-major. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{MP}}^{-3}` - + ================ ========================= =========================================================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NCOMP} / \texttt{NPARTYPE} \cdot \texttt{NCOMP}` ================ ========================= =========================================================================== @@ -98,7 +98,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Initial concentrations for each bound state of each component in the bead solid phase. If :math:`\texttt{ADSORPTION_MODEL_MULTIPLEX}` is :math:`0`, values for each particle type are required in type-component-major ordering (length is :math:`\texttt{NTOTALBND}`). If :math:`\texttt{ADSORPTION_MODEL_MULTIPLEX}` is :math:`1`, values for one particle type are required in component-major ordering (length is :math:`\sum_{i = 0}^{\texttt{NCOMP} - 1} \texttt{NBND}_i`). **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{SP}}^{-3}` - + ================ ========================= **Type:** double **Range:** :math:`\geq 0` ================ ========================= @@ -108,7 +108,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Full state vector for initialization (optional, :math:`\texttt{INIT_C}`, :math:`\texttt{INIT_CP}`, and :math:`\texttt{INIT_Q}` will be ignored; if length is :math:`2\texttt{NDOF}`, then the second half is used for time derivatives) **Unit:** :math:`various` - + ================ ============================= ================================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NDOF} / 2\texttt{NDOF}` ================ ============================= ================================================== @@ -118,20 +118,20 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Axial dispersion coefficient **Unit:** :math:`\mathrm{m}_{\mathrm{IV}}^{2}\,\mathrm{s}^{-1}` - + ================ ========================= ========================================================= **Type:** double **Range:** :math:`\geq 0` **Length:** see :math:`\texttt{COL_DISPERSION_MULTIPLEX}` ================ ========================= ========================================================= ``COL_DISPERSION_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{COL_DISPERSION}`. Determines whether :math:`\texttt{COL_DISPERSION}` is treated as component- and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{COL_DISPERSION}`. Valid modes are: + Multiplexing mode of :math:`\texttt{COL_DISPERSION}`. Determines whether :math:`\texttt{COL_DISPERSION}` is treated as component- and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{COL_DISPERSION}`. Valid modes are: + + 0. Component-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`1` + 1. Component-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP}` + 2. Component-independent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NSEC}` + 3. Component-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - 0. Component-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`1` - 1. Component-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP}` - 2. Component-independent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NSEC}` - 3. Component-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 3 \}` **Length:** 1 ============= =================================== ============= @@ -141,7 +141,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Column length **Unit:** :math:`\mathrm{m}` - + ================ ====================== ============= **Type:** double **Range:** :math:`> 0` **Length:** 1 ================ ====================== ============= @@ -149,7 +149,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. ``COL_POROSITY`` Column porosity - + ================ ======================== ============= **Type:** double **Range:** :math:`(0,1]` **Length:** 1 ================ ======================== ============= @@ -159,20 +159,20 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Film diffusion coefficients for each component of each particle type **Unit:** :math:`\mathrm{m}\,\mathrm{s}^{-1}` - + ================ ========================= ========================================================= **Type:** double **Range:** :math:`\geq 0` **Length:** see :math:`\texttt{FILM_DIFFUSION_MULTIPLEX}` ================ ========================= ========================================================= ``FILM_DIFFUSION_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{FILM_DIFFUSION}`. Determines whether :math:`\texttt{FILM_DIFFUSION}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{FILM_DIFFUSION}`. Valid modes are: + Multiplexing mode of :math:`\texttt{FILM_DIFFUSION}`. Determines whether :math:`\texttt{FILM_DIFFUSION}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{FILM_DIFFUSION}`. Valid modes are: 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP}` - 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major - 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major - + 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major + 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major + 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major + ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 3 \}` **Length:** 1 ============= =================================== ============= @@ -180,7 +180,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. ``PAR_POROSITY`` Particle porosity of all particle types or for each particle type - + ================ ======================== ========================================= **Type:** double **Range:** :math:`(0,1]` **Length:** :math:`1 / \texttt{NPARTYPE}` ================ ======================== ========================================= @@ -190,7 +190,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Particle radius of all particle types or for each particle type **Unit:** :math:`\mathrm{m}` - + ================ ===================== ========================================= **Type:** double **Range:** :math:`>0` **Length:** :math:`1 / \texttt{NPARTYPE}` ================ ===================== ========================================= @@ -200,7 +200,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Particle core radius of all particle types or for each particle type (optional, defaults to :math:`\mathrm{m}`) **Unit:** :math:`\mathrm{m}` - + ================ =========================================== ========================================= **Type:** double **Range:** :math:`[0, \texttt{PAR_RADIUS})` **Length:** :math:`1 / \texttt{NPARTYPE}` ================ =========================================== ========================================= @@ -209,19 +209,19 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Pore accessibility factor of each component in each particle type (optional, defaults to :math:`1`). Note: Should not be used in combination with any binding model! - + ================ ========================= ============================================================= **Type:** double **Range:** :math:`(0, 1]` **Length:** see :math:`\texttt{PORE_ACCESSIBILITY_MULTIPLEX}` ================ ========================= ============================================================= ``PORE_ACCESSIBILITY_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{PORE_ACCESSIBILITY}`. Determines whether :math:`\texttt{PORE_ACCESSIBILITY}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PORE_ACCESSIBILITY}`. Valid modes are: - 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP}` - 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major - 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major - + Multiplexing mode of :math:`\texttt{PORE_ACCESSIBILITY}`. Determines whether :math:`\texttt{PORE_ACCESSIBILITY}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PORE_ACCESSIBILITY}`. Valid modes are: + 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP}` + 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major + 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major + 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major + ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 3 \}` **Length:** 1 ============= =================================== ============= @@ -231,21 +231,21 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Effective particle diffusion coefficients of each component in each particle type **Unit:** :math:`\mathrm{m}_{\mathrm{MP}}^{2}\,\mathrm{s}^{-1}` - + ================ ========================= ======================================================== **Type:** double **Range:** :math:`\geq 0` **Length:** see :math:`\texttt{PAR_DIFFUSION_MULTIPLEX}` ================ ========================= ======================================================== ``PAR_DIFFUSION_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{PAR_DIFFUSION}`. Determines whether :math:`\texttt{PAR_DIFFUSION}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PAR_DIFFUSION}`. Valid modes are: + Multiplexing mode of :math:`\texttt{PAR_DIFFUSION}`. Determines whether :math:`\texttt{PAR_DIFFUSION}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PAR_DIFFUSION}`. Valid modes are: + + 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP}` + 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major + 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major + 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major - 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP}` - 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major - 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{PAR_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major - ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 3 \}` **Length:** 1 ============= =================================== ============= @@ -259,15 +259,15 @@ For information on model equations, refer to :ref:`general_rate_model_model`. ================ ========================= ============================================================ **Type:** double **Range:** :math:`\geq 0` **Length:** see :math:`\texttt{PAR_SURFDIFFUSION_MULTIPLEX}` ================ ========================= ============================================================ - + ``PAR_SURFDIFFUSION_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{PAR_SURFDIFFUSION}`. Determines whether :math:`\texttt{PAR_SURFDIFFUSION}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PAR_SURFDIFFUSION}`. Valid modes are: + Multiplexing mode of :math:`\texttt{PAR_SURFDIFFUSION}`. Determines whether :math:`\texttt{PAR_SURFDIFFUSION}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PAR_SURFDIFFUSION}`. Valid modes are: + + 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NBND}`; ordering is component-major + 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NBND} \cdot \texttt{NSEC}`; ordering is section-component-major + 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NTOTALBND}`; ordering is type-component-major + 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NTOTALBND} \cdot \texttt{NSEC}`; ordering is section-type-component-major - 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NBND}`; ordering is component-major - 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NBND} \cdot \texttt{NSEC}`; ordering is section-component-major - 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NTOTALBND}`; ordering is type-component-major - 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{PAR_SURFDIFFUSION}` is :math:`\texttt{NTOTALBND} \cdot \texttt{NSEC}`; ordering is section-type-component-major - ``PAR_SURFDIFFUSION_MULTIPLEX`` ============= ==================================== ============= **Type:** int **Range:** :math:`\{ 0, \dots, 3 \}` **Length:** 1 @@ -284,7 +284,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Optional: If left out, no parameter dependence is assumed and the original surface diffusion coefficients are used unmodified. - + ================ ========================================= **Type:** string **Length:** :math:`1 / \texttt{NPARTYPE}` ================ ========================================= @@ -294,7 +294,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Factor :math:`\texttt{p1}` in exponential law particle surface diffusion relation :math:`D_{s, i, m} = \tilde{D}_{s, i, m} p_{1, i, m} exp \left(p_{2, i, m} c_{0}^{p} \right)` where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient. Only required if :math:`\texttt{PAR_SURFDIFFUSION_DEP}` is :math:`\texttt{LIQUID_SALT_EXPONENTIAL}`. - + ================ ========================= =================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NBOUND}` ================ ========================= =================================== @@ -304,7 +304,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Factor :math:`\texttt{p2}` in exponential law particle surface diffusion relation :math:`D_{s, i, m} = \tilde{D}_{s, i, m} p_{1, i, m} exp \left(p_{2, i, m} c_{0}^{p} \right)` where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient. Only required if :math:`\texttt{PAR_SURFDIFFUSION_DEP}` is :math:`\texttt{LIQUID_SALT_EXPONENTIAL}`. - + ================ ============================= =================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NBOUND}` ================ ============================= =================================== @@ -314,7 +314,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Factor :math:`\texttt{p1}` in power law particle surface diffusion relation :math:`D_{s, i, m} = \tilde{D}_{s, i, m} p_{1, i, m} \left( c_{0}^{p} \right)^{p_{2, i, m}}` where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient. Only required if :math:`\texttt{PAR_SURFDIFFUSION_DEP}` is :math:`\texttt{LIQUID_SALT_POWER}`. - + ================ ========================= =================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NBOUND}` ================ ========================= =================================== @@ -324,7 +324,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Fjactor :math:`\texttt{p2}` in power law particle surface diffusion relation :math:`D_{s, i, m} = \tilde{D}_{s, i, m} p_{1, i, m} \left( c_{0}^{p} \right)^{p_{2, i, m}}` where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient. Only required if :math:`\texttt{PAR_SURFDIFFUSION_DEP}` is :math:`\texttt{LIQUID_SALT_POWER}`. - + ================ ============================= =================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NBOUND}` ================ ============================= =================================== @@ -333,10 +333,10 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Factor :math:`\texttt{p1}` in colloidal affinity law particle surface diffusion relation :math:`D_{s, i, m} = \tilde{D}_{s, i, m} \left[ p_{4, i, m} \left( k_{i, m} \left( c_{0}^{p} \right) \right)^{p_{5, i, m}} p_{6, i, m} exp \left( p_{7, i, m} k_{i, m} \left( c_{0}^{p} \right) \right) \right]` - where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient and + where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient and :math:`k_{i, m} \left( c_{0}^{p} \right) = p_{1, i, m}\left( c_{0}^{p} \right)^{p_{2, i, m}} + p_{3, i, m}`. Only required if :math:`\texttt{PAR_SURFDIFFUSION_DEP}` is :math:`\texttt{LIQUID_SALT_COLLOIDAL_AFFINITY}`. - + ================ ============================= =================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NBOUND}` ================ ============================= =================================== @@ -345,10 +345,10 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Factor :math:`\texttt{p2}` in colloidal affinity law particle surface diffusion relation :math:`D_{s, i, m} = \tilde{D}_{s, i, m} \left[ p_{4, i, m} \left( k_{i, m} \left( c_{0}^{p} \right) \right)^{p_{5, i, m}} p_{6, i, m} exp \left( p_{7, i, m} k_{i, m} \left( c_{0}^{p} \right) \right) \right]` - where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient and + where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient and :math:`k_{i, m} \left( c_{0}^{p} \right) = p_{1, i, m}\left( c_{0}^{p} \right)^{p_{2, i, m}} + p_{3, i, m}`. Only required if :math:`\texttt{PAR_SURFDIFFUSION_DEP}` is :math:`\texttt{LIQUID_SALT_COLLOIDAL_AFFINITY}`. - + ================ ============================= =================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NBOUND}` ================ ============================= =================================== @@ -357,10 +357,10 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Factor :math:`\texttt{p3}` in colloidal affinity law particle surface diffusion relation :math:`D_{s, i, m} = \tilde{D}_{s, i, m} \left[ p_{4, i, m} \left( k_{i, m} \left( c_{0}^{p} \right) \right)^{p_{5, i, m}} p_{6, i, m} exp \left( p_{7, i, m} k_{i, m} \left( c_{0}^{p} \right) \right) \right]` - where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient and + where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient and :math:`k_{i, m} \left( c_{0}^{p} \right) = p_{1, i, m}\left( c_{0}^{p} \right)^{p_{2, i, m}} + p_{3, i, m}`. Only required if :math:`\texttt{PAR_SURFDIFFUSION_DEP}` is :math:`\texttt{LIQUID_SALT_COLLOIDAL_AFFINITY}`. - + ================ ============================= =================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NBOUND}` ================ ============================= =================================== @@ -369,10 +369,10 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Factor :math:`\texttt{p4}` in colloidal affinity law particle surface diffusion relation :math:`D_{s, i, m} = \tilde{D}_{s, i, m} \left[ p_{4, i, m} \left( k_{i, m} \left( c_{0}^{p} \right) \right)^{p_{5, i, m}} p_{6, i, m} exp \left( p_{7, i, m} k_{i, m} \left( c_{0}^{p} \right) \right) \right]` - where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient and + where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient and :math:`k_{i, m} \left( c_{0}^{p} \right) = p_{1, i, m}\left( c_{0}^{p} \right)^{p_{2, i, m}} + p_{3, i, m}`. Only required if :math:`\texttt{PAR_SURFDIFFUSION_DEP}` is :math:`\texttt{LIQUID_SALT_COLLOIDAL_AFFINITY}`. - + ================ ============================= =================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NBOUND}` ================ ============================= =================================== @@ -381,20 +381,20 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Factor :math:`\texttt{p5}` in colloidal affinity law particle surface diffusion relation :math:`D_{s, i, m} = \tilde{D}_{s, i, m} \left[ p_{4, i, m} \left( k_{i, m} \left( c_{0}^{p} \right) \right)^{p_{5, i, m}} p_{6, i, m} exp \left( p_{7, i, m} k_{i, m} \left( c_{0}^{p} \right) \right) \right]` - where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient and + where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient and :math:`k_{i, m} \left( c_{0}^{p} \right) = p_{1, i, m}\left( c_{0}^{p} \right)^{p_{2, i, m}} + p_{3, i, m}`. Only required if :math:`\texttt{PAR_SURFDIFFUSION_DEP}` is :math:`\texttt{LIQUID_SALT_COLLOIDAL_AFFINITY}`. - + ================ ============================= =================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NBOUND}` ================ ============================= =================================== ``PAR_SURFDIFFUSION_EXPFACTOR`` :math:`D_{s, i, m} = \tilde{D}_{s, i, m} \left[ p_{4, i, m} \left( k_{i, m} \left( c_{0}^{p} \right) \right)^{p_{5, i, m}} p_{6, i, m} exp \left( p_{7, i, m} k_{i, m} \left( c_{0}^{p} \right) \right) \right]` - where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient and + where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient and :math:`k_{i, m} \left( c_{0}^{p} \right) = p_{1, i, m}\left( c_{0}^{p} \right)^{p_{2, i, m}} + p_{3, i, m}`. Only required if :math:`\texttt{PAR_SURFDIFFUSION_DEP}` is :math:`\texttt{LIQUID_SALT_COLLOIDAL_AFFINITY}`. - + ================ ============================= =================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NBOUND}` ================ ============================= =================================== @@ -403,10 +403,10 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Factor :math:`\texttt{p5}` in colloidal affinity law particle surface diffusion relation :math:`D_{s, i, m} = \tilde{D}_{s, i, m} \left[ p_{4, i, m} \left( k_{i, m} \left( c_{0}^{p} \right) \right)^{p_{5, i, m}} p_{6, i, m} exp \left( p_{7, i, m} k_{i, m} \left( c_{0}^{p} \right) \right) \right]` - where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient and + where :math:`\tilde{D}_{s, i, m}` is the original surface diffusion coefficient and :math:`k_{i, m} \left( c_{0}^{p} \right) = p_{1, i, m}\left( c_{0}^{p} \right)^{p_{2, i, m}} + p_{3, i, m}`. Only required if :math:`\texttt{PAR_SURFDIFFUSION_DEP}` is :math:`\texttt{LIQUID_SALT_COLLOIDAL_AFFINITY}`. - + ================ ============================= =================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NBOUND}` ================ ============================= =================================== @@ -415,7 +415,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Interstitial velocity of the mobile phase (optional if :math:`\texttt{CROSS_SECTION_AREA}` is present, see Section :ref:`MUOPGRMflow`) **Unit:** :math:`\mathrm{m}\,\mathrm{s}^{-1}` - + ================ ============================= ======================================= **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`1 / \texttt{NSEC}` ================ ============================= ======================================= @@ -424,7 +424,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. Cross section area of the column (optional if :math:`\texttt{VELOCITY}` is present, see Section :ref:`MUOPGRMflow`) **Unit:** :math:`\mathrm{m}^{2}` - + ================ ===================== ============= **Type:** double **Range:** :math:`>0` **Length:** 1 ================ ===================== ============= @@ -432,7 +432,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. ``NPARTYPE`` Number of particle types. Optional, inferred from the length of :math:`\texttt{NPAR}` or :math:`\texttt{NBOUND}` if left out. - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= @@ -440,7 +440,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. ``PAR_GEOM`` Specifies the particle geometry for all or each particle type. Valid values are :math:`\texttt{SPHERE}`, :math:`\texttt{CYLINDER}`, :math:`\texttt{SLAB}`. Optional, defaults to :math:`\texttt{SPHERE}`. - + ================ ================================================= **Type:** string **Length:** :math:`1` / :math:`\texttt{NPARTYPE}` ================ ================================================= @@ -448,7 +448,7 @@ For information on model equations, refer to :ref:`general_rate_model_model`. ``PAR_TYPE_VOLFRAC`` Volume fractions of the particle types. The volume fractions can be set for all axial cells together or for each individual axial cell. For each cell, the volume fractions have to sum to :math:`1`. In case of a spatially inhomogeneous setting, the data is expected in cell-major ordering and the :math:`\texttt{SENS_SECTION}` field is used for indexing the axial cell when specifying parameter sensitivities. This field is optional in case of only one particle type. - + ================ ======================== ============================================================================= **Type:** double **Range:** :math:`[0,1]` **Length:** :math:`\texttt{NPARTYPE} / \texttt{NCOL} \cdot \texttt{NPARTYPE}` ================ ======================== ============================================================================= @@ -460,7 +460,7 @@ Group /input/model/unit_XXX/discretization - UNIT_TYPE - GENERAL_RATE_MODEL ``USE_ANALYTIC_JACOBIAN`` Determines whether analytically computed Jacobian matrix (faster) is used (value is 1) instead of Jacobians generated by algorithmic differentiation (slower, value is 0) - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= @@ -468,7 +468,7 @@ Group /input/model/unit_XXX/discretization - UNIT_TYPE - GENERAL_RATE_MODEL ``PAR_DISC_TYPE`` Specifies the discretization scheme inside the particles for all or each particle type. Valid values are :math:`\texttt{EQUIDISTANT_PAR}`, :math:`\texttt{EQUIVOLUME_PAR}`, and :math:`\texttt{USER_DEFINED_PAR}`. - + ================ ================================================= **Type:** string **Length:** :math:`1` / :math:`\texttt{NPARTYPE}` ================ ================================================= @@ -476,7 +476,7 @@ Group /input/model/unit_XXX/discretization - UNIT_TYPE - GENERAL_RATE_MODEL ``PAR_DISC_VECTOR`` Node coordinates for the cell boundaries (ignored if :math:`\texttt{PAR_DISC_TYPE} \neq \texttt{USER_DEFINED_PAR}`). The coordinates are relative and have to include the endpoints :math:`0` and :math:`1`. They are later linearly mapped to the true radial range :math:`[r_{c,j}, r_{p,j}]`. The coordinates for each particle type are appended to one long vector in type-major ordering. - + ================ ======================== ================================================ **Type:** double **Range:** :math:`[0,1]` **Length:** :math:`\sum_i (\texttt{NPAR}_i + 1)` ================ ======================== ================================================ @@ -504,7 +504,7 @@ Finite Volumes (Default) ``NCOL`` Number of axial column discretization points - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= @@ -512,7 +512,7 @@ Finite Volumes (Default) ``NPAR`` Number of particle (radial) discretization points for each particle type - + ============= ========================= ================================================= **Type:** int **Range:** :math:`\geq 1` **Length:** :math:`1` / :math:`\texttt{NPARTYPE}` ============= ========================= ================================================= @@ -520,7 +520,7 @@ Finite Volumes (Default) ``PAR_BOUNDARY_ORDER`` Order of accuracy of outer particle boundary condition. Optional, defaults to :math:`2`. - + ============= ============================ ============= **Type:** int **Range:** :math:`\{ 1,2 \}` **Length:** 1 ============= ============================ ============= @@ -528,7 +528,7 @@ Finite Volumes (Default) ``RECONSTRUCTION`` Type of reconstruction method for fluxes - + ================ ================================ ============= **Type:** string **Range:** :math:`\texttt{WENO}` **Length:** 1 ================ ================================ ============= @@ -536,7 +536,7 @@ Finite Volumes (Default) ``GS_TYPE`` Type of Gram-Schmidt orthogonalization, see IDAS guide Section 4.5.7.3, p. 41f. A value of :math:`0` enables classical Gram-Schmidt, a value of 1 uses modified Gram-Schmidt. - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= @@ -544,7 +544,7 @@ Finite Volumes (Default) ``MAX_KRYLOV`` Defines the size of the Krylov subspace in the iterative linear GMRES solver (0: :math:`\texttt{MAX_KRYLOV} = \texttt{NCOL} \cdot \texttt{NCOMP} \cdot \texttt{NPARTYPE}`) - + ============= ============================================================================================ ============= **Type:** int **Range:** :math:`\{0, \dots, \texttt{NCOL} \cdot \texttt{NCOMP} \cdot \texttt{NPARTYPE} \}` **Length:** 1 ============= ============================================================================================ ============= @@ -552,7 +552,7 @@ Finite Volumes (Default) ``MAX_RESTARTS`` Maximum number of restarts in the GMRES algorithm. If lack of memory is not an issue, better use a larger Krylov space than restarts. - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 0` **Length:** 1 ============= ========================= ============= @@ -560,7 +560,7 @@ Finite Volumes (Default) ``SCHUR_SAFETY`` Schur safety factor; Influences the tradeoff between linear iterations and nonlinear error control; see IDAS guide Section~2.1 and 5. - + ================ ========================= ============= **Type:** double **Range:** :math:`\geq 0` **Length:** 1 ================ ========================= ============= @@ -568,7 +568,7 @@ Finite Volumes (Default) ``FIX_ZERO_SURFACE_DIFFUSION`` Determines whether the surface diffusion parameters :math:`\texttt{PAR_SURFDIFFUSION}` are fixed if the parameters are zero. If the parameters are fixed to zero (:math:`\texttt{FIX_ZERO_SURFACE_DIFFUSION} = 1`, :math:`\texttt{PAR_SURFDIFFUSION} = 0`), the parameters must not become non-zero during this or subsequent simulation runs. The internal data structures are optimized for a more efficient simulation. This field is optional and defaults to :math:`0` (optimization disabled in favor of flexibility). - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= @@ -581,7 +581,7 @@ Discontinuous Galerkin ``POLYDEG`` DG polynomial degree. Optional, defaults to 4 and :math:`N_d \in \{3, 4, 5\}` is recommended. The total number of axial discrete points is given by (``POLYDEG`` + 1 ) * ``NELEM`` - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= @@ -589,7 +589,7 @@ Discontinuous Galerkin ``NELEM`` Number of axial column discretization DG cells\elements. The total number of axial discrete points is given by (``POLYDEG`` + 1 ) * ``NELEM`` - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= @@ -597,7 +597,7 @@ Discontinuous Galerkin ``NCOL`` Number of axial discrete points. Optional and ignored if ``NELEM`` is defined. Otherwise, used to calculate ``NELEM`` = :math:`\lfloor` ``NCOL`` / (``POLYDEG`` + 1 ) :math:`\rfloor` - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= @@ -605,7 +605,7 @@ Discontinuous Galerkin ``EXACT_INTEGRATION`` Specifies the DG integration variant. Optional, defaults to 0 - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= @@ -613,7 +613,7 @@ Discontinuous Galerkin ``PAR_POLYDEG`` DG particle (radial) polynomial degree. Optional, defaults to 3. The total number of particle (radial) discrete points is given by (``PARPOLYDEG`` + 1 ) * ``PAR_NELEM``. - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= @@ -621,10 +621,10 @@ Discontinuous Galerkin ``PAR_NELEM`` Number of particle (radial) discretization DG elements for each particle type. For the particle discretization, it is usually most performant to fix ``PAR_NELEM`` = 1 and to increase the polynomial degree for more accuracy. - + ============= ========================= ================================================= **Type:** int **Range:** :math:`\geq 1` **Length:** :math:`1` / :math:`\texttt{NPARTYPE}` ============= ========================= ================================================= - + When using the DG method for the GRM, we recommend specifying ``USE_MODIFIED_NEWTON = 1`` in :ref:`FFSolverTime`, i.e. to use the modified Newton method to solve the linear system within the time integrator. For further discretization parameters, see also :ref:`non_consistency_solver_parameters`. diff --git a/doc/interface/unit_operations/index.rst b/doc/interface/unit_operations/index.rst index 1d80a6b36..807654a89 100644 --- a/doc/interface/unit_operations/index.rst +++ b/doc/interface/unit_operations/index.rst @@ -8,11 +8,10 @@ Unit Operations inlet outlet - general_rate_model + general_rate_model lumped_rate_model_with_pores lumped_rate_model_without_pores 2d_general_rate_model cstr radial_flow_models multi_channel_transport_model - diff --git a/doc/interface/unit_operations/inlet.rst b/doc/interface/unit_operations/inlet.rst index fba0fc476..be15a3083 100644 --- a/doc/interface/unit_operations/inlet.rst +++ b/doc/interface/unit_operations/inlet.rst @@ -12,23 +12,23 @@ For information on model equations, refer to :ref:`inlet_model`. ``UNIT_TYPE`` Specifies the type of unit operation model - + ================ ================================= ============= **Type:** string **Range:** :math:`\texttt{INLET}` **Length:** 1 ================ ================================= ============= - + ``NCOMP`` Number of chemical components in the chromatographic medium - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``INLET_TYPE`` Specifies the type of inlet profile - + ================ ================================================ ============= **Type:** string **Range:** :math:`\texttt{PIECEWISE_CUBIC_POLY}` **Length:** 1 ================ ================================================ ============= @@ -41,39 +41,37 @@ Group /input/model/unit_XXX/sec_XXX Constant coefficients for inlet concentrations **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}` - + ================ ============================= ================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NCOMP}` ================ ============================= ================================== - + ``LIN_COEFF`` Linear coefficients for inlet concentrations **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-1}` - + ================ ============================= ================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NCOMP}` ================ ============================= ================================== - + ``QUAD_COEFF`` Quadratic coefficients for inlet concentrations **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-2}` - + ================ ============================= ================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NCOMP}` ================ ============================= ================================== - + ``CUBE_COEFF`` Cubic coefficients for inlet concentrations **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}\,\mathrm{s}^{-3}` - + ================ ============================= ================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NCOMP}` ================ ============================= ================================== - - diff --git a/doc/interface/unit_operations/lumped_rate_model_with_pores.rst b/doc/interface/unit_operations/lumped_rate_model_with_pores.rst index ace7b4c2b..386bbab85 100644 --- a/doc/interface/unit_operations/lumped_rate_model_with_pores.rst +++ b/doc/interface/unit_operations/lumped_rate_model_with_pores.rst @@ -11,237 +11,237 @@ For information on model equations, refer to :ref:`lumped_rate_model_with_pores_ ``UNIT_TYPE`` Specifies the type of unit operation model - + ================ ======================================================== ============= **Type:** string **Range:** :math:`\texttt{LUMPED_RATE_MODEL_WITH_PORES}` **Length:** 1 ================ ======================================================== ============= - + ``NCOMP`` Number of chemical components in the chromatographic medium - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``ADSORPTION_MODEL`` Specifies the type of binding model of each particle type (or of all particle types if length is :math:`1`) - + ================ ========================================== ========================================= **Type:** string **Range:** See Section :ref:`FFAdsorption` **Length:** :math:`1 / \texttt{NPARTYPE}` ================ ========================================== ========================================= - + ``ADSORPTION_MODEL_MULTIPLEX`` Multiplexing mode of :math:`\texttt{ADSORPTION_MODEL}`. If set to :math:`0`, each particle type has a different binding model and the length of :math:`\texttt{ADSORPTION_MODEL}` is :math:`\texttt{NPARTYPE}`. If set to :math:`1`, all particle types share the same binding model and the length of :math:`\texttt{ADSORPTION_MODEL}` is :math:`1`. This field is optional and inferred from the length of :math:`\texttt{ADSORPTION_MODEL}` if left out. - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= - + ``NBOUND`` Number of bound states for each component in each particle type in type-major ordering - + ============= ========================= =========================================================================== **Type:** int **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NCOMP} / \texttt{NPARTYPE} \cdot \texttt{NCOMP}` ============= ========================= =========================================================================== - + ``REACTION_MODEL_BULK`` Specifies the type of reaction model of the bulk volume. The model is configured in the subgroup :math:`\texttt{reaction_bulk}`. - + ================ ================================ ============= **Type:** string **Range:** See :ref:`FFReaction` **Length:** 1 ================ ================================ ============= - + ``REACTION_MODEL_PARTICLES`` Specifies the type of reaction model of each particle type (or of all particle types if length is :math:`1`). The model is configured in the subgroup :math:`\texttt{reaction_particle}`, or :math:`\texttt{reaction_particle_XXX}` in case of disabled multiplexing. - + ================ ======================================== =================================== **Type:** string **Range:** See Section :ref:`FFReaction` **Length:** :math:`1` / :math:`\texttt{NPARTYPE}` ================ ======================================== =================================== - + ``REACTION_MODEL_PARTICLES_MULTIPLEX`` Multiplexing mode of :math:`\texttt{REACTION_MODEL_PARTICLES}`. If set to :math:`0`, each particle type has a different reaction model and the length of :math:`\texttt{REACTION_MODEL_PARTICLES}` is :math:`\texttt{NPARTYPE}`. If set to :math:`1`, all particle types share the same reaction model and the length of :math:`\texttt{REACTION_MODEL_PARTICLES}` is :math:`1`. This field is optional and inferred from the length of :math:`\texttt{REACTION_MODEL_PARTICLES}` if left out. - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= - + ``INIT_C`` Initial concentrations for each component in the bulk mobile phase **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}` - + ================ ========================= ================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NCOMP}` ================ ========================= ================================== - + ``INIT_CP`` Initial concentrations for each component in the bead liquid phase (optional, :math:`\texttt{INIT_C}` is used if left out). The length of this field can be :math:`\texttt{NCOMP}` (same values for each particle type) or :math:`\texttt{NPARTYPE} \cdot \texttt{NCOMP}` Values for each particle type can only be given when :math:`\texttt{ADSORPTION_MODEL_MULTIPLEX}` is :math:`0`. The ordering is type-major. **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{MP}}^{-3}` - + ================ ========================= =========================================================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NCOMP} / \texttt{NPARTYPE} \cdot \texttt{NCOMP}` ================ ========================= =========================================================================== - + ``INIT_Q`` Initial concentrations for each bound state of each component in the bead solid phase. If :math:`\texttt{ADSORPTION_MODEL_MULTIPLEX}` is :math:`0`, values for each particle type are required in type-component-major ordering (length is :math:`\texttt{NTOTALBND}`). If :math:`\texttt{ADSORPTION_MODEL_MULTIPLEX}` is :math:`1`, values for one particle type are required in component-major ordering (length is :math:`\sum_{i = 0}^{\texttt{NCOMP} - 1} \texttt{NBND}_i`). **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{SP}}^{-3}` - + ================ ========================= **Type:** double **Range:** :math:`\geq 0` ================ ========================= - + ``INIT_STATE`` Full state vector for initialization (optional, :math:`\texttt{INIT_C}`, :math:`\texttt{INIT_CP}`, and :math:`\texttt{INIT_Q}` will be ignored; if length is :math:`2\texttt{NDOF}`, then the second half is used for time derivatives) **Unit:** :math:`various` - + ================ ============================= ================================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NDOF} / 2\texttt{NDOF}` ================ ============================= ================================================== - + ``COL_DISPERSION`` Axial dispersion coefficient **Unit:** :math:`\mathrm{m}_{\mathrm{IV}}^{2}\,\mathrm{s}^{-1}` - + ================ ========================= ========================================================= **Type:** double **Range:** :math:`\geq 0` **Length:** see :math:`\texttt{COL_DISPERSION_MULTIPLEX}` ================ ========================= ========================================================= - + ``COL_DISPERSION_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{COL_DISPERSION}`. Determines whether :math:`\texttt{COL_DISPERSION}` is treated as component- and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{COL_DISPERSION}`. Valid modes are: + Multiplexing mode of :math:`\texttt{COL_DISPERSION}`. Determines whether :math:`\texttt{COL_DISPERSION}` is treated as component- and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{COL_DISPERSION}`. Valid modes are: + + 0. Component-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`1` + 1. Component-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP}` + 2. Component-independent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NSEC}` + 3. Component-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - 0. Component-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`1` - 1. Component-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP}` - 2. Component-independent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NSEC}` - 3. Component-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 3 \}` **Length:** 1 ============= =================================== ============= - + ``COL_LENGTH`` Column length **Unit:** :math:`\mathrm{m}` - + ================ ====================== ============= **Type:** double **Range:** :math:`> 0` **Length:** 1 ================ ====================== ============= - + ``COL_POROSITY`` Column porosity - + ================ ======================== ============= **Type:** double **Range:** :math:`(0,1]` **Length:** 1 ================ ======================== ============= - + ``FILM_DIFFUSION`` Film diffusion coefficients for each component of each particle type **Unit:** :math:`\mathrm{m}\,\mathrm{s}^{-1}` - + ================ ========================= ========================================================= **Type:** double **Range:** :math:`\geq 0` **Length:** see :math:`\texttt{FILM_DIFFUSION_MULTIPLEX}` ================ ========================= ========================================================= - + ``FILM_DIFFUSION_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{FILM_DIFFUSION}`. Determines whether :math:`\texttt{FILM_DIFFUSION}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{FILM_DIFFUSION}`. Valid modes are: + Multiplexing mode of :math:`\texttt{FILM_DIFFUSION}`. Determines whether :math:`\texttt{FILM_DIFFUSION}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{FILM_DIFFUSION}`. Valid modes are: + + 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP}` + 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major + 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major + 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major - 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP}` - 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major - 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{FILM_DIFFUSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major - ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 3 \}` **Length:** 1 ============= =================================== ============= - + ``PAR_POROSITY`` Particle porosity of all particle types or for each particle type - + ================ ======================== ========================================= **Type:** double **Range:** :math:`(0,1]` **Length:** :math:`1 / \texttt{NPARTYPE}` ================ ======================== ========================================= - + ``PAR_RADIUS`` Particle radius of all particle types or for each particle type **Unit:** :math:`\mathrm{m}` - + ================ ===================== ========================================= **Type:** double **Range:** :math:`>0` **Length:** :math:`1 / \texttt{NPARTYPE}` ================ ===================== ========================================= - + ``PORE_ACCESSIBILITY`` Pore accessibility factor of each component in each particle type (optional, defaults to :math:`1`). Note: Should not be used in combination with any binding model! - + ================ ========================= ============================================================= **Type:** double **Range:** :math:`(0, 1]` **Length:** see :math:`\texttt{PORE_ACCESSIBILITY_MULTIPLEX}` ================ ========================= ============================================================= - + ``PORE_ACCESSIBILITY_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{PORE_ACCESSIBILITY}`. Determines whether :math:`\texttt{PORE_ACCESSIBILITY}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PORE_ACCESSIBILITY}`. Valid modes are: + Multiplexing mode of :math:`\texttt{PORE_ACCESSIBILITY}`. Determines whether :math:`\texttt{PORE_ACCESSIBILITY}` is treated as component-, type-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{PORE_ACCESSIBILITY}`. Valid modes are: + + 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP}` + 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major + 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major + 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major - 0. Component-dependent, type-independent, section-independent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP}` - 1. Component-dependent, type-independent, section-dependent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - 2. Component-dependent, type-dependent, section-independent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE}`; ordering is type-major - 3. Component-dependent, type-dependent, section-dependent; length of :math:`\texttt{PORE_ACCESSIBILITY}` is :math:`\texttt{NCOMP} \cdot \texttt{NPARTYPE} \cdot \texttt{NSEC}`; ordering is section-type-major - ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 3 \}` **Length:** 1 ============= =================================== ============= - + ``VELOCITY`` Interstitial velocity of the mobile phase (optional if :math:`\texttt{CROSS_SECTION_AREA}` is present, see Section :ref:`MUOPGRMflow`) **Unit:** :math:`\mathrm{m}\,\mathrm{s}^{-1}` - + ================ ============================= ===================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`1 / \texttt{NSEC}` ================ ============================= ===================================== - + ``CROSS_SECTION_AREA`` Cross section area of the column (optional if :math:`\texttt{VELOCITY}` is present, see Section :ref:`MUOPGRMflow`) **Unit:** :math:`\mathrm{m}^{2}` - + ================ ===================== ============= **Type:** double **Range:** :math:`>0` **Length:** 1 ================ ===================== ============= - + ``NPARTYPE`` Number of particle types. Optional, inferred from the length of :math:`\texttt{NBOUND}` if left out. - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``PAR_GEOM`` Specifies the particle geometry for all or each particle type. Valid values are :math:`\texttt{SPHERE}`, :math:`\texttt{CYLINDER}`, :math:`\texttt{SLAB}`. Optional, defaults to :math:`\texttt{SPHERE}`. - + ================ ================================================= **Type:** string **Length:** :math:`1` / :math:`\texttt{NPARTYPE}` ================ ================================================= - + ``PAR_TYPE_VOLFRAC`` Volume fractions of the particle types. The volume fractions can be set for all axial cells together or for each individual axial cell. For each cell, the volume fractions have to sum to :math:`1`. In case of a spatially inhomogeneous setting, the data is expected in cell-major ordering and the :math:`\texttt{SENS_SECTION}` field is used for indexing the axial cell when specifying parameter sensitivities. This field is optional in case of only one particle type. - + ================ ======================== ===================================================================================== **Type:** double **Range:** :math:`[0,1]` **Length:** :math:`\texttt{NPARTYPE}` / :math:`\texttt{NCOL} \cdot \texttt{NPARTYPE}` ================ ======================== ===================================================================================== @@ -269,47 +269,47 @@ Finite Volumes (Default) ``NCOL`` Number of axial column discretization points - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``RECONSTRUCTION`` Type of reconstruction method for fluxes - + ================ ================================ ============= **Type:** string **Range:** :math:`\texttt{WENO}` **Length:** 1 ================ ================================ ============= - + ``GS_TYPE`` Type of Gram-Schmidt orthogonalization, see IDAS guide Section~4.5.7.3, p.~41f. A value of :math:`0` enables classical Gram-Schmidt, a value of 1 uses modified Gram-Schmidt. - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= - + ``MAX_KRYLOV`` Defines the size of the Krylov subspace in the iterative linear GMRES solver (0: :math:`\texttt{MAX_KRYLOV} = \texttt{NCOL} \cdot \texttt{NCOMP} \cdot \texttt{NPARTYPE}`) - + ============= ============================================================================================ ============= **Type:** int **Range:** :math:`\{0, \dots, \texttt{NCOL} \cdot \texttt{NCOMP} \cdot \texttt{NPARTYPE} \}` **Length:** 1 ============= ============================================================================================ ============= - + ``MAX_RESTARTS`` Maximum number of restarts in the GMRES algorithm. If lack of memory is not an issue, better use a larger Krylov space than restarts. - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 0` **Length:** 1 ============= ========================= ============= - + ``SCHUR_SAFETY`` Schur safety factor; Influences the tradeoff between linear iterations and nonlinear error control; see IDAS guide Section~2.1 and 5. - + ================ ========================= ============= **Type:** double **Range:** :math:`\geq 0` **Length:** 1 ================ ========================= ============= @@ -323,7 +323,7 @@ Discontinuous Galerkin ``POLYDEG`` DG polynomial degree. Optional, defaults to 4 and :math:`N_d \in \{3, 4, 5\}` is recommended. The total number of axial discrete points is given by (``POLYDEG`` + 1 ) * ``NELEM`` - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= @@ -331,7 +331,7 @@ Discontinuous Galerkin ``NELEM`` Number of axial column discretization DG cells\elements. The total number of axial discrete points is given by (``POLYDEG`` + 1 ) * ``NELEM`` - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= @@ -339,7 +339,7 @@ Discontinuous Galerkin ``NCOL`` Number of axial discrete points. Optional and ignored if ``NELEM`` is defined. Otherwise, used to calculate ``NELEM`` = :math:`\lfloor` ``NCOL`` / (``POLYDEG`` + 1 ) :math:`\rfloor` - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= @@ -347,7 +347,7 @@ Discontinuous Galerkin ``EXACT_INTEGRATION`` Specifies the DG integration variant. Optional, defaults to 0 - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= diff --git a/doc/interface/unit_operations/lumped_rate_model_without_pores.rst b/doc/interface/unit_operations/lumped_rate_model_without_pores.rst index 5afc37821..eee6f6101 100644 --- a/doc/interface/unit_operations/lumped_rate_model_without_pores.rst +++ b/doc/interface/unit_operations/lumped_rate_model_without_pores.rst @@ -12,142 +12,142 @@ For information on model equations, refer to :ref:`lumped_rate_model_without_por ``UNIT_TYPE`` Specifies the type of unit operation model - + ================ =========================================================== ============= **Type:** string **Range:** :math:`\texttt{LUMPED_RATE_MODEL_WITHOUT_PORES}` **Length:** 1 ================ =========================================================== ============= - + ``NCOMP`` Number of chemical components in the chromatographic medium - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``ADSORPTION_MODEL`` Specifies the type of binding model - + ================ ========================================== ============= **Type:** string **Range:** See Section :ref:`FFAdsorption` **Length:** 1 ================ ========================================== ============= - + ``NBOUND`` Number of bound states for each component - + ============= ========================= ================================== **Type:** int **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NCOMP}` ============= ========================= ================================== - + ``REACTION_MODEL`` Specifies the type of reaction model of the combined bulk and particle volume. The model is configured in the subgroup :math:`\texttt{reaction}`. - + ================ ======================================== ============= **Type:** string **Range:** See Section :ref:`FFReaction` **Length:** 1 ================ ======================================== ============= - + ``INIT_C`` Initial concentrations for each component in the bulk mobile phase **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{IV}}^{-3}` - + ================ ========================= =================================== **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NCOMP}` ================ ========================= =================================== - + ``INIT_Q`` Initial concentrations for each bound state of each component in the bead solid phase in component-major ordering **Unit:** :math:`\mathrm{mol}\,\mathrm{m}_{\mathrm{SP}}^{-3}` - + ================ ========================= ======================================= **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NTOTALBND}` ================ ========================= ======================================= - + ``INIT_STATE`` Full state vector for initialization (optional, :math:`\texttt{INIT_C}` and :math:`\texttt{INIT_Q}` will be ignored; if length is :math:`2\texttt{NDOF}`, then the second half is used for time derivatives) **Unit:** :math:`various` - + ================ ============================= =================================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NDOF} / 2\texttt{NDOF}` ================ ============================= =================================================== - + ``COL_DISPERSION`` Axial dispersion coefficient **Unit:** :math:`\mathrm{m}_{\mathrm{IV}}^{2}\,\mathrm{s}^{-1}` - + ================ ========================= ========================================================= **Type:** double **Range:** :math:`\geq 0` **Length:** see :math:`\texttt{COL_DISPERSION_MULTIPLEX}` ================ ========================= ========================================================= - + ``COL_DISPERSION_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{COL_DISPERSION}`. Determines whether :math:`\texttt{COL_DISPERSION}` is treated as component- and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{COL_DISPERSION}`. Valid modes are: + Multiplexing mode of :math:`\texttt{COL_DISPERSION}`. Determines whether :math:`\texttt{COL_DISPERSION}` is treated as component- and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{COL_DISPERSION}`. Valid modes are: + + 0. Component-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`1` + 1. Component-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP}` + 2. Component-independent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NSEC}` + 3. Component-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - 0. Component-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`1` - 1. Component-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP}` - 2. Component-independent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NSEC}` - 3. Component-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 3 \}` **Length:** 1 ============= =================================== ============= - + ``COL_LENGTH`` Column length **Unit:** :math:`\mathrm{m}` - + ================ ====================== ============= **Type:** double **Range:** :math:`> 0` **Length:** 1 ================ ====================== ============= - + ``TOTAL_POROSITY`` Total porosity - + ================ ======================== ============= **Type:** double **Range:** :math:`[0,1]` **Length:** 1 ================ ======================== ============= - + ``VELOCITY`` Interstitial velocity of the mobile phase (optional if :math:`\texttt{CROSS_SECTION_AREA}` is present, see Section :ref:`MUOPGRMflow`) **Unit:** :math:`\mathrm{m}\,\mathrm{s}^{-1}` - + ================ ============================= ====================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`1 / \texttt{NSEC}` ================ ============================= ====================================== - + ``CROSS_SECTION_AREA`` Cross section area of the column (optional if :math:`\texttt{VELOCITY}` is present, see Section :ref:`MUOPGRMflow`) **Unit:** :math:`\mathrm{m}^{2}` - + ================ ===================== ============= **Type:** double **Range:** :math:`>0` **Length:** 1 ================ ===================== ============= - + Group /input/model/unit_XXX/discretization - UNIT_TYPE = LUMPED_RATE_MODEL_WITHOUT_PORES ---------------------------------------------------------------------------------------- - + ``USE_ANALYTIC_JACOBIAN`` Determines whether analytically computed Jacobian matrix (faster) is used (value is 1) instead of Jacobians generated by algorithmic differentiation (slower, value is 0) - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= @@ -175,19 +175,19 @@ Finite Volumes (Default) ``NCOL`` Number of axial column discretization points - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``RECONSTRUCTION`` Type of reconstruction method for fluxes only (only needs to be specified for FV) - + ================ ================================ ============= **Type:** string **Range:** :math:`\texttt{WENO}` **Length:** 1 ================ ================================ ============= - + For further discretization parameters, see also :ref:`flux_restruction_methods` (FV specific)), and :ref:`non_consistency_solver_parameters`. @@ -197,7 +197,7 @@ Discontinuous Galerkin ``POLYDEG`` DG polynomial degree. Optional, defaults to 4 and :math:`N_d \in \{3, 4, 5\}` is recommended. The total number of axial discrete points is given by (``POLYDEG`` + 1 ) * ``NELEM`` - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= @@ -205,7 +205,7 @@ Discontinuous Galerkin ``NELEM`` Number of axial column discretization DG cells\elements. The total number of axial discrete points is given by (``POLYDEG`` + 1 ) * ``NELEM`` - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= @@ -213,7 +213,7 @@ Discontinuous Galerkin ``NCOL`` Number of axial discrete points. Optional and ignored if ``NELEM`` is defined. Otherwise, used to calculate ``NELEM`` = :math:`\lfloor` ``NCOL`` / (``POLYDEG`` + 1 ) :math:`\rfloor` - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= @@ -221,9 +221,9 @@ Discontinuous Galerkin ``EXACT_INTEGRATION`` Specifies the DG integration variant. Optional, defaults to 0 - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= - + For further discretization parameters, see also :ref:`non_consistency_solver_parameters`. diff --git a/doc/interface/unit_operations/multi_channel_transport_model.rst b/doc/interface/unit_operations/multi_channel_transport_model.rst index 7d058fcdf..ea9702fa4 100644 --- a/doc/interface/unit_operations/multi_channel_transport_model.rst +++ b/doc/interface/unit_operations/multi_channel_transport_model.rst @@ -1,6 +1,6 @@ .. _multi_channel_transport_model_config: -Multichannel Transport model (MCT model) +Multichannel Transport model (MCT model) ======================================== Group /input/model/unit_XXX - UNIT_TYPE = MULTI_CHANNEL_TRANSPORT @@ -11,84 +11,84 @@ For information on model equations, refer to :ref:`multi_channel_transport_model ``UNIT_TYPE`` Specifies the type of unit operation model - + ================ =================================================== ============= **Type:** string **Range:** :math:`\texttt{MULTI_CHANNEL_TRANSPORT}` **Length:** 1 ================ =================================================== ============= - + ``NCOMP`` Number of chemical components in the chromatographic medium - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - + ``REACTION_MODEL_BULK`` Specifies the type of reaction model of the bulk volume. The model is configured in the subgroup :math:`\texttt{reaction_bulk}`. - + ================ ======================================== ============= **Type:** string **Range:** See Section :ref:`FFReaction` **Length:** 1 ================ ======================================== ============= - + ``INIT_C`` Initial concentrations for each component in all channels of the bulk mobile phase (length :math:`\texttt{NCOMP}`), or for each component in each channel (length :math:`\texttt{NCOMP} \cdot \texttt{NCHANNEL}`, ordering channel-major) **Unit:** :math:`\mathrm{mol}\,\mathrm{m}^{-3}` - + ================ ========================= ========================================================================= **Type:** double **Range:** :math:`\geq 0` **Length:** :math:`\texttt{NCOMP} / \texttt{NCOMP} \cdot \texttt{NCHANNEL}` ================ ========================= ========================================================================= - + ``INIT_STATE`` Full state vector for initialization (optional, :math:`\texttt{INIT_C}` will be ignored; if length is :math:`2\texttt{NDOF}`, then the second half is used for time derivatives) **Unit:** :math:`various` - + ================ ============================= ================================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`\texttt{NDOF} / 2\texttt{NDOF}` ================ ============================= ================================================== - + ``COL_DISPERSION`` Axial dispersion coefficient. In case of a spatially inhomogeneous setting, the :math:`\texttt{SENS_PARTYPE}` field is used for indexing the channel when specifying parameter sensitivities. **Unit:** :math:`\mathrm{m}^{2}\,\mathrm{s}^{-1}` - + ================ ========================= ========================================================= **Type:** double **Range:** :math:`\geq 0` **Length:** see :math:`\texttt{COL_DISPERSION_MULTIPLEX}` ================ ========================= ========================================================= - + ``COL_DISPERSION_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{COL_DISPERSION}`. Determines whether :math:`\texttt{COL_DISPERSION}` is treated as component-, channel-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{COL_DISPERSION}`. Valid modes are: - - 1. Component-independent, channel-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is 1 - 2. Component-independent, channel-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCHANNEL}` - 3. Component-dependent, channel-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP}` - 4. Component-dependent, channel-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NCHANNEL}`; ordering is channel-major - 5. Component-independent, channel-independent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NSEC}` - 6. Component-independent, channel-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCHANNEL} \cdot \texttt{NSEC}`; ordering is section-major - 7. Component-dependent, channel-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major - 8. Component-dependent, channel-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NCHANNEL} \cdot \texttt{NSEC}`; ordering is section-channel-major - + Multiplexing mode of :math:`\texttt{COL_DISPERSION}`. Determines whether :math:`\texttt{COL_DISPERSION}` is treated as component-, channel-, and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{COL_DISPERSION}`. Valid modes are: + + 1. Component-independent, channel-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is 1 + 2. Component-independent, channel-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCHANNEL}` + 3. Component-dependent, channel-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP}` + 4. Component-dependent, channel-dependent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NCHANNEL}`; ordering is channel-major + 5. Component-independent, channel-independent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NSEC}` + 6. Component-independent, channel-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCHANNEL} \cdot \texttt{NSEC}`; ordering is section-major + 7. Component-dependent, channel-independent, section-independent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NSEC}`; ordering is section-major + 8. Component-dependent, channel-dependent, section-dependent; length of :math:`\texttt{COL_DISPERSION}` is :math:`\texttt{NCOMP} \cdot \texttt{NCHANNEL} \cdot \texttt{NSEC}`; ordering is section-channel-major + ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 7 \}` **Length:** 1 ============= =================================== ============= - + ``COL_LENGTH`` Column length **Unit:** :math:`\mathrm{m}` - + ================ ====================== ============= **Type:** double **Range:** :math:`> 0` **Length:** 1 ================ ====================== ============= - + ``VELOCITY`` Velocity @@ -96,20 +96,20 @@ For information on model equations, refer to :ref:`multi_channel_transport_model **Unit:** :math:`\mathrm{m}\,\mathrm{s}^{-1}` Indicates flow direction in each channel (forward if value is positive, backward if value is negative), see Section :ref:`MUOPGRMflow2D`). In case of a spatially inhomogeneous setting, the :math:`\texttt{SENS_PARTYPE}` field is used for indexing the channel when specifying parameter sensitivities. - + ================ ============================= =================================================== **Type:** double **Range:** :math:`\mathbb{R}` **Length:** see :math:`\texttt{VELOCITY_MULTIPLEX}` ================ ============================= =================================================== - + ``VELOCITY_MULTIPLEX`` - Multiplexing mode of :math:`\texttt{VELOCITY}`. Determines whether :math:`\texttt{VELOCITY}` is treated as channel- and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{VELOCITY}`. Valid modes are: + Multiplexing mode of :math:`\texttt{VELOCITY}`. Determines whether :math:`\texttt{VELOCITY}` is treated as channel- and/or section-independent. This field is optional. When left out, multiplexing behavior is inferred from the length of :math:`\texttt{VELOCITY}`. Valid modes are: + + 1. Channel-independent, section-independent; length of :math:`\texttt{VELOCITY}` is 1 + 2. Channel-dependent, section-independent; length of :math:`\texttt{VELOCITY}` is :math:`\texttt{NCHANNEL}` + 3. Section-dependent; length of :math:`\texttt{VELOCITY}` is :math:`\texttt{NSEC}` + 4. Channel-dependent, section-dependent; length of :math:`\texttt{VELOCITY}` is :math:`\texttt{NCHANNEL} \cdot \texttt{NSEC}`; ordering is section-major - 1. Channel-independent, section-independent; length of :math:`\texttt{VELOCITY}` is 1 - 2. Channel-dependent, section-independent; length of :math:`\texttt{VELOCITY}` is :math:`\texttt{NCHANNEL}` - 3. Section-dependent; length of :math:`\texttt{VELOCITY}` is :math:`\texttt{NSEC}` - 4. Channel-dependent, section-dependent; length of :math:`\texttt{VELOCITY}` is :math:`\texttt{NCHANNEL} \cdot \texttt{NSEC}`; ordering is section-major - ============= =================================== ============= **Type:** int **Range:** :math:`\{0, \dots, 3 \}` **Length:** 1 ============= =================================== ============= @@ -123,27 +123,27 @@ For information on model equations, refer to :ref:`multi_channel_transport_model Ordered list containing all exchange rates :math:`e^k_{ij}` for component :math:`k` from compartment :math:`i` to :math:`j` based on the exchange matrix :math:`E^k`. The vector ordering is source channel - destination channel - component (i.e. i-j-k) major. .. math:: - - E^k=\begin{bmatrix} + + E^k=\begin{bmatrix} 0 & e^k_{12} & \dots & e^k_{1N} \\ e^k_{21} & \ddots & & \vdots\\ \vdots & & \ddots & e^k_{(N-1)N}\\ - e^k_{N1} & \dots & e^k_{N(N-1)} & 0 - \end{bmatrix} + e^k_{N1} & \dots & e^k_{N(N-1)} & 0 + \end{bmatrix} ================ ======================== =============================================== **Type:** double **Range:** :math:`[0,1]` **Length:** :math:`\texttt{NCHANNEL}*\texttt{NCHANNEL}*\texttt{NCOMP}` ================ ======================== =============================================== - + ``NCHANNEL`` - Number of channels :math:`ij` - + Number of channels :math:`ij` + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - - + + ``CHANNEL_CROSS_SECTION_AREAS`` Cross section areas @@ -151,39 +151,39 @@ For information on model equations, refer to :ref:`multi_channel_transport_model **Unit:** :math:`\mathrm{m}^{2}` Defines the cross section area of each channel - + ================ ====================== ====================================== **Type:** double **Range:** :math:`> 0` **Length:** :math:`\texttt{NCHANNEL}` ================ ====================== ====================================== - - + + Group /input/model/unit_XXX/discretization - UNIT_TYPE = MULTI_CHANNEL_TRANSPORT -------------------------------------------------------------------------------- - + ``USE_ANALYTIC_JACOBIAN`` Determines whether analytically computed Jacobian matrix (faster) is used (value is 1) instead of Jacobians generated by algorithmic differentiation (slower, value is 0) - + ============= =========================== ============= **Type:** int **Range:** :math:`\{0, 1\}` **Length:** 1 ============= =========================== ============= - + ``NCOL`` Number of axial column discretization cells - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= ``LINEAR_SOLVER_BULK`` - Linear solver used for the sparse column bulk block. This field is optional, the best available method is selected (i.e., sparse direct solver if possible). Valid values are: + Linear solver used for the sparse column bulk block. This field is optional, the best available method is selected (i.e., sparse direct solver if possible). Valid values are: + + - :math:`\texttt{DENSE}` Converts the sparse matrix into a banded matrix and uses regular LAPACK. Slow and memory intensive, but always available. + - :math:`\texttt{UMFPACK}` Uses the UMFPACK sparse direct solver (LU decomposition) from SuiteSparse. Fast, but has to be enabled when compiling and requires UMFPACK library. + - :math:`\texttt{SUPERLU}` Uses the SuperLU sparse direct solver (LU decomposition). Fast, but has to be enabled when compiling and requires SuperLU library. - - :math:`\texttt{DENSE}` Converts the sparse matrix into a banded matrix and uses regular LAPACK. Slow and memory intensive, but always available. - - :math:`\texttt{UMFPACK}` Uses the UMFPACK sparse direct solver (LU decomposition) from SuiteSparse. Fast, but has to be enabled when compiling and requires UMFPACK library. - - :math:`\texttt{SUPERLU}` Uses the SuperLU sparse direct solver (LU decomposition). Fast, but has to be enabled when compiling and requires SuperLU library. - ================ ======================================================================= ============= **Type:** string **Range:** :math:`\{\texttt{DENSE},\texttt{UMFPACK},\texttt{SUPERLU}\}` **Length:** 1 ================ ======================================================================= ============= @@ -191,9 +191,9 @@ Group /input/model/unit_XXX/discretization - UNIT_TYPE = MULTI_CHANNEL_TRANSPORT ``RECONSTRUCTION`` Type of reconstruction method for FV fluxes - + ================ ================================ ============= **Type:** string **Range:** :math:`\texttt{WENO}` **Length:** 1 ================ ================================ ============= - + For further discretization parameters, see also :ref:`flux_restruction_methods` (FV specific)), and :ref:`non_consistency_solver_parameters`. diff --git a/doc/interface/unit_operations/outlet.rst b/doc/interface/unit_operations/outlet.rst index 323da8bb6..ae543f2d0 100644 --- a/doc/interface/unit_operations/outlet.rst +++ b/doc/interface/unit_operations/outlet.rst @@ -13,18 +13,15 @@ For information on model equations, refer to :ref:`outlet_model`. Specifies the type of unit operation model - + ================ ================================== ============= **Type:** string **Range:** :math:`\texttt{OUTLET}` **Length:** 1 ================ ================================== ============= - + ``NCOMP`` Number of chemical components in the chromatographic medium - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= - - - diff --git a/doc/interface/unit_operations/radial_flow_models.rst b/doc/interface/unit_operations/radial_flow_models.rst index 4bc99053a..b1adc2dd7 100644 --- a/doc/interface/unit_operations/radial_flow_models.rst +++ b/doc/interface/unit_operations/radial_flow_models.rst @@ -19,7 +19,7 @@ For information on model equations, refer to :ref:`lumped_rate_model_without_por ``UNIT_TYPE`` Specifies the type of unit operation model - + ================ ===================================================== ============= **Type:** string **Range:** :math:`\texttt{RADIAL_GENERAL_RATE_MODEL}` **Length:** 1 ================ ===================================================== ============= @@ -29,7 +29,7 @@ For information on model equations, refer to :ref:`lumped_rate_model_without_por Radial dispersion coefficient **Unit:** :math:`\mathrm{m}_{\mathrm{IV}}^{2}\,\mathrm{s}^{-1}` - + ================ ========================= ========================================================= **Type:** double **Range:** :math:`\geq 0` **Length:** see :math:`\texttt{COL_DISPERSION_MULTIPLEX}` ================ ========================= ========================================================= @@ -40,20 +40,20 @@ For information on model equations, refer to :ref:`lumped_rate_model_without_por ``COL_RADIUS_INNER`` - Inner column radius + Inner column radius **Unit:** :math:`\mathrm{m}` - + ================ ====================== ============= **Type:** double **Range:** :math:`> 0` **Length:** 1 ================ ====================== ============= ``COL_RADIUS_OUTER`` - Outer column radius + Outer column radius **Unit:** :math:`\mathrm{m}` - + ================ ====================== ============= **Type:** double **Range:** :math:`> 0` **Length:** 1 ================ ====================== ============= @@ -64,10 +64,10 @@ For information on model equations, refer to :ref:`lumped_rate_model_without_por ``COL_LENGTH`` - Column length/height (optional if :math:`\texttt{VELOCITY_COEFF}` is present, see Section :ref:`MUOPGRMflow`) + Column length/height (optional if :math:`\texttt{VELOCITY_COEFF}` is present, see Section :ref:`MUOPGRMflow`) **Unit:** :math:`\mathrm{m}` - + ================ ====================== ============= **Type:** double **Range:** :math:`> 0` **Length:** 1 ================ ====================== ============= @@ -76,9 +76,9 @@ For information on model equations, refer to :ref:`lumped_rate_model_without_por Interstitial velocity coefficient of the mobile phase (optional :math:`\texttt{COL_LENGTH}` is present, see Section :ref:`MUOPGRMflow`). This input replaces the ``VELOCITY`` field, which is used for axial flow models. The distinction is made to emphasize that radial flow models do not incorporate a global velocity but a variable velocity field that depends on the spatial position, for details see Section :ref:`MUOPGRMradialFlow`. - + **Unit:** :math:`\mathrm{m}\,\mathrm{s}^{-1}` - + ================ ============================= ======================================= **Type:** double **Range:** :math:`\mathbb{R}` **Length:** :math:`1 / \texttt{NSEC}` ================ ============================= ======================================= @@ -90,7 +90,7 @@ Group /input/model/unit_XXX/discretization - UNIT_TYPE - RADIAL_GENERAL_RATE_MOD ``NCOL`` Number of radial column discretization points - + ============= ========================= ============= **Type:** int **Range:** :math:`\geq 1` **Length:** 1 ============= ========================= ============= @@ -101,9 +101,9 @@ Accordingly, the following specifications can be left out for radial flow models ``RECONSTRUCTION`` Type of reconstruction method for fluxes - + ================ ================================ ============= **Type:** string **Range:** :math:`\texttt{NONE}` **Length:** 1 ================ ================================ ============= -Parameters specified under :ref:`flux_restruction_methods` can also be ignored. \ No newline at end of file +Parameters specified under :ref:`flux_restruction_methods` can also be ignored. diff --git a/doc/literature.bib b/doc/literature.bib index 26c877bb2..1af3a67b1 100644 --- a/doc/literature.bib +++ b/doc/literature.bib @@ -497,4 +497,4 @@ @article{Xu2009 url = {https://www.sciencedirect.com/science/article/pii/S0021967309010103}, author = {Xuankuo Xu and Abraham M. Lenhoff}, keywords = {Protein adsorption isotherms, Competitive adsorption, Protein–surface interactions, Protein–protein interactions, Colloidal interactions, Ion-exchange chromatography}, -} \ No newline at end of file +} diff --git a/doc/modelling/binding/freundlich_ldf.rst b/doc/modelling/binding/freundlich_ldf.rst index 4351ce196..4f5710b64 100644 --- a/doc/modelling/binding/freundlich_ldf.rst +++ b/doc/modelling/binding/freundlich_ldf.rst @@ -7,11 +7,11 @@ The Freundlich isotherm model is an empirical model that considers changes in th This variant of the model is based on the linear driving force approximation (see section :ref:`ldf_model`) and is given as .. math:: - \begin{aligned} + \begin{aligned} q^*_i= k_{F,i}c_{p,i}^{\frac{1}{n_{i}}} && i = 0, \dots, N_{\text{comp}} - 1. \end{aligned} -No interaction between the components is considered when the model has multiple components. +No interaction between the components is considered when the model has multiple components. One of the limitation of this isotherm is the first order Jacobian :math:`\left(\frac{dq^*}{dc_p}\right)` tends to infinity as :math:`c_{p} \rightarrow 0` for :math:`n>1`. To address this issue an approximation of isotherm is considered near the origin. This approximation matches the isotherm in such a way that :math:`q=0` at :math:`c_p=0` and also matches the first derivative of the istotherm at :math:`c_p = \epsilon`, where :math:`\epsilon` is a very small number, for example :math:`1e-14`. @@ -19,16 +19,16 @@ The form of approximation and its derivative is given below for :math:`c_p < \va .. math:: - \begin{aligned} - q^* = \alpha_0+\alpha_1 c+\alpha_2 c_p^2 + \begin{aligned} + q^* = \alpha_0+\alpha_1 c+\alpha_2 c_p^2 \end{aligned} - - \begin{aligned} - \frac{dq^*}{dc_p} = \alpha_1+ 2 \alpha_2 c_p + + \begin{aligned} + \frac{dq^*}{dc_p} = \alpha_1+ 2 \alpha_2 c_p \end{aligned} where :math:`\alpha_0=0` and :math:`\alpha_1` and :math:`\alpha_2` are determined from :math:`\alpha_1 \varepsilon+\alpha_2 \varepsilon^2 = k_f \varepsilon^{1/n}` and :math:`\alpha_1 + 2 \alpha_2 \varepsilon = \frac{1}{n}k_f \varepsilon^{\frac{1-n}{n}}`. - + .. math:: \begin{aligned} \alpha_1 = \frac{2 n-1}{n}k_f \varepsilon^{\frac{1-n}{n}} @@ -41,4 +41,3 @@ where :math:`\alpha_0=0` and :math:`\alpha_1` and :math:`\alpha_2` are determine This approximation can be used for any pore phase cocentration :math:`c_p < \epsilon` given :math:`n>1`. For the case, when :math:`n \le 1` no special treament near the origin is required. For more information on model parameters required to define in CADET file format, see :ref:`freundlich_ldf_config`. - diff --git a/doc/modelling/binding/hic_constant_water_activity.rst b/doc/modelling/binding/hic_constant_water_activity.rst index b1dc8fa73..1348b1b63 100644 --- a/doc/modelling/binding/hic_constant_water_activity.rst +++ b/doc/modelling/binding/hic_constant_water_activity.rst @@ -15,4 +15,3 @@ This model implemments the HIC isotherm assuming a constant water activity as de - Components without bound state (i.e., salt and non-binding components) are supported. For more information on model parameters required to define in CADET file format, see :ref:`hic_constant_water_activity_config`. - diff --git a/doc/modelling/binding/hic_water_on_hydrophobic_surfaces.rst b/doc/modelling/binding/hic_water_on_hydrophobic_surfaces.rst index f14deff60..c166b0680 100644 --- a/doc/modelling/binding/hic_water_on_hydrophobic_surfaces.rst +++ b/doc/modelling/binding/hic_water_on_hydrophobic_surfaces.rst @@ -12,7 +12,7 @@ A naive multicomponent version was added that reduces to the original formulatio \beta &= \beta_0 e^{c_{p,0}\beta_1} \\ \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} \left( 1 - \sum_j \frac{q_j}{q_{max,j}} \right)^{\nu_i} - k_{d,i} q_i \left(\sum_j q_j \right)^{\nu_i \beta} \end{align} - + - Component :math:`c_0` is assumed to be salt without a bound state. - Multiple bound states are not supported. - Components without bound state (i.e., salt and non-binding components) are supported. diff --git a/doc/modelling/binding/index.rst b/doc/modelling/binding/index.rst index 98211587f..14e46a931 100644 --- a/doc/modelling/binding/index.rst +++ b/doc/modelling/binding/index.rst @@ -279,4 +279,3 @@ The models also differ in whether a mobile phase modifier (e.g., salt) is suppor :glob: * - diff --git a/doc/modelling/binding/multi_component_bi_langmuir_ldf.rst b/doc/modelling/binding/multi_component_bi_langmuir_ldf.rst index 090a9b3d6..490905f88 100644 --- a/doc/modelling/binding/multi_component_bi_langmuir_ldf.rst +++ b/doc/modelling/binding/multi_component_bi_langmuir_ldf.rst @@ -8,7 +8,7 @@ It is based on the equilibrium concentration :math:`q^*` for a given liquid phas .. math:: \begin{aligned} - q_{i,j}^*=\frac{q_{m,i,j} k_{eq,i,j} c_i}{1 + \sum_{k=1}^{N_{comp}}{k_{eq,k,j} c_k}} && i = 0, \dots, N_{\text{comp}} - 1, \: j = 0, \dots, M - 1.% (0 \leq i \leq N_{\text{comp}} - 1, \: 0 \leq j \leq M - 1). + q_{i,j}^*=\frac{q_{m,i,j} k_{eq,i,j} c_i}{1 + \sum_{k=1}^{N_{comp}}{k_{eq,k,j} c_k}} && i = 0, \dots, N_{\text{comp}} - 1, \: j = 0, \dots, M - 1.% (0 \leq i \leq N_{\text{comp}} - 1, \: 0 \leq j \leq M - 1). \end{aligned} diff --git a/doc/modelling/binding/multi_component_colloidal.rst b/doc/modelling/binding/multi_component_colloidal.rst index c0ec0d40c..151417d82 100644 --- a/doc/modelling/binding/multi_component_colloidal.rst +++ b/doc/modelling/binding/multi_component_colloidal.rst @@ -3,7 +3,7 @@ Multi Component Colloidal ~~~~~~~~~~~~~~~~~~~~~~~~~ -The colloidal isotherm assumes that adsorbed protein molecules are evenly distributed in a hexagonal arrangement at equilibrium, with the coverage varying via adjustment of the lattice size :cite:`Xu2009`. +The colloidal isotherm assumes that adsorbed protein molecules are evenly distributed in a hexagonal arrangement at equilibrium, with the coverage varying via adjustment of the lattice size :cite:`Xu2009`. Protein-surface interactions are captured in a parameter :math:`K_{e}`. The surface coverage is modulated at higher coverage, described by a Yukawa potential. The Yukawa constant :math:`b_{pp}` characterizes protein-protein interactions. @@ -11,13 +11,13 @@ Since these are long-ranged, they are assumed to be primarily electrostatic for .. math:: \begin{aligned} - \frac{{dq}_{i}}{dt} = & k_{kin,i} \left( c_{p,i} - c_{p,i}^\star \right) + \frac{{dq}_{i}}{dt} = & k_{kin,i} \left( c_{p,i} - c_{p,i}^\star \right) \quad i = 0, \dots, N_{comp} - 1, \\ c_{p,i}^\star = & q_{i} \exp \Biggl[ \frac{n\left( 3 + \kappa R \right)}{4 q_{tot}} \sum_{j=0}^{N_{bound}} {q_{j} \sqrt{b_{pp,i} b_{pp,j}}} \frac{r_{i} + r_{j}}{2R} \exp \bigl[ - \kappa \left( R - \left( r_{i} + r_{j} \right) \right) \bigr] \\ - &\phantom{q_{i} \exp \Biggl[} - ln \left(K_{e,i} \right) \Biggr], + &\phantom{q_{i} \exp \Biggl[} - ln \left(K_{e,i} \right) \Biggr], \end{aligned} where :math:`n` is the coordination number describing the two-dimensional lattice agreement of protein molecules on the resin surface (:math:`n=6` for hexagonal arrangement). @@ -62,7 +62,7 @@ Optionally, they can also be varied as a function of the pH, then represented by ln \left( K_{e, i} \right) &= {c_{p,1}}^{k_{e,i}} \left( k_{a,i} {c_{p, 0}}^{-k_{b,i}} + k_{c,i} \exp \left( k_{d,i} c_{p,0} \right) \right) \\ b_{pp,i} &= {c_{p,1}}^{b_{e,i}} \left( b_{a,i} {c_{p,0}}^{b_{b,i}} + b_{c,i} \exp \left( b_{d,i} c_{p,0} \right) \right), -where :math:`k_{a-e}`, :math:`b_{a-e}` are fitting constants. +where :math:`k_{a-e}`, :math:`b_{a-e}` are fitting constants. Because the model becomes mathematically singular at zero concentration, the original equation is replaced by its mathematical limit below a threshold concentration :math:`c_\epsilon > 0`. @@ -74,4 +74,3 @@ Because the model becomes mathematically singular at zero concentration, the ori For more information on model parameters required to define in CADET file format, see :ref:`multi_component_colloidal_config`. - diff --git a/doc/modelling/index.rst b/doc/modelling/index.rst index 283c9f3a9..49cdf4e00 100644 --- a/doc/modelling/index.rst +++ b/doc/modelling/index.rst @@ -4,7 +4,7 @@ Modelling ========= This section gives complete information about the supported unit operations, binding and reaction models, and creating a network among all the unit operations in CADET. For details on file format specifications related to define each unit operation in CADET, see section :ref:`file_format`. - + .. toctree:: unit_operations/index binding/index diff --git a/doc/modelling/networks.rst b/doc/modelling/networks.rst index d613a64e7..30070f3fc 100644 --- a/doc/modelling/networks.rst +++ b/doc/modelling/networks.rst @@ -4,7 +4,7 @@ Networks of unit operations =========================== Unit operation models can be composed into a network or graph, in which a node represents a unit operation and an edge denotes a connection between two unit operations. -When utilized to full extent, this allows the simulation of complicated setups and processes (e.g., SMB, MCSGP). +When utilized to full extent, this allows the simulation of complicated setups and processes (e.g., SMB, MCSGP). A more simple use case is the addition of plug flows and stirred tanks up- and downstream of a column in order to account for dead volume and additional dispersion from the tubing. In a network, outlet ports of unit operations can be connected to any number of inlet ports of unit operations. @@ -25,17 +25,17 @@ The inlet port variables :math:`c_{\text{in},n,k}` of unit operation :math:`n` a :label: NetworkInletConnection \begin{aligned} - c_{\text{in},n,k,i} &= c_{\text{con},n,k,i}, \qquad k = 1, \dots, N_{\text{port},\text{in},n},\quad i = 1, \dots, N_{\text{comp},n}. + c_{\text{in},n,k,i} &= c_{\text{con},n,k,i}, \qquad k = 1, \dots, N_{\text{port},\text{in},n},\quad i = 1, \dots, N_{\text{comp},n}. \end{aligned} While :math:`N_{\text{port},\text{in},n}` denotes the number of inlet ports of unit operation :math:`n`, the number of outlet ports is given by :math:`N_{\text{port},\text{out},n}`. -The connection variables :math:`c_{\text{con},n,k,i}` collect all inflows of component :math:`i` into port :math:`k` of unit operation :math:`n`: +The connection variables :math:`c_{\text{con},n,k,i}` collect all inflows of component :math:`i` into port :math:`k` of unit operation :math:`n`: .. math:: :label: NetworkConnection \begin{aligned} - c_{\text{con},n,k,i} &= \frac{\sum_{m=1}^{N_{\text{units}}} \sum_{\ell = 1}^{N_{\text{port},\text{out},n}} \sum_{j = 1}^{N_{\text{comp},m}} S_{(n,k,i),(m,\ell,j)} Q_{m,\ell} c_{\text{out},m,\ell,j}}{\sum_{m=1}^{N_{\text{units}}} \sum_{\ell=1}^{N_{\text{port},\text{out},m}} \hat{S}_{(n,k),(m,\ell)} Q_{m,\ell} }, + c_{\text{con},n,k,i} &= \frac{\sum_{m=1}^{N_{\text{units}}} \sum_{\ell = 1}^{N_{\text{port},\text{out},n}} \sum_{j = 1}^{N_{\text{comp},m}} S_{(n,k,i),(m,\ell,j)} Q_{m,\ell} c_{\text{out},m,\ell,j}}{\sum_{m=1}^{N_{\text{units}}} \sum_{\ell=1}^{N_{\text{port},\text{out},m}} \hat{S}_{(n,k),(m,\ell)} Q_{m,\ell} }, \end{aligned} where :math:`F_{m,\ell}` denotes the volumetric flow rate from outlet port :math:`\ell` of unit operation :math:`m`, :math:`S_{(n,k,i),(m,\ell,j)} \in \{0, 1\}` is a connection matrix indicating whether component :math:`i` at outlet port :math:`k` of unit operation :math:`n` is connected to component :math:`j` at inlet port :math:`\ell` of unit operation :math:`m`, and :math:`\hat{S}_{(n,k),(m,\ell)} \in \{0, 1\}` is another connection matrix indicating whether outlet port :math:`k` of unit operation :math:`n` is connected to inlet port :math:`\ell` of unit operation :math:`m`, that is @@ -83,7 +83,7 @@ The same setting (i.e., setting both port indices to :math:`-1`) can be used to Note that in case of multiple rows for one connection between two unit operation ports (e.g., in case of separate component connections) the flow rate of the first row of that connection is used and all following flow rates are ignored. Consequently, there can only be one flow rate for a connection between two unit operations regardless of which components are connected. -The connection table is expected in row-major storage format (i.e., the rows are appended to one long array). +The connection table is expected in row-major storage format (i.e., the rows are appended to one long array). .. _MUOPNetworkValveSwitches: @@ -145,4 +145,3 @@ This default can be overridden by a flag (see Table :ref:`FFModelSolver`). The solution method is selected for each valve switch individually. If some network configurations contain cycles, the parallel method is chosen for them regardless of the method used for the other configurations. - diff --git a/doc/modelling/reaction/index.rst b/doc/modelling/reaction/index.rst index 172908182..d70292f37 100644 --- a/doc/modelling/reaction/index.rst +++ b/doc/modelling/reaction/index.rst @@ -38,4 +38,4 @@ The 0-based indices of the external source for each parameter is given in the da By assigning only one index to ``EXTFUN``, all parameters use the same source. The ordering of the parameters in ``EXTFUN`` is given by the ordering in the file format specification. -For the configuration of external function dependence and more information on model parameters required to define in CADET file format, see section :ref:`FFReaction`. \ No newline at end of file +For the configuration of external function dependence and more information on model parameters required to define in CADET file format, see section :ref:`FFReaction`. diff --git a/doc/modelling/reaction/mass_action_law.rst b/doc/modelling/reaction/mass_action_law.rst index 79b25a400..c9d390675 100644 --- a/doc/modelling/reaction/mass_action_law.rst +++ b/doc/modelling/reaction/mass_action_law.rst @@ -26,7 +26,7 @@ The matrices :math:`E^l_{\mathrm{fwd}} = (e^l_{\mathrm{fwd},\ell,j}) \in \mathbb \begin{aligned} e^l_{\mathrm{fwd},\ell,j} &= \max(0, -s^l_{\ell,j}), \\ - e^l_{\mathrm{bwd},\ell,j} &= \max(0, s^l_{\ell,j}). + e^l_{\mathrm{bwd},\ell,j} &= \max(0, s^l_{\ell,j}). \end{aligned} However, these defaults can be changed by providing those matrices. diff --git a/doc/modelling/unit_operations/2d_general_rate_model.rst b/doc/modelling/unit_operations/2d_general_rate_model.rst index 6684f475e..da39e962b 100644 --- a/doc/modelling/unit_operations/2d_general_rate_model.rst +++ b/doc/modelling/unit_operations/2d_general_rate_model.rst @@ -11,15 +11,15 @@ This model can be improved by introducing a radial coordinate :math:`\rho \in [0 .. math:: :label: ModelColumn2D - \varepsilon_c \frac{\partial c^\ell_i}{\partial t} = &-\varepsilon_c u \frac{\partial c^\ell_i}{\partial z} + \varepsilon_c D_{\text{ax},i} \frac{\partial^2 c^\ell_i}{\partial z^2} + \frac{1}{\rho} \frac{\partial}{\partial \rho} \left( \rho D_{\text{rad},i} \frac{\partial}{\partial \rho} \left( \varepsilon_c c^\ell_i \right) \right) \\ - &- \left(1 - \varepsilon_c\right) \sum_j d_j \frac{ 3 k_{f,j,i} }{r_{p,j}} \left[ c^\ell_i - c^p_{j,i}(\cdot, \cdot, \cdot, r_{p,j}) \right] + \varepsilon_c f_{\text{react},i}^\ell\left(c^\ell\right). + \varepsilon_c \frac{\partial c^\ell_i}{\partial t} = &-\varepsilon_c u \frac{\partial c^\ell_i}{\partial z} + \varepsilon_c D_{\text{ax},i} \frac{\partial^2 c^\ell_i}{\partial z^2} + \frac{1}{\rho} \frac{\partial}{\partial \rho} \left( \rho D_{\text{rad},i} \frac{\partial}{\partial \rho} \left( \varepsilon_c c^\ell_i \right) \right) \\ + &- \left(1 - \varepsilon_c\right) \sum_j d_j \frac{ 3 k_{f,j,i} }{r_{p,j}} \left[ c^\ell_i - c^p_{j,i}(\cdot, \cdot, \cdot, r_{p,j}) \right] + \varepsilon_c f_{\text{react},i}^\ell\left(c^\ell\right). -Here, +Here, - :math:`c^\ell_i\colon \left[0, T_{\text{end}}\right] \times [0, L] \times [0, R] \rightarrow \mathbb{R}^{\geq 0}`, - - :math:`c^p_{j,i}\colon \left[0, T_{\text{end}}\right] \times [0, L] \times [0, R] \times [r_{c,j}, r_{p,j}] \rightarrow \mathbb{R}^{\geq 0}`, and - - :math:`c^s_{j,i,m_{j,i}}\colon \left[0, T_{\text{end}}\right] \times [0, L] \times [0, R] \times [r_{c,j}, r_{p,j}] \rightarrow \mathbb{R}^{\geq 0}` - + - :math:`c^p_{j,i}\colon \left[0, T_{\text{end}}\right] \times [0, L] \times [0, R] \times [r_{c,j}, r_{p,j}] \rightarrow \mathbb{R}^{\geq 0}`, and + - :math:`c^s_{j,i,m_{j,i}}\colon \left[0, T_{\text{end}}\right] \times [0, L] \times [0, R] \times [r_{c,j}, r_{p,j}] \rightarrow \mathbb{R}^{\geq 0}` + depend on :math:`\rho`. Additionally, the porosity :math:`\varepsilon_c`, axial dispersion coefficient :math:`D_{\text{ax},i}`, radial dispersion coefficient :math:`D_{\text{rad},i}`, and interstitial velocity :math:`u` may depend on :math:`\rho`. @@ -31,7 +31,7 @@ Continuous dependence of the parameters can be realized by piecewise constant ap The Danckwerts boundary conditions at the column in- and outlet, Eq. :eq:`BCInlet` and :eq:`BCOutlet`, are modified to account for the radial coordinate: .. math:: - :label: BCInlet2D + :label: BCInlet2D \begin{aligned} u(\rho) c_{\text{in},i}(t,\rho) &= u(\rho) c^\ell_i(t,0,\rho) - D_{\text{ax},i}(\rho) \frac{\partial c^\ell_i}{\partial z}(t, 0, \rho) & \forall t > 0, \rho \in (0,R), @@ -41,7 +41,7 @@ The Danckwerts boundary conditions at the column in- and outlet, Eq. :eq:`BCInl :label: BCOutlet2D \begin{aligned} - \frac{\partial c^\ell_i}{\partial z}(t, L, \rho) &= 0 & \forall t > 0, \rho \in (0,R). + \frac{\partial c^\ell_i}{\partial z}(t, L, \rho) &= 0 & \forall t > 0, \rho \in (0,R). \end{aligned} Conditions for the radial direction are added: @@ -57,7 +57,7 @@ Conditions for the radial direction are added: :label: BCRadial2DOuter \begin{aligned} - \frac{\partial{c^\ell_i}}{\partial \rho}(\cdot, \cdot, R) &= 0. + \frac{\partial{c^\ell_i}}{\partial \rho}(\cdot, \cdot, R) &= 0. \end{aligned} While the inner condition Eq.\ :eq:`BCRadial2DInner` represents symmetry at the column center, the outer condition Eq. :eq:`BCRadial2DOuter` is a no-flux condition. diff --git a/doc/modelling/unit_operations/cstr.rst b/doc/modelling/unit_operations/cstr.rst index bf9961d97..35cfe2bf5 100644 --- a/doc/modelling/unit_operations/cstr.rst +++ b/doc/modelling/unit_operations/cstr.rst @@ -39,5 +39,3 @@ The additional parameter :math:`F_{\text{filter}}`, which denotes the flow rate Note that it is the user’s duty to make sure that the volume of the CSTR does not fall below 0. If it does, the simulation may fail to run or may produce unreasonable (e.g., unphysical) results. See :ref:`cstr_config`. - - diff --git a/doc/modelling/unit_operations/general_rate_model.rst b/doc/modelling/unit_operations/general_rate_model.rst index b6dd3b6ff..1c446fb8b 100644 --- a/doc/modelling/unit_operations/general_rate_model.rst +++ b/doc/modelling/unit_operations/general_rate_model.rst @@ -22,11 +22,11 @@ The main assumptions are: - Domain - Description * - :math:`i` - - :math:`\left\{ 0, \dots, N_{\text{comp}} - 1 \right\}` + - :math:`\left\{ 0, \dots, N_{\text{comp}} - 1 \right\}` - Component index * - :math:`j` - :math:`\left\{ 0, \dots, N_{\text{partype}} - 1 \right\}` - - Particle type index + - Particle type index * - :math:`m_{j,i}` - :math:`\left\{ 0, \dots, N_{\text{bnd},j,i} - 1 \right\}` - Bound state index of :math:`i`\ th component in :math:`j`\ th particle type @@ -35,13 +35,13 @@ The main assumptions are: - Total bound state index in particle type :math:`j` * - :math:`t` - :math:`\left[0, T_{\text{end}}\right]` - - Time coordinate + - Time coordinate * - :math:`z` - :math:`\left[0, L\right]` - Axial coordinate * - :math:`r` - :math:`\left[r_{c,j}, r_{p,j}\right]` - - Generic bead radial coordinate + - Generic bead radial coordinate * - :math:`c^\ell_{i}(t,z)` - :math:`\left[0, T_{\text{end}}\right] \times [0, L]` - Interstitial concentration of the :math:`i`\ th component @@ -51,11 +51,11 @@ The main assumptions are: * - :math:`c^s_{j,i,m_{j,i}}(t, z, r)` - :math:`\left[0, T_{\text{end}}\right] \times [0,L] \times \left[r_{c,j}, r_{p,j}\right]` - Solid phase concentration of the :math:`i`\ th component's :math:`m_{j,i}`\th bound state in particles of type :math:`j` - * - :math:`j_{f,j,i}(t, z)` + * - :math:`j_{f,j,i}(t, z)` - :math:`\left[0, T_{\text{end}}\right] \times [0, L]` - - Flux of the :math:`i`\ th component through stagnant film into the bead of type :math:`j` + - Flux of the :math:`i`\ th component through stagnant film into the bead of type :math:`j` -.. _ModelGRMColumn: +.. _ModelGRMColumn: .. figure:: column_bulk_model.png Column bulk model @@ -69,7 +69,7 @@ Consider a column of length :math:`L>0` filled with spherical beads of (possibly \begin{aligned} \frac{\partial c^\ell_i}{\partial t} = -u \frac{\partial c^\ell_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 c^\ell_i}{\partial z^2} &- \frac{1}{\beta_c} \sum_j d_j \frac{3}{r_{p,j}} k_{f,j,i} \left[ c^\ell_i - c^p_{j,i}(\cdot, \cdot, r_{p,j}) \right] \\ - &+ f_{\text{react},i}^\ell\left(c^\ell\right). + &+ f_{\text{react},i}^\ell\left(c^\ell\right). \end{aligned} Here, :math:`c^\ell_i\colon \left[0, T_{\text{end}}\right] \times [0, L] \rightarrow \mathbb{R}^{\geq 0}` denotes the concentration in the interstitial column volume, :math:`c^p_{j,i}\colon \left[0, T_{\text{end}}\right] \times [0, L] \times [r_{c,j}, r_{p,j}] \rightarrow \mathbb{R}^{\geq 0}` the liquid phase concentration in the beads, :math:`k_{f,j,i}` the film diffusion coefficient, :math:`D_{\text{ax},i}` the dispersion coefficient, :math:`u` the interstitial velocity, :math:`d_j` the volume fraction of particle type :math:`j`, and :math:`\beta_c = \varepsilon_c / (1 - \varepsilon_c)` the column phase ratio, where :math:`\varepsilon_c` is the column porosity (ratio of interstitial volume to total column volume). @@ -88,7 +88,7 @@ Danckwerts boundary conditions :cite:`Danckwerts1953` are applied to inlet and o :label: BCInlet \begin{aligned} - \frac{\partial c^\ell_i}{\partial z}(t, L) &= 0 & \forall t > 0. + \frac{\partial c^\ell_i}{\partial z}(t, L) &= 0 & \forall t > 0. \end{aligned} Note that the outlet boundary condition Eq. :eq:`BCOutlet` is also known as “do nothing” or natural outflow condition. @@ -124,7 +124,7 @@ The GRM is used with both quasi-stationary (Eq. :eq:`REqBinding`) and dynamic (E \begin{aligned} \text{dynamic: } \frac{\partial c^s_j}{\partial t} &= D_{s,j} \left[\frac{\partial^2}{\partial r^2} + \frac{2}{r} \frac{\partial }{\partial r} \right] c^s_{j} \\ - &+ f_{\text{ads},j}\left( c^p_j, c^s_j\right) + f_{\text{react},j}^s\left( c_j^p, c_j^s \right). + &+ f_{\text{ads},j}\left( c^p_j, c^s_j\right) + f_{\text{react},j}^s\left( c_j^p, c_j^s \right). \end{aligned} Note that :math:`c^p_j` and :math:`c^s_j` denote the vector of all :math:`c^p_{j,i}` and :math:`c^s_{j,i,m_{j,i}}`, respectively. @@ -152,7 +152,7 @@ By default, the following initial conditions are applied for all :math:`z \in [0 :label: InitialConditions \begin{aligned} - c^\ell_i(0, z) &= 0, & c^p_{j,i}(0, z, r) &= 0, & c^s_{j,i,m_{j,i}}(0,z,r) &= 0. + c^\ell_i(0, z) &= 0, & c^p_{j,i}(0, z, r) &= 0, & c^s_{j,i,m_{j,i}}(0,z,r) &= 0. \end{aligned} .. _ModelGRMBead: @@ -163,7 +163,7 @@ By default, the following initial conditions are applied for all :math:`z \in [0 .. _ModelGRMStates: .. figure:: multiple_bound_states.png :scale: 50 % - + Binding with multiple bound states @@ -196,7 +196,7 @@ For cylinders, the factor :math:`3 / r_{p,j}` in Eq. (:eq:`ModelColumn`) changes \begin{aligned} \left[\frac{\partial^2}{\partial r^2} + \frac{2}{r} \frac{\partial }{\partial r} \right] \quad \rightarrow \quad \left[\frac{\partial^2}{\partial r^2} + \frac{1}{r} \frac{\partial }{\partial r} \right]. \end{aligned} - + For slabs, the factor :math:`3 / r_{p,j}` in (see Eq. (:eq:`ModelColumn`)) changes to :math:`1 / r_{p,j}` and the diffusion operator in Eq. (:eq:`ModelBead`) and Eq. (:eq:`DynBinding`) changes as .. math:: @@ -225,7 +225,7 @@ The distribution of the particle types is governed by their volume fractions :ma The particle type volume fractions can be spatially constant throughout the column, or depend on the position inside the column bulk volume. In the latter case, the user can specify a set of volume fractions for each discretized finite volume cell. -This allows, for example, the placement of smaller particles near the frits. +This allows, for example, the placement of smaller particles near the frits. .. _MUOPGRMSizeExclusion: @@ -233,7 +233,7 @@ Size exclusion chromatography ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The general rate model can be used to simulate size exclusion chromatography (SEC) :cite:`Gu1995`. -The particle porosity :math:`\varepsilon_{p,j}` on the mobile phase side of the transport equations is replaced by a component-dependent accessible porosity +The particle porosity :math:`\varepsilon_{p,j}` on the mobile phase side of the transport equations is replaced by a component-dependent accessible porosity .. math:: @@ -270,7 +270,7 @@ Since volumetric flow rates are specified for each network connection, the unit \end{aligned} where :math:`F_{\text{in}}` denotes the volumetric flow rate and :math:`A` the cross section area. -Note that without the bulk porosity :math:`\varepsilon_c`, the superficial velocity would be obtained. +Note that without the bulk porosity :math:`\varepsilon_c`, the superficial velocity would be obtained. The direction of flow inside the unit operation is governed by the sign of the interstitial velocity :math:`u`. A positive sign results in (standard) forward flow, whereas a negative sign reverses the flow direction. @@ -318,7 +318,7 @@ Consider a hollow (double walled) column with inner column diameter :math:`\math \begin{aligned} \frac{\partial c^\ell_i}{\partial t} = -\frac{u}{\rho} \frac{\partial c^\ell_i}{\partial \rho} + D_{\text{rad},i} \frac{1}{\rho} \frac{\partial}{\partial \rho} \left(\rho \frac{\partial c^\ell_i}{\partial \rho} \right) &- \frac{1}{\beta_c} \sum_j d_j \frac{3}{r_{p,j}} k_{f,j,i} \left[ c^\ell_i - c^p_{j,i}(\cdot, \cdot, r_{p,j}) \right] \\ - &+ f_{\text{react},i}^\ell\left(c^\ell\right). + &+ f_{\text{react},i}^\ell\left(c^\ell\right). \end{aligned} Here, :math:`c^\ell_i\colon \left[0, T_{\text{end}}\right] \times [\mathrm{P}_c, \mathrm{P}] \rightarrow \mathbb{R}^{\geq 0}` denotes the concentration in the interstitial column volume, :math:`c^p_{j,i}\colon \left[0, T_{\text{end}}\right] \times [P_c, P] \times [r_{c,j}, r_{p,j}] \rightarrow \mathbb{R}^{\geq 0}` the liquid phase concentration in the beads, :math:`k_{f,j,i}` the film diffusion coefficient, :math:`D_{\text{rad},i}` the dispersion coefficient, :math:`u` the interstitial velocity, :math:`d_j` the volume fraction of particle type :math:`j`, and :math:`\beta_c = \varepsilon_c / (1 - \varepsilon_c)` the column phase ratio, where :math:`\varepsilon_c` is the column porosity (ratio of interstitial volume to total column volume). @@ -337,7 +337,7 @@ Danckwerts boundary conditions :cite:`Danckwerts1953` are applied to inlet and o :label: BCInlet \begin{aligned} - \frac{\partial c^\ell_i}{\partial \rho}(t, \mathrm{P}) &= 0 & \forall t > 0. + \frac{\partial c^\ell_i}{\partial \rho}(t, \mathrm{P}) &= 0 & \forall t > 0. \end{aligned} Note that the outlet boundary condition Eq. :eq:`BCOutlet` is also known as “do nothing” or natural outflow condition. diff --git a/doc/modelling/unit_operations/index.rst b/doc/modelling/unit_operations/index.rst index d712f21a3..2fd25ec73 100644 --- a/doc/modelling/unit_operations/index.rst +++ b/doc/modelling/unit_operations/index.rst @@ -55,7 +55,7 @@ is given in :numref:`table_features_unit_operations`. - × -Moreover, the pseudo unit operations :ref:`inlet_model`, and :ref:`outlet_model` act as sources and sinks for the system. +Moreover, the pseudo unit operations :ref:`inlet_model`, and :ref:`outlet_model` act as sources and sinks for the system. We further note that radial flow model variants are available for the LRM, LRMP and GRM. diff --git a/doc/modelling/unit_operations/lumped_rate_model_without_pores.rst b/doc/modelling/unit_operations/lumped_rate_model_without_pores.rst index bc959603d..13e40138b 100644 --- a/doc/modelling/unit_operations/lumped_rate_model_without_pores.rst +++ b/doc/modelling/unit_operations/lumped_rate_model_without_pores.rst @@ -4,13 +4,13 @@ Lumped rate model without pores (LRM) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The lumped rate model without pores :cite:`Guiochon2006,Felinger2004` deviates from the lumped rate model with pores (see Section :ref:`lumped_rate_model_with_pores_model`) by neglecting pores completely. -The particle phase :math:`c^p` is removed and the porosity :math:`\varepsilon_t` is taken as total porosity +The particle phase :math:`c^p` is removed and the porosity :math:`\varepsilon_t` is taken as total porosity .. math:: :label: TotalPorosity \begin{aligned} - \varepsilon_t = \varepsilon_c + \left( 1 - \varepsilon_c \right) \varepsilon_p. + \varepsilon_t = \varepsilon_c + \left( 1 - \varepsilon_c \right) \varepsilon_p. \end{aligned} The phase ratio is denoted by :math:`\beta_t = \varepsilon_t / (1 - \varepsilon_t)` accordingly. diff --git a/doc/modelling/unit_operations/multi_channel_transport_model.rst b/doc/modelling/unit_operations/multi_channel_transport_model.rst index 7f5cd8e25..612fc5b81 100644 --- a/doc/modelling/unit_operations/multi_channel_transport_model.rst +++ b/doc/modelling/unit_operations/multi_channel_transport_model.rst @@ -1,13 +1,13 @@ .. _multi_channel_transport_model_model: -Multichannel Transport model (MCT model) +Multichannel Transport model (MCT model) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Multichannel Transport (MCT) model in CADET is based on a class of compartment models introduced by Jonas Bühler et al. :cite:`Buehler2014`, which was originally developed in the field of plant sciences. There it is used to determine transport and storage parameters of radioactive labelled tracer molecules from positron emission tomography (PET) or magnetic resonance imaging (MRI) based experimental data. The model represents main functions of vascular transport pathways: axial transport of the tracer, diffusion in axial direction, lateral exchange between compartments and storage of tracer in compartments. Here, the axial direction represents the length of the stem of the plant and the lateral dimension its cross section. In the MCT context, the compartments of the model class are also referred to as channels. -The same model equations arise in describing other biological and technical processes outside of the field of plant sciences, where solutes are transported and exchanged between spatially separated compartments, for example liquid-liquid chromatography (LLC). Here, components in a mixture are separated based on their interactions with two immiscible phases of a biphasic solvent system :cite:`Morley2020`. The MCT model represents these phases by channels with respective transport and exchange properties. While the current implementation only covers linear driving forces for the exchange processes, the reaction module in CADET allows to add non-linear driving forces for the exchange processes and other chemical reactions in the channels. +The same model equations arise in describing other biological and technical processes outside of the field of plant sciences, where solutes are transported and exchanged between spatially separated compartments, for example liquid-liquid chromatography (LLC). Here, components in a mixture are separated based on their interactions with two immiscible phases of a biphasic solvent system :cite:`Morley2020`. The MCT model represents these phases by channels with respective transport and exchange properties. While the current implementation only covers linear driving forces for the exchange processes, the reaction module in CADET allows to add non-linear driving forces for the exchange processes and other chemical reactions in the channels. The MCT model equations are given for all channels :math:`l \in \{1, \dots, N_k\}` and components :math:`i \in \{1, \dots, N_c\}` by @@ -46,7 +46,7 @@ For information on model parameters see :ref:`multi_channel_transport_model_conf .. _fig-model-class: .. figure:: multi_channel_transport_model_class.png - Illustration of the Multichannel Transport model class and relevant parameters. + Illustration of the Multichannel Transport model class and relevant parameters. Figure taken from Jonas Bühler et al. :cite:`Buehler2014`. The cross-section area :math:`A_N` is individually specified for each channel (see :numref:`fig-variable-areas`). The MCT is agnostic to the shape of these cross sections, while their ratio determines the distribution of the volumetric flow. @@ -77,7 +77,7 @@ There, the model class is defined by a system of partial differential equations: e_{21} & \ddots & & \vdots\\ \vdots & & \ddots & e_{(N-1)N}\\ e_{N1} & \dots & e_{N(N-1)} & 0 - \end{bmatrix}- + \end{bmatrix}- \begin{bmatrix} {\sum_{k=1}^{N} e_{1k}} & & 0 \\ & \ddots & \\ @@ -127,7 +127,7 @@ We set the transport parameters of the second channel to zero, change notation f \frac{\partial c^s}{\partial t} = e_{12}^0 c^\ell A_1 / A_2 - e_{21}^0 c^s. To model the linear binding, we define the exchange rates according to the adsorption and desorption rates and adjust for the channel volumes: -Since the binding fluxes are computed from the binding rates w.r.t bead surface area (i.e. solid volume), we need to adjust the exchange rate from the first to the second channel accordingly; remember that exchange fluxes are computed based on exchange rates w.r.t channel (in this case liquid) volume. +Since the binding fluxes are computed from the binding rates w.r.t bead surface area (i.e. solid volume), we need to adjust the exchange rate from the first to the second channel accordingly; remember that exchange fluxes are computed based on exchange rates w.r.t channel (in this case liquid) volume. That is, we define :math:`e_{12}^0 = k_a A_2 / A_1 = k_a \frac{1-\varepsilon_t}{\varepsilon_t}` and :math:`e_{12}^0 = k_d` and get .. math:: @@ -142,4 +142,4 @@ Adding :math:`\frac{1-\varepsilon_t}{\varepsilon_t}` times the second channel eq \frac{\partial c^\ell}{\partial t} + \frac{1-\varepsilon_t}{\varepsilon_t} \frac{\partial c^s}{\partial t} = - u \frac{\partial c^\ell}{\partial z} + D_\text{ax} \frac{\partial^2 c^\ell}{\partial z^2}, \\ - \frac{\partial c^s}{\partial t} = k_a c^\ell - k_d c^s. \ No newline at end of file + \frac{\partial c^s}{\partial t} = k_a c^\ell - k_d c^s. diff --git a/doc/simulation/index.rst b/doc/simulation/index.rst index a242b5862..6175ea33f 100644 --- a/doc/simulation/index.rst +++ b/doc/simulation/index.rst @@ -6,7 +6,7 @@ Simulation This chapter gives an overview of the simulation process and the different steps involved. CADET uses a backward-differentiation-formula (BDF) time discretization as implemented by the IDAS solver from SUNDIALS :cite:`Hindmarsh2005`. -Each time step requires the solution of a nonlinear algebraic system of equations is performed by a Newton method. +Each time step requires the solution of a nonlinear algebraic system of equations is performed by a Newton method. Since chromatographic systems can exhibit strong nonlinearity and stiff systems, the Jacobian of the equation system is always updated (i.e., an “exact” Newton method is used). .. _SimFlowchart: @@ -137,7 +137,7 @@ This *ansatz* is inserted into the DAE and the resulting nonlinear algebraic equ :label: BDFNonlinSystem \begin{aligned} - H(y_\tau) = F\left(t_\tau,y_\tau,\frac{1}{\Delta t_\tau} \,\sum_{i=0}^{q_\tau}{\alpha_{\tau,i} \, y_{\tau-i}}, p\right) = 0 + H(y_\tau) = F\left(t_\tau,y_\tau,\frac{1}{\Delta t_\tau} \,\sum_{i=0}^{q_\tau}{\alpha_{\tau,i} \, y_{\tau-i}}, p\right) = 0 \end{aligned} This requires (possibly many) solutions of linear equation systems involving the Jacobian of :math:`H` given by @@ -265,5 +265,3 @@ Note that nonlinear relationships between original parameter and meta parameters \begin{aligned} \alpha_i = \frac{\partial p_i}{\partial p}. \end{aligned} - - diff --git a/doc/zbibliography.rst b/doc/zbibliography.rst index e012803d1..66d7dfb50 100644 --- a/doc/zbibliography.rst +++ b/doc/zbibliography.rst @@ -1,9 +1,5 @@ Bibliography ============ -.. bibliography:: +.. bibliography:: :style: unsrt - - - - diff --git a/include/ad/setfad.hpp b/include/ad/setfad.hpp index c89975067..54ba0f24c 100644 --- a/include/ad/setfad.hpp +++ b/include/ad/setfad.hpp @@ -1,11 +1,11 @@ // ============================================================================= // SETFAD - Simple Expression Template Forward Automatic Differentiation // (Part of SFAD library) -// +// // Copyright © Samuel Leweke¹ -// +// // ¹ Forschungszentrum Juelich GmbH, IBG-1, Juelich, Germany. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -24,590 +24,813 @@ namespace sfad { - namespace detail - { - template real_t sqr(const real_t& x) SFAD_NOEXCEPT { return x * x; } - } - - - // Base class for all expression type trees - template - class Expr - { - public: - - inline const T& base() const SFAD_NOEXCEPT { return static_cast(*this); } - inline const real_t value() const SFAD_NOEXCEPT { return base().value(); } - inline const real_t gradient(std::size_t idx) const { return base().gradient(idx); } - - private: - // Cannot assign to expression - Expr& operator=(const Expr&) { return *this; } - }; - - - // Main data type that actually holds the derivative vector - template - class FwdET : public Expr, real_t> - { - public: - typedef std::size_t idx_t; - - FwdET(const real_t val) SFAD_NOEXCEPT : _val(0) - { - setADValue(real_t(0)); - } - FwdET(const real_t val) SFAD_NOEXCEPT : _val(val) - { - setADValue(real_t(0)); - } - FwdET(const real_t val, real_t const* const grad) SFAD_NOEXCEPT : _val(val) - { - std::copy_n(grad, detail::globalGradSize, _grad); - } - FwdET(const FwdET& cpy) SFAD_NOEXCEPT = default; - FwdET(FwdET&& other) SFAD_NOEXCEPT = default; - - // Contains the one (and only) loop in expression template paradigm - template - FwdET(const Expr& other) SFAD_NOEXCEPT : _val(other.value()) - { - for (idx_t i = 0; i < detail::globalGradSize; ++i) - _grad[i] = other.gradient(i); - } - - ~FwdET() SFAD_NOEXCEPT = default; - - FwdET& operator=(FwdET&& other) SFAD_NOEXCEPT = default; - FwdET& operator=(const FwdET& other) SFAD_NOEXCEPT = default; - - inline const idx_t gradientSize() const SFAD_NOEXCEPT { return detail::globalGradSize; } - - template friend void swap (FwdET& x, FwdET& y) SFAD_NOEXCEPT; - - // ADOL-C compatibility - - inline real_t getValue() SFAD_NOEXCEPT { return _val; } - inline const real_t getValue() const SFAD_NOEXCEPT { return _val; } - inline void setValue(const real_t v) SFAD_NOEXCEPT { _val = v; } - - inline real_t getADValue(const idx_t idx) { return _grad[idx]; } - inline const real_t getADValue(const idx_t idx) const { return _grad[idx]; } - inline void setADValue(const idx_t idx, const real_t v) { _grad[idx] = v; } - inline void setADValue(const real_t v) - { - fillADValue(v); - } - - inline void fillADValue(const real_t v) - { - fillADValue(0, detail::globalGradSize, v); - } - inline void fillADValue(const idx_t start, const real_t v) - { - fillADValue(start, detail::globalGradSize, v); - } - inline void fillADValue(const idx_t start, const idx_t end, const real_t v) - { - std::fill(_grad + start, _grad + end, v); - } - - // Modern C++ accessor - - inline real_t& operator[](const idx_t idx) { return _grad[idx]; } - inline const real_t operator[](const idx_t idx) const { return _grad[idx]; } - - // Explicit cast operator to underlying scalar type - explicit operator real_t() const SFAD_NOEXCEPT { return _val; } - - // Assignment - inline FwdET& operator=(const real_t v) - { - _val = v; - setADValue(real_t(0)); - - return *this; - } - - // Contains the one (and only) loop in expression template paradigm - template - inline FwdET& operator=(const Expr& v) - { - _val = v.value(); - for (idx_t i = 0; i < detail::globalGradSize; ++i) - _grad[i] = v.gradient(i); - - return *this; - } - - // Expr class interface - const real_t value() const SFAD_NOEXCEPT { return _val; } - const real_t gradient(std::size_t idx) const { return _grad[idx]; } - - - // AssignOp Operators, i.e., +=, -=, *=, /= - - // Addition - inline FwdET& operator+=(const real_t v) - { - _val += v; - return *this; - } - - inline FwdET& operator+=(const FwdET& a) - { - _val += a._val; - for (idx_t i = 0; i < detail::globalGradSize; ++i) - _grad[i] += a._grad[i]; - - return *this; - } - - // Substraction - inline FwdET& operator-=(const real_t v) - { - _val -= v; - return *this; - } - - inline FwdET& operator-=(const FwdET& a) - { - _val -= a._val; - for (idx_t i = 0; i < detail::globalGradSize; ++i) - _grad[i] -= a._grad[i]; - - return *this; - } - - // Multiplication - inline FwdET& operator*=(const real_t v) - { - _val *= v; - for (idx_t i = 0; i < detail::globalGradSize; ++i) - _grad[i] *= v; - return *this; - } - - inline FwdET& operator*=(const FwdET& a) - { - for (idx_t i = 0; i < detail::globalGradSize; ++i) - _grad[i] = a._val * _grad[i] + _val * a._grad[i]; - - _val *= a._val; - return *this; - } - - // Division - inline FwdET& operator/=(const real_t v) - { - _val /= v; - for (idx_t i = 0; i < detail::globalGradSize; ++i) - _grad[i] /= v; - return *this; - } - - inline FwdET& operator/=(const FwdET& a) - { - for (idx_t i = 0; i < detail::globalGradSize; ++i) -// _grad[i] = (_grad[i] - _val / a._val * a._grad[i]) / a._val; - _grad[i] = (_grad[i] * a._val - _val * a._grad[i]) / (a._val * a._val); +namespace detail +{ +template real_t sqr(const real_t& x) SFAD_NOEXCEPT +{ + return x * x; +} +} // namespace detail - _val /= a._val; - return *this; - } - - protected: - real_t _val; - real_t _grad[SFAD_DEFAULT_DIR]; - }; - - // Basic arithmetics +// Base class for all expression type trees +template class Expr +{ +public: + inline const T& base() const SFAD_NOEXCEPT + { + return static_cast(*this); + } + inline const real_t value() const SFAD_NOEXCEPT + { + return base().value(); + } + inline const real_t gradient(std::size_t idx) const + { + return base().gradient(idx); + } - template - class Add : public Expr, real_t> +private: + // Cannot assign to expression + Expr& operator=(const Expr&) { - public: - Add(const Expr& a, const Expr& b) SFAD_NOEXCEPT : _a(a.base()), _b(b.base()) { } + return *this; + } +}; - inline const real_t value() const SFAD_NOEXCEPT { return _a.value() + _b.value(); } - inline const real_t gradient(std::size_t idx) const { return _a.gradient(idx) + _b.gradient(idx); } +// Main data type that actually holds the derivative vector +template class FwdET : public Expr, real_t> +{ +public: + typedef std::size_t idx_t; - protected: - const A& _a; - const B& _b; - }; + FwdET(const real_t val) SFAD_NOEXCEPT : _val(0) + { + setADValue(real_t(0)); + } + FwdET(const real_t val) SFAD_NOEXCEPT : _val(val) + { + setADValue(real_t(0)); + } + FwdET(const real_t val, real_t const* const grad) SFAD_NOEXCEPT : _val(val) + { + std::copy_n(grad, detail::globalGradSize, _grad); + } + FwdET(const FwdET& cpy) SFAD_NOEXCEPT = default; + FwdET(FwdET&& other) SFAD_NOEXCEPT = default; + + // Contains the one (and only) loop in expression template paradigm + template FwdET(const Expr& other) SFAD_NOEXCEPT : _val(other.value()) + { + for (idx_t i = 0; i < detail::globalGradSize; ++i) + _grad[i] = other.gradient(i); + } + + ~FwdET() SFAD_NOEXCEPT = default; - template - inline Add operator+(const Expr& a, const Expr& b) SFAD_NOEXCEPT { return Add(a.base(), b.base()); } + FwdET& operator=(FwdET&& other) SFAD_NOEXCEPT = default; + FwdET& operator=(const FwdET& other) SFAD_NOEXCEPT = default; + inline const idx_t gradientSize() const SFAD_NOEXCEPT + { + return detail::globalGradSize; + } + + template friend void swap(FwdET& x, FwdET& y) SFAD_NOEXCEPT; + + // ADOL-C compatibility - template - class Subtract : public Expr, real_t> + inline real_t getValue() SFAD_NOEXCEPT + { + return _val; + } + inline const real_t getValue() const SFAD_NOEXCEPT + { + return _val; + } + inline void setValue(const real_t v) SFAD_NOEXCEPT { - public: - Subtract(const Expr& a, const Expr& b) SFAD_NOEXCEPT : _a(a.base()), _b(b.base()) { } + _val = v; + } - inline const real_t value() const SFAD_NOEXCEPT { return _a.value() - _b.value(); } - inline const real_t gradient(std::size_t idx) const { return _a.gradient(idx) - _b.gradient(idx); } + inline real_t getADValue(const idx_t idx) + { + return _grad[idx]; + } + inline const real_t getADValue(const idx_t idx) const + { + return _grad[idx]; + } + inline void setADValue(const idx_t idx, const real_t v) + { + _grad[idx] = v; + } + inline void setADValue(const real_t v) + { + fillADValue(v); + } - protected: - const A& _a; - const B& _b; - }; + inline void fillADValue(const real_t v) + { + fillADValue(0, detail::globalGradSize, v); + } + inline void fillADValue(const idx_t start, const real_t v) + { + fillADValue(start, detail::globalGradSize, v); + } + inline void fillADValue(const idx_t start, const idx_t end, const real_t v) + { + std::fill(_grad + start, _grad + end, v); + } - template - inline Subtract operator-(const Expr& a, const Expr& b) SFAD_NOEXCEPT { return Subtract(a.base(), b.base()); } + // Modern C++ accessor + inline real_t& operator[](const idx_t idx) + { + return _grad[idx]; + } + inline const real_t operator[](const idx_t idx) const + { + return _grad[idx]; + } - template - class Multiply : public Expr, real_t> + // Explicit cast operator to underlying scalar type + explicit operator real_t() const SFAD_NOEXCEPT { - public: - Multiply(const Expr& a, const Expr& b) SFAD_NOEXCEPT : _a(a.base()), _b(b.base()), _cacheA(_a.value()), _cacheB(_b.value()) { } + return _val; + } -// inline const real_t value() const SFAD_NOEXCEPT { return _a.value() * _b.value(); } -// inline const real_t gradient(std::size_t idx) const { return _a.gradient(idx) * _b.value() + _a.value() * _b.gradient(idx); } - inline const real_t value() const SFAD_NOEXCEPT { return _cacheA * _cacheB; } - inline const real_t gradient(std::size_t idx) const { return _a.gradient(idx) * _cacheB + _cacheA * _b.gradient(idx); } + // Assignment + inline FwdET& operator=(const real_t v) + { + _val = v; + setADValue(real_t(0)); - protected: - const A& _a; - const B& _b; - const real_t _cacheA; - const real_t _cacheB; - }; + return *this; + } - template - inline Multiply operator*(const Expr& a, const Expr& b) SFAD_NOEXCEPT { return Multiply(a.base(), b.base()); } + // Contains the one (and only) loop in expression template paradigm + template inline FwdET& operator=(const Expr& v) + { + _val = v.value(); + for (idx_t i = 0; i < detail::globalGradSize; ++i) + _grad[i] = v.gradient(i); + return *this; + } - template - class Divide : public Expr, real_t> + // Expr class interface + const real_t value() const SFAD_NOEXCEPT { - public: - Divide(const Expr& a, const Expr& b) SFAD_NOEXCEPT : _a(a.base()), _b(b.base()), _cacheA(_a.value()), _cacheB(_b.value()) { } + return _val; + } + const real_t gradient(std::size_t idx) const + { + return _grad[idx]; + } -// inline const real_t value() const SFAD_NOEXCEPT { return _a.value() / _b.value(); } -// inline const real_t gradient(std::size_t idx) const { return (_a.gradient(idx) * _b.value() - _a.value() * _b.gradient(idx)) / (_b.value() * _b.value()); } - inline const real_t value() const SFAD_NOEXCEPT { return _cacheA / _cacheB; } - inline const real_t gradient(std::size_t idx) const { return (_a.gradient(idx) * _cacheB - _cacheA * _b.gradient(idx)) / (_cacheB * _cacheB); } + // AssignOp Operators, i.e., +=, -=, *=, /= - protected: - const A& _a; - const B& _b; - const real_t _cacheA; - const real_t _cacheB; - }; + // Addition + inline FwdET& operator+=(const real_t v) + { + _val += v; + return *this; + } - template - inline Divide operator/(const Expr& a, const Expr& b) SFAD_NOEXCEPT { return Divide(a.base(), b.base()); } + inline FwdET& operator+=(const FwdET& a) + { + _val += a._val; + for (idx_t i = 0; i < detail::globalGradSize; ++i) + _grad[i] += a._grad[i]; + return *this; + } - template - class ScalarAdd : public Expr, real_t> + // Substraction + inline FwdET& operator-=(const real_t v) { - public: - ScalarAdd(const Expr& a, const real_t b) SFAD_NOEXCEPT : _a(a.base()), _b(b) { } + _val -= v; + return *this; + } - inline const real_t value() const SFAD_NOEXCEPT { return _a.value() + _b; } - inline const real_t gradient(std::size_t idx) const { return _a.gradient(idx); } + inline FwdET& operator-=(const FwdET& a) + { + _val -= a._val; + for (idx_t i = 0; i < detail::globalGradSize; ++i) + _grad[i] -= a._grad[i]; - protected: - const A& _a; - const real_t _b; - }; + return *this; + } - template - inline ScalarAdd operator+(const Expr& a, const real_t b) SFAD_NOEXCEPT { return ScalarAdd(a.base(), b); } + // Multiplication + inline FwdET& operator*=(const real_t v) + { + _val *= v; + for (idx_t i = 0; i < detail::globalGradSize; ++i) + _grad[i] *= v; + return *this; + } - template - inline ScalarAdd operator+(const real_t a, const Expr& b) SFAD_NOEXCEPT { return ScalarAdd(b.base(), a); } + inline FwdET& operator*=(const FwdET& a) + { + for (idx_t i = 0; i < detail::globalGradSize; ++i) + _grad[i] = a._val * _grad[i] + _val * a._grad[i]; - template - inline ScalarAdd operator-(const Expr& a, const real_t b) SFAD_NOEXCEPT { return ScalarAdd(a.base(), -b); } + _val *= a._val; + return *this; + } + // Division + inline FwdET& operator/=(const real_t v) + { + _val /= v; + for (idx_t i = 0; i < detail::globalGradSize; ++i) + _grad[i] /= v; + return *this; + } - template - class ScalarSubtract : public Expr, real_t> + inline FwdET& operator/=(const FwdET& a) { - public: - ScalarSubtract(const real_t a, const Expr& b) SFAD_NOEXCEPT : _a(a), _b(b.base()) { } + for (idx_t i = 0; i < detail::globalGradSize; ++i) + // _grad[i] = (_grad[i] - _val / a._val * a._grad[i]) / a._val; + _grad[i] = (_grad[i] * a._val - _val * a._grad[i]) / (a._val * a._val); - inline const real_t value() const SFAD_NOEXCEPT { return _a - _b.value(); } - inline const real_t gradient(std::size_t idx) const { return -_b.gradient(idx); } + _val /= a._val; + return *this; + } - protected: - const real_t _a; - const A& _b; - }; +protected: + real_t _val; + real_t _grad[SFAD_DEFAULT_DIR]; +}; - template - inline ScalarSubtract operator-(const real_t a, const Expr& b) SFAD_NOEXCEPT { return ScalarSubtract(a, b.base()); } +// Basic arithmetics +template class Add : public Expr, real_t> +{ +public: + Add(const Expr& a, const Expr& b) SFAD_NOEXCEPT : _a(a.base()), _b(b.base()) + { + } - template - class ScalarMultiply : public Expr, real_t> + inline const real_t value() const SFAD_NOEXCEPT + { + return _a.value() + _b.value(); + } + inline const real_t gradient(std::size_t idx) const { - public: - ScalarMultiply(const Expr& a, const real_t b) SFAD_NOEXCEPT : _a(a.base()), _b(b) { } + return _a.gradient(idx) + _b.gradient(idx); + } - inline const real_t value() const SFAD_NOEXCEPT { return _a.value() * _b; } - inline const real_t gradient(std::size_t idx) const { return _b * _a.gradient(idx); } +protected: + const A& _a; + const B& _b; +}; + +template +inline Add operator+(const Expr& a, const Expr& b) SFAD_NOEXCEPT +{ + return Add(a.base(), b.base()); +} - protected: - const A& _a; - const real_t _b; - }; +template class Subtract : public Expr, real_t> +{ +public: + Subtract(const Expr& a, const Expr& b) SFAD_NOEXCEPT : _a(a.base()), _b(b.base()) + { + } - template - inline ScalarMultiply operator*(const Expr& a, const real_t b) SFAD_NOEXCEPT { return ScalarMultiply(a.base(), b); } + inline const real_t value() const SFAD_NOEXCEPT + { + return _a.value() - _b.value(); + } + inline const real_t gradient(std::size_t idx) const + { + return _a.gradient(idx) - _b.gradient(idx); + } - template - inline ScalarMultiply operator*(const real_t a, const Expr& b) SFAD_NOEXCEPT { return ScalarMultiply(b.base(), a); } +protected: + const A& _a; + const B& _b; +}; +template +inline Subtract operator-(const Expr& a, const Expr& b) SFAD_NOEXCEPT +{ + return Subtract(a.base(), b.base()); +} - template - class DivideScalar : public Expr, real_t> +template class Multiply : public Expr, real_t> +{ +public: + Multiply(const Expr& a, const Expr& b) SFAD_NOEXCEPT : _a(a.base()), + _b(b.base()), + _cacheA(_a.value()), + _cacheB(_b.value()) { - public: - DivideScalar(const Expr& a, const real_t b) SFAD_NOEXCEPT : _a(a.base()), _b(b) { } + } - inline const real_t value() const SFAD_NOEXCEPT { return _a.value() / _b; } - inline const real_t gradient(std::size_t idx) const { return _a.gradient(idx) / _b; } + // inline const real_t value() const SFAD_NOEXCEPT { return _a.value() * _b.value(); } + // inline const real_t gradient(std::size_t idx) const { return _a.gradient(idx) * _b.value() + _a.value() * + //_b.gradient(idx); } + inline const real_t value() const SFAD_NOEXCEPT + { + return _cacheA * _cacheB; + } + inline const real_t gradient(std::size_t idx) const + { + return _a.gradient(idx) * _cacheB + _cacheA * _b.gradient(idx); + } - protected: - const A& _a; - const real_t _b; - }; +protected: + const A& _a; + const B& _b; + const real_t _cacheA; + const real_t _cacheB; +}; - template - inline DivideScalar operator/(const Expr& a, const real_t b) SFAD_NOEXCEPT { return DivideScalar(a.base(), b); } +template +inline Multiply operator*(const Expr& a, const Expr& b) SFAD_NOEXCEPT +{ + return Multiply(a.base(), b.base()); +} +template class Divide : public Expr, real_t> +{ +public: + Divide(const Expr& a, const Expr& b) SFAD_NOEXCEPT : _a(a.base()), + _b(b.base()), + _cacheA(_a.value()), + _cacheB(_b.value()) + { + } - template - class ScalarDivide : public Expr, real_t> + // inline const real_t value() const SFAD_NOEXCEPT { return _a.value() / _b.value(); } + // inline const real_t gradient(std::size_t idx) const { return (_a.gradient(idx) * _b.value() - _a.value() * + //_b.gradient(idx)) / (_b.value() * _b.value()); } + inline const real_t value() const SFAD_NOEXCEPT + { + return _cacheA / _cacheB; + } + inline const real_t gradient(std::size_t idx) const { - public: - ScalarDivide(const real_t a, const Expr& b) SFAD_NOEXCEPT : _a(a), _b(b.base()), _cache(_b.value()) { } + return (_a.gradient(idx) * _cacheB - _cacheA * _b.gradient(idx)) / (_cacheB * _cacheB); + } + +protected: + const A& _a; + const B& _b; + const real_t _cacheA; + const real_t _cacheB; +}; - inline const real_t value() const SFAD_NOEXCEPT { return _a / _b.value(); } - inline const real_t gradient(std::size_t idx) const { return -_a * _b.gradient(idx) / (_cache * _cache); } +template +inline Divide operator/(const Expr& a, const Expr& b) SFAD_NOEXCEPT +{ + return Divide(a.base(), b.base()); +} + +template class ScalarAdd : public Expr, real_t> +{ +public: + ScalarAdd(const Expr& a, const real_t b) SFAD_NOEXCEPT : _a(a.base()), _b(b) + { + } - protected: - const real_t _a; - const A& _b; - const real_t _cache; - }; + inline const real_t value() const SFAD_NOEXCEPT + { + return _a.value() + _b; + } + inline const real_t gradient(std::size_t idx) const + { + return _a.gradient(idx); + } - template - inline ScalarDivide operator/(const real_t a, const Expr& b) SFAD_NOEXCEPT { return ScalarDivide(a, b.base()); } +protected: + const A& _a; + const real_t _b; +}; +template +inline ScalarAdd operator+(const Expr& a, const real_t b) SFAD_NOEXCEPT +{ + return ScalarAdd(a.base(), b); +} - template - class UnaryMinus : public Expr, real_t> +template +inline ScalarAdd operator+(const real_t a, const Expr& b) SFAD_NOEXCEPT +{ + return ScalarAdd(b.base(), a); +} + +template +inline ScalarAdd operator-(const Expr& a, const real_t b) SFAD_NOEXCEPT +{ + return ScalarAdd(a.base(), -b); +} + +template class ScalarSubtract : public Expr, real_t> +{ +public: + ScalarSubtract(const real_t a, const Expr& b) SFAD_NOEXCEPT : _a(a), _b(b.base()) { - public: - UnaryMinus(const Expr& a) SFAD_NOEXCEPT : _a(a.base()) { } + } - inline const real_t value() const SFAD_NOEXCEPT { return -_a.value(); } - inline const real_t gradient(std::size_t idx) const { return -_a.gradient(idx); } + inline const real_t value() const SFAD_NOEXCEPT + { + return _a - _b.value(); + } + inline const real_t gradient(std::size_t idx) const + { + return -_b.gradient(idx); + } - protected: - const A& _a; - }; +protected: + const real_t _a; + const A& _b; +}; - template - inline UnaryMinus operator-(const Expr& a) SFAD_NOEXCEPT { return UnaryMinus(a.base()); } +template +inline ScalarSubtract operator-(const real_t a, const Expr& b) SFAD_NOEXCEPT +{ + return ScalarSubtract(a, b.base()); +} - template - inline A operator+(const Expr& a) SFAD_NOEXCEPT { return a.base(); } +template class ScalarMultiply : public Expr, real_t> +{ +public: + ScalarMultiply(const Expr& a, const real_t b) SFAD_NOEXCEPT : _a(a.base()), _b(b) + { + } + + inline const real_t value() const SFAD_NOEXCEPT + { + return _a.value() * _b; + } + inline const real_t gradient(std::size_t idx) const + { + return _b * _a.gradient(idx); + } +protected: + const A& _a; + const real_t _b; +}; - // Conditionals +template +inline ScalarMultiply operator*(const Expr& a, const real_t b) SFAD_NOEXCEPT +{ + return ScalarMultiply(a.base(), b); +} + +template +inline ScalarMultiply operator*(const real_t a, const Expr& b) SFAD_NOEXCEPT +{ + return ScalarMultiply(b.base(), a); +} + +template class DivideScalar : public Expr, real_t> +{ +public: + DivideScalar(const Expr& a, const real_t b) SFAD_NOEXCEPT : _a(a.base()), _b(b) + { + } - #define SETFAD_CONDITIONAL_ET_FUNC(OPNAME, OPERATOR) \ - template \ - inline bool OPNAME(const Expr& a, const Expr& b) SFAD_NOEXCEPT \ - { return a.value() OPERATOR b.value(); } \ - \ - template \ - inline bool OPNAME(const Expr& a, const real_t b) SFAD_NOEXCEPT \ - { return a.value() OPERATOR b; } \ - \ - template \ - inline bool OPNAME(real_t a, const Expr& b) SFAD_NOEXCEPT \ - { return a OPERATOR b.value(); } + inline const real_t value() const SFAD_NOEXCEPT + { + return _a.value() / _b; + } + inline const real_t gradient(std::size_t idx) const + { + return _a.gradient(idx) / _b; + } - SETFAD_CONDITIONAL_ET_FUNC(operator==, ==) - SETFAD_CONDITIONAL_ET_FUNC(operator!=, !=) - SETFAD_CONDITIONAL_ET_FUNC(operator>, >) - SETFAD_CONDITIONAL_ET_FUNC(operator<, <) - SETFAD_CONDITIONAL_ET_FUNC(operator>=, >=) - SETFAD_CONDITIONAL_ET_FUNC(operator<=, <=) +protected: + const A& _a; + const real_t _b; +}; +template +inline DivideScalar operator/(const Expr& a, const real_t b) SFAD_NOEXCEPT +{ + return DivideScalar(a.base(), b); +} - // Unary math functions +template class ScalarDivide : public Expr, real_t> +{ +public: + ScalarDivide(const real_t a, const Expr& b) SFAD_NOEXCEPT : _a(a), _b(b.base()), _cache(_b.value()) + { + } - template - class Exp : public Expr, real_t> + inline const real_t value() const SFAD_NOEXCEPT { - public: - Exp(const Expr& a) SFAD_NOEXCEPT : _a(a.base()), _cache(std::exp(_a.value())) { } + return _a / _b.value(); + } + inline const real_t gradient(std::size_t idx) const + { + return -_a * _b.gradient(idx) / (_cache * _cache); + } - inline const real_t value() const SFAD_NOEXCEPT { return _cache; } - inline const real_t gradient(std::size_t idx) const { return _cache * _a.gradient(idx); } +protected: + const real_t _a; + const A& _b; + const real_t _cache; +}; - protected: - const A& _a; - const real_t _cache; - }; +template +inline ScalarDivide operator/(const real_t a, const Expr& b) SFAD_NOEXCEPT +{ + return ScalarDivide(a, b.base()); +} - template - inline Exp exp(const Expr& a) SFAD_NOEXCEPT { return Exp(a.base()); } +template class UnaryMinus : public Expr, real_t> +{ +public: + UnaryMinus(const Expr& a) SFAD_NOEXCEPT : _a(a.base()) + { + } + + inline const real_t value() const SFAD_NOEXCEPT + { + return -_a.value(); + } + inline const real_t gradient(std::size_t idx) const + { + return -_a.gradient(idx); + } - #define SETFAD_UNARY_FUNC_ET_CLASS(CLASSNAME, VALOP, CACHEOP, FUNCDIFF, FUNCNAME) \ - template \ - class CLASSNAME : public Expr, real_t> \ - { \ - public: \ - CLASSNAME(const Expr& a) SFAD_NOEXCEPT : _a(a.base()), _cache(CACHEOP) { } \ - \ - inline const real_t value() const SFAD_NOEXCEPT { return VALOP; } \ - inline const real_t gradient(std::size_t idx) const { return FUNCDIFF; } \ - \ - protected: \ - const A& _a; \ - const real_t _cache; \ - }; \ - \ - template \ - inline CLASSNAME FUNCNAME(const Expr& a) SFAD_NOEXCEPT { return CLASSNAME(a.base()); } +protected: + const A& _a; +}; - SETFAD_UNARY_FUNC_ET_CLASS(Log, std::log(_cache), _a.value(), _a.gradient(idx) / _cache, log) - SETFAD_UNARY_FUNC_ET_CLASS(Square, detail::sqr(_cache), _a.value(), _a.gradient(idx) * real_t(2) * _cache, sqr) - SETFAD_UNARY_FUNC_ET_CLASS(Sqrt, _cache, std::sqrt(_a.value()), _a.gradient(idx) / (real_t(2) * _cache), sqrt) - - SETFAD_UNARY_FUNC_ET_CLASS(Sin, std::sin(_a.value()), std::cos(_a.value()), _a.gradient(idx) * _cache, sin) - SETFAD_UNARY_FUNC_ET_CLASS(Cos, std::cos(_a.value()), -std::sin(_a.value()), _a.gradient(idx) * _cache, cos) - SETFAD_UNARY_FUNC_ET_CLASS(Tan, std::tan(_a.value()), detail::sqr(std::cos(_a.value())), _a.gradient(idx) / _cache, tan) - - SETFAD_UNARY_FUNC_ET_CLASS(Asin, std::asin(_a.value()), std::sqrt(real_t(1) - detail::sqr(_a.value())), _a.gradient(idx) / _cache, asin) - SETFAD_UNARY_FUNC_ET_CLASS(Acos, std::acos(_a.value()), -std::sqrt(real_t(1) - detail::sqr(_a.value())), _a.gradient(idx) / _cache, acos) - SETFAD_UNARY_FUNC_ET_CLASS(Atan, std::atan(_a.value()), real_t(1) + detail::sqr(_a.value()), _a.gradient(idx) / _cache, atan) - - SETFAD_UNARY_FUNC_ET_CLASS(Sinh, std::sinh(_a.value()), std::cosh(_a.value()), _a.gradient(idx) * _cache, sinh) - SETFAD_UNARY_FUNC_ET_CLASS(Cosh, std::cosh(_a.value()), std::sinh(_a.value()), _a.gradient(idx) * _cache, cosh) - SETFAD_UNARY_FUNC_ET_CLASS(Tanh, _cache, std::tanh(_a.value()), _a.gradient(idx) * (real_t(1) - _cache) * (real_t(1) + _cache), tanh) - - SETFAD_UNARY_FUNC_ET_CLASS(Asinh, std::asinh(_a.value()), std::sqrt(real_t(1) + detail::sqr(_a.value())), _a.gradient(idx) / _cache, asinh) - SETFAD_UNARY_FUNC_ET_CLASS(Acosh, std::acosh(_a.value()), std::sqrt(detail::sqr(_a.value()) - real_t(1)), _a.gradient(idx) / _cache, acosh) - SETFAD_UNARY_FUNC_ET_CLASS(Atanh, std::atanh(_a.value()), real_t(1) - detail::sqr(_a.value()), _a.gradient(idx) / _cache, atanh) - - SETFAD_UNARY_FUNC_ET_CLASS(Abs, std::abs(_a.value()), _a.value() > real_t(0) ? real_t(1) : real_t(-1), _a.gradient(idx) * _cache, abs) - - template - inline Abs fabs(const Expr& a) SFAD_NOEXCEPT { return Abs(a.base()); } - - // Unary functions that reset gradient to zero - - #define SETFAD_UNARY_FUNC_ET_CLASS_RESET(CLASSNAME, FUNCAPPLY, FUNCNAME) \ - template \ - class CLASSNAME : public Expr, real_t> \ - { \ - public: \ - CLASSNAME(const Expr& a) SFAD_NOEXCEPT : _a(a.base()) { } \ - \ - inline const real_t value() const SFAD_NOEXCEPT { return FUNCAPPLY(_a.value()); } \ - inline const real_t gradient(std::size_t idx) const { return real_t(0); } \ - \ - protected: \ - const A& _a; \ - }; \ - \ - template \ - inline CLASSNAME FUNCNAME(const Expr& a) SFAD_NOEXCEPT { return CLASSNAME(a.base()); } - - SETFAD_UNARY_FUNC_ET_CLASS_RESET(Ceil, std::ceil, ceil) - SETFAD_UNARY_FUNC_ET_CLASS_RESET(Floor, std::floor, floor) - - // Binary math functions - - template - class Pow : public Expr, real_t> - { - public: - Pow(const Expr& a, const Expr& b) SFAD_NOEXCEPT : _a(a.base()), _b(b.base()), - _cacheA(_a.value()), _cacheB(_b.value()), - _cacheC(_cacheB * std::pow(_cacheA, _cacheB - real_t(1))), _cacheD(std::pow(_cacheA, _cacheB) * std::log(_cacheA)) - { } - - inline const real_t value() const SFAD_NOEXCEPT { return std::pow(_cacheA, _cacheB); } - inline const real_t gradient(std::size_t idx) const { return _a.gradient(idx) * _cacheC + _b.gradient(idx) * _cacheD; } - - protected: - const A& _a; - const B& _b; - const real_t _cacheA; - const real_t _cacheB; - const real_t _cacheC; - const real_t _cacheD; - }; - - template - inline Pow pow(const Expr& a, const Expr& b) SFAD_NOEXCEPT { return Pow(a.base(), b.base()); } - - - template - class PowScalar : public Expr, real_t> - { - public: - PowScalar(const Expr& a, const real_t b) SFAD_NOEXCEPT : _a(a.base()), _b(b), _cacheA(_a.value()), - _cacheB(_b * std::pow(_cacheA, _b - real_t(1))) - { } - - inline const real_t value() const SFAD_NOEXCEPT { return std::pow(_cacheA, _b); } - inline const real_t gradient(std::size_t idx) const { return _a.gradient(idx) * _cacheB; } - - protected: - const A& _a; - const real_t _b; - const real_t _cacheA; - const real_t _cacheB; - }; - - template - inline PowScalar pow(const Expr& a, const real_t b) SFAD_NOEXCEPT { return PowScalar(a.base(), b); } +template inline UnaryMinus operator-(const Expr& a) SFAD_NOEXCEPT +{ + return UnaryMinus(a.base()); +} +template inline A operator+(const Expr& a) SFAD_NOEXCEPT +{ + return a.base(); +} - template - class ScalarPow : public Expr, real_t> +// Conditionals + +#define SETFAD_CONDITIONAL_ET_FUNC(OPNAME, OPERATOR) \ + template \ + inline bool OPNAME(const Expr& a, const Expr& b) SFAD_NOEXCEPT \ + { \ + return a.value() OPERATOR b.value(); \ + } \ + \ + template inline bool OPNAME(const Expr& a, const real_t b) SFAD_NOEXCEPT \ + { \ + return a.value() OPERATOR b; \ + } \ + \ + template inline bool OPNAME(real_t a, const Expr& b) SFAD_NOEXCEPT \ + { \ + return a OPERATOR b.value(); \ + } + +SETFAD_CONDITIONAL_ET_FUNC(operator==, ==) +SETFAD_CONDITIONAL_ET_FUNC(operator!=, !=) +SETFAD_CONDITIONAL_ET_FUNC(operator>, >) +SETFAD_CONDITIONAL_ET_FUNC(operator<, <) +SETFAD_CONDITIONAL_ET_FUNC(operator>=, >=) +SETFAD_CONDITIONAL_ET_FUNC(operator<=, <=) + +// Unary math functions + +template class Exp : public Expr, real_t> +{ +public: + Exp(const Expr& a) SFAD_NOEXCEPT : _a(a.base()), _cache(std::exp(_a.value())) { - public: - ScalarPow(const real_t a, const Expr& b) SFAD_NOEXCEPT : _a(a), _b(b.base()), _cacheA(_b.value()), - _cacheB(std::pow(_a, _cacheA) * std::log(_a)) - { } + } - inline const real_t value() const SFAD_NOEXCEPT { return std::pow(_a, _b.value()); } - inline const real_t gradient(std::size_t idx) const { return _b.gradient(idx) * _cacheB; } + inline const real_t value() const SFAD_NOEXCEPT + { + return _cache; + } + inline const real_t gradient(std::size_t idx) const + { + return _cache * _a.gradient(idx); + } - protected: - const real_t _a; - const A& _b; - const real_t _cacheA; - const real_t _cacheB; - }; +protected: + const A& _a; + const real_t _cache; +}; - template - inline ScalarPow pow(const real_t a, const Expr& b) SFAD_NOEXCEPT { return ScalarPow(a, b.base()); } +template inline Exp exp(const Expr& a) SFAD_NOEXCEPT +{ + return Exp(a.base()); +} - template - void swap(FwdET& x, FwdET& y) SFAD_NOEXCEPT +#define SETFAD_UNARY_FUNC_ET_CLASS(CLASSNAME, VALOP, CACHEOP, FUNCDIFF, FUNCNAME) \ + template class CLASSNAME : public Expr, real_t> \ + { \ + public: \ + CLASSNAME(const Expr& a) SFAD_NOEXCEPT : _a(a.base()), _cache(CACHEOP) \ + { \ + } \ + \ + inline const real_t value() const SFAD_NOEXCEPT \ + { \ + return VALOP; \ + } \ + inline const real_t gradient(std::size_t idx) const \ + { \ + return FUNCDIFF; \ + } \ + \ + protected: \ + const A& _a; \ + const real_t _cache; \ + }; \ + \ + template inline CLASSNAME FUNCNAME(const Expr& a) SFAD_NOEXCEPT \ + { \ + return CLASSNAME(a.base()); \ + } + +SETFAD_UNARY_FUNC_ET_CLASS(Log, std::log(_cache), _a.value(), _a.gradient(idx) / _cache, log) +SETFAD_UNARY_FUNC_ET_CLASS(Square, detail::sqr(_cache), _a.value(), _a.gradient(idx) * real_t(2) * _cache, sqr) +SETFAD_UNARY_FUNC_ET_CLASS(Sqrt, _cache, std::sqrt(_a.value()), _a.gradient(idx) / (real_t(2) * _cache), sqrt) + +SETFAD_UNARY_FUNC_ET_CLASS(Sin, std::sin(_a.value()), std::cos(_a.value()), _a.gradient(idx) * _cache, sin) +SETFAD_UNARY_FUNC_ET_CLASS(Cos, std::cos(_a.value()), -std::sin(_a.value()), _a.gradient(idx) * _cache, cos) +SETFAD_UNARY_FUNC_ET_CLASS(Tan, std::tan(_a.value()), detail::sqr(std::cos(_a.value())), _a.gradient(idx) / _cache, tan) + +SETFAD_UNARY_FUNC_ET_CLASS(Asin, std::asin(_a.value()), std::sqrt(real_t(1) - detail::sqr(_a.value())), + _a.gradient(idx) / _cache, asin) +SETFAD_UNARY_FUNC_ET_CLASS(Acos, std::acos(_a.value()), -std::sqrt(real_t(1) - detail::sqr(_a.value())), + _a.gradient(idx) / _cache, acos) +SETFAD_UNARY_FUNC_ET_CLASS(Atan, std::atan(_a.value()), real_t(1) + detail::sqr(_a.value()), _a.gradient(idx) / _cache, + atan) + +SETFAD_UNARY_FUNC_ET_CLASS(Sinh, std::sinh(_a.value()), std::cosh(_a.value()), _a.gradient(idx) * _cache, sinh) +SETFAD_UNARY_FUNC_ET_CLASS(Cosh, std::cosh(_a.value()), std::sinh(_a.value()), _a.gradient(idx) * _cache, cosh) +SETFAD_UNARY_FUNC_ET_CLASS(Tanh, _cache, std::tanh(_a.value()), + _a.gradient(idx) * (real_t(1) - _cache) * (real_t(1) + _cache), tanh) + +SETFAD_UNARY_FUNC_ET_CLASS(Asinh, std::asinh(_a.value()), std::sqrt(real_t(1) + detail::sqr(_a.value())), + _a.gradient(idx) / _cache, asinh) +SETFAD_UNARY_FUNC_ET_CLASS(Acosh, std::acosh(_a.value()), std::sqrt(detail::sqr(_a.value()) - real_t(1)), + _a.gradient(idx) / _cache, acosh) +SETFAD_UNARY_FUNC_ET_CLASS(Atanh, std::atanh(_a.value()), real_t(1) - detail::sqr(_a.value()), + _a.gradient(idx) / _cache, atanh) + +SETFAD_UNARY_FUNC_ET_CLASS(Abs, std::abs(_a.value()), _a.value() > real_t(0) ? real_t(1) : real_t(-1), + _a.gradient(idx) * _cache, abs) + +template inline Abs fabs(const Expr& a) SFAD_NOEXCEPT +{ + return Abs(a.base()); +} + +// Unary functions that reset gradient to zero + +#define SETFAD_UNARY_FUNC_ET_CLASS_RESET(CLASSNAME, FUNCAPPLY, FUNCNAME) \ + template class CLASSNAME : public Expr, real_t> \ + { \ + public: \ + CLASSNAME(const Expr& a) SFAD_NOEXCEPT : _a(a.base()) \ + { \ + } \ + \ + inline const real_t value() const SFAD_NOEXCEPT \ + { \ + return FUNCAPPLY(_a.value()); \ + } \ + inline const real_t gradient(std::size_t idx) const \ + { \ + return real_t(0); \ + } \ + \ + protected: \ + const A& _a; \ + }; \ + \ + template inline CLASSNAME FUNCNAME(const Expr& a) SFAD_NOEXCEPT \ + { \ + return CLASSNAME(a.base()); \ + } + +SETFAD_UNARY_FUNC_ET_CLASS_RESET(Ceil, std::ceil, ceil) +SETFAD_UNARY_FUNC_ET_CLASS_RESET(Floor, std::floor, floor) + +// Binary math functions + +template class Pow : public Expr, real_t> +{ +public: + Pow(const Expr& a, const Expr& b) SFAD_NOEXCEPT + : _a(a.base()), + _b(b.base()), + _cacheA(_a.value()), + _cacheB(_b.value()), + _cacheC(_cacheB* std::pow(_cacheA, _cacheB - real_t(1))), + _cacheD(std::pow(_cacheA, _cacheB) * std::log(_cacheA)) + { + } + + inline const real_t value() const SFAD_NOEXCEPT + { + return std::pow(_cacheA, _cacheB); + } + inline const real_t gradient(std::size_t idx) const { - using std::swap; - swap(x._val, y._val); - swap(x._grad, y._grad); + return _a.gradient(idx) * _cacheC + _b.gradient(idx) * _cacheD; } +protected: + const A& _a; + const B& _b; + const real_t _cacheA; + const real_t _cacheB; + const real_t _cacheC; + const real_t _cacheD; +}; + +template +inline Pow pow(const Expr& a, const Expr& b) SFAD_NOEXCEPT +{ + return Pow(a.base(), b.base()); } +template class PowScalar : public Expr, real_t> +{ +public: + PowScalar(const Expr& a, const real_t b) SFAD_NOEXCEPT : _a(a.base()), + _b(b), + _cacheA(_a.value()), + _cacheB(_b* std::pow(_cacheA, _b - real_t(1))) + { + } + + inline const real_t value() const SFAD_NOEXCEPT + { + return std::pow(_cacheA, _b); + } + inline const real_t gradient(std::size_t idx) const + { + return _a.gradient(idx) * _cacheB; + } + +protected: + const A& _a; + const real_t _b; + const real_t _cacheA; + const real_t _cacheB; +}; + +template +inline PowScalar pow(const Expr& a, const real_t b) SFAD_NOEXCEPT +{ + return PowScalar(a.base(), b); +} + +template class ScalarPow : public Expr, real_t> +{ +public: + ScalarPow(const real_t a, const Expr& b) SFAD_NOEXCEPT : _a(a), + _b(b.base()), + _cacheA(_b.value()), + _cacheB(std::pow(_a, _cacheA) * std::log(_a)) + { + } + + inline const real_t value() const SFAD_NOEXCEPT + { + return std::pow(_a, _b.value()); + } + inline const real_t gradient(std::size_t idx) const + { + return _b.gradient(idx) * _cacheB; + } + +protected: + const real_t _a; + const A& _b; + const real_t _cacheA; + const real_t _cacheB; +}; + +template +inline ScalarPow pow(const real_t a, const Expr& b) SFAD_NOEXCEPT +{ + return ScalarPow(a, b.base()); +} + +template void swap(FwdET& x, FwdET& y) SFAD_NOEXCEPT +{ + using std::swap; + swap(x._val, y._val); + swap(x._grad, y._grad); +} + +} // namespace sfad + #endif diff --git a/include/ad/sfad-common.hpp b/include/ad/sfad-common.hpp index a1d69d509..db07871e4 100644 --- a/include/ad/sfad-common.hpp +++ b/include/ad/sfad-common.hpp @@ -1,10 +1,10 @@ // ============================================================================= // SFAD - Simple Forward Automatic Differentiation -// +// // Copyright © Samuel Leweke¹ -// +// // ¹ Forschungszentrum Juelich GmbH, IBG-1, Juelich, Germany. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -15,11 +15,11 @@ #define _SFAD_COMMON_HPP_ #ifndef SFAD_DEFAULT_DIR - #define SFAD_DEFAULT_DIR 80 +#define SFAD_DEFAULT_DIR 80 #endif #ifndef SFAD_GLOBAL_GRAD_SIZE - #define SFAD_GLOBAL_GRAD_SIZE std::size_t sfad::detail::globalGradSize = SFAD_DEFAULT_DIR; +#define SFAD_GLOBAL_GRAD_SIZE std::size_t sfad::detail::globalGradSize = SFAD_DEFAULT_DIR; #endif // Improve branch prediction by marking likely and unlikely execution paths @@ -28,84 +28,84 @@ // transforms to // if (sfad_unlikely(some_unlikely_condition)) { ... } #ifdef __GNUC__ - #define sfad_likely(x) __builtin_expect(!!(x), 1) - #define sfad_unlikely(x) __builtin_expect(!!(x), 0) +#define sfad_likely(x) __builtin_expect(!!(x), 1) +#define sfad_unlikely(x) __builtin_expect(!!(x), 0) #endif #ifndef sfad_likely - #define sfad_likely(x) (x) - #define sfad_unlikely(x) (x) +#define sfad_likely(x) (x) +#define sfad_unlikely(x) (x) #endif #if defined(__clang__) && defined(__apple_build_version__) - // Apple Clang - #if ((__clang_major__ * 100) + __clang_minor__) >= 400 && __has_feature(cxx_noexcept) - #define SFAD_COMPILER_CXX_NOEXCEPT 1 - #else - #define SFAD_COMPILER_CXX_NOEXCEPT 0 - #endif +// Apple Clang +#if ((__clang_major__ * 100) + __clang_minor__) >= 400 && __has_feature(cxx_noexcept) +#define SFAD_COMPILER_CXX_NOEXCEPT 1 +#else +#define SFAD_COMPILER_CXX_NOEXCEPT 0 +#endif #elif defined(__clang__) - // Clang - #if ((__clang_major__ * 100) + __clang_minor__) >= 304 && __has_feature(cxx_noexcept) - #define SFAD_COMPILER_CXX_NOEXCEPT 1 - #else - #define SFAD_COMPILER_CXX_NOEXCEPT 0 - #endif +// Clang +#if ((__clang_major__ * 100) + __clang_minor__) >= 304 && __has_feature(cxx_noexcept) +#define SFAD_COMPILER_CXX_NOEXCEPT 1 +#else +#define SFAD_COMPILER_CXX_NOEXCEPT 0 +#endif #elif defined(__INTEL_COMPILER) || defined(__ICC) - // Intel icpc - #if (__INTEL_COMPILER >= 1400) - #define SFAD_COMPILER_CXX_NOEXCEPT 1 - #else - #define SFAD_COMPILER_CXX_NOEXCEPT 0 - #endif +// Intel icpc +#if (__INTEL_COMPILER >= 1400) +#define SFAD_COMPILER_CXX_NOEXCEPT 1 +#else +#define SFAD_COMPILER_CXX_NOEXCEPT 0 +#endif #elif defined(__GNUC__) - // GNU GCC - #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) - #define SFAD_COMPILER_CXX_NOEXCEPT 1 - #else - #define SFAD_COMPILER_CXX_NOEXCEPT 0 - #endif +// GNU GCC +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && \ + (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +#define SFAD_COMPILER_CXX_NOEXCEPT 1 +#else +#define SFAD_COMPILER_CXX_NOEXCEPT 0 +#endif #elif defined(_MSC_VER) - // MS Visual C++ - #if _MSC_VER >= 1900 - #define SFAD_COMPILER_CXX_NOEXCEPT 1 - #else - #define SFAD_COMPILER_CXX_NOEXCEPT 0 - #endif +// MS Visual C++ +#if _MSC_VER >= 1900 +#define SFAD_COMPILER_CXX_NOEXCEPT 1 +#else +#define SFAD_COMPILER_CXX_NOEXCEPT 0 +#endif #endif #if SFAD_COMPILER_CXX_NOEXCEPT - #define SFAD_NOEXCEPT noexcept - #define SFAD_NOEXCEPT_EXPR(X) noexcept(X) +#define SFAD_NOEXCEPT noexcept +#define SFAD_NOEXCEPT_EXPR(X) noexcept(X) #else - #define SFAD_NOEXCEPT - #define SFAD_NOEXCEPT_EXPR(X) +#define SFAD_NOEXCEPT +#define SFAD_NOEXCEPT_EXPR(X) #endif - #include namespace sfad { - namespace detail - { - extern std::size_t globalGradSize; - } - - inline void setGradientSize(const std::size_t n) SFAD_NOEXCEPT - { - detail::globalGradSize = n; - } - - inline std::size_t getGradientSize() SFAD_NOEXCEPT - { - return detail::globalGradSize; - } +namespace detail +{ +extern std::size_t globalGradSize; +} + +inline void setGradientSize(const std::size_t n) SFAD_NOEXCEPT +{ + detail::globalGradSize = n; +} + +inline std::size_t getGradientSize() SFAD_NOEXCEPT +{ + return detail::globalGradSize; } +} // namespace sfad #endif diff --git a/include/ad/sfad.hpp b/include/ad/sfad.hpp index 7a6d7896e..cc2e5849d 100644 --- a/include/ad/sfad.hpp +++ b/include/ad/sfad.hpp @@ -1,10 +1,10 @@ // ============================================================================= // SFAD - Simple Forward Automatic Differentiation -// +// // Copyright © Samuel Leweke¹ -// +// // ¹ Forschungszentrum Juelich GmbH, IBG-1, Juelich, Germany. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -23,776 +23,857 @@ namespace sfad { - template - class Fwd - { - public: - typedef std::size_t idx_t; - - Fwd() SFAD_NOEXCEPT : _val(0) - { - setADValue(real_t(0)); - } - Fwd(const real_t val) SFAD_NOEXCEPT : _val(val) - { - setADValue(real_t(0)); - } - Fwd(const real_t val, real_t const* const grad) SFAD_NOEXCEPT : _val(val) - { - std::copy_n(grad, detail::globalGradSize, _grad); - } - Fwd(const Fwd& cpy) SFAD_NOEXCEPT = default; - Fwd(Fwd&& other) SFAD_NOEXCEPT = default; - - ~Fwd() = default; - - Fwd& operator=(Fwd&& other) SFAD_NOEXCEPT = default; - Fwd& operator=(const Fwd& other) = default; - - const idx_t gradientSize() const SFAD_NOEXCEPT { return detail::globalGradSize; } - - template friend void swap (Fwd& x, Fwd& y) SFAD_NOEXCEPT; - - // ADOL-C compatibility - - inline real_t getValue() SFAD_NOEXCEPT { return _val; } - inline const real_t getValue() const SFAD_NOEXCEPT { return _val; } - inline void setValue(const real_t v) SFAD_NOEXCEPT { _val = v; } - - inline real_t getADValue(const idx_t idx) { return _grad[idx]; } - inline const real_t getADValue(const idx_t idx) const { return _grad[idx]; } - inline void setADValue(const idx_t idx, const real_t v) { _grad[idx] = v; } - inline void setADValue(const real_t v) - { - fillADValue(v); - } - - inline void fillADValue(const real_t v) - { - fillADValue(0, detail::globalGradSize, v); - } - inline void fillADValue(const idx_t start, const real_t v) - { - fillADValue(start, detail::globalGradSize, v); - } - inline void fillADValue(const idx_t start, const idx_t end, const real_t v) - { - std::fill(_grad + start, _grad + end, v); - } - - // Modern C++ accessor - - inline real_t& operator[](const idx_t idx) { return _grad[idx]; } - inline const real_t operator[](const idx_t idx) const { return _grad[idx]; } +template class Fwd +{ +public: + typedef std::size_t idx_t; - explicit operator real_t() const SFAD_NOEXCEPT { return _val; } + Fwd() SFAD_NOEXCEPT : _val(0) + { + setADValue(real_t(0)); + } + Fwd(const real_t val) SFAD_NOEXCEPT : _val(val) + { + setADValue(real_t(0)); + } + Fwd(const real_t val, real_t const* const grad) SFAD_NOEXCEPT : _val(val) + { + std::copy_n(grad, detail::globalGradSize, _grad); + } + Fwd(const Fwd& cpy) SFAD_NOEXCEPT = default; + Fwd(Fwd&& other) SFAD_NOEXCEPT = default; - // Operators with non-temporary results - - // Assignment - inline Fwd& operator=(const real_t v) - { - _val = v; - setADValue(real_t(0)); + ~Fwd() = default; - return *this; - } + Fwd& operator=(Fwd&& other) SFAD_NOEXCEPT = default; + Fwd& operator=(const Fwd& other) = default; - // Addition - inline Fwd& operator+=(const real_t v) - { - _val += v; - return *this; - } + const idx_t gradientSize() const SFAD_NOEXCEPT + { + return detail::globalGradSize; + } - inline Fwd& operator+=(const Fwd& a) - { - _val += a._val; - for (idx_t i = 0; i < detail::globalGradSize; ++i) - _grad[i] += a._grad[i]; + template friend void swap(Fwd& x, Fwd& y) SFAD_NOEXCEPT; - return *this; - } + // ADOL-C compatibility - // Substraction - inline Fwd& operator-=(const real_t v) - { - _val -= v; - return *this; - } + inline real_t getValue() SFAD_NOEXCEPT + { + return _val; + } + inline const real_t getValue() const SFAD_NOEXCEPT + { + return _val; + } + inline void setValue(const real_t v) SFAD_NOEXCEPT + { + _val = v; + } - inline Fwd& operator-=(const Fwd& a) - { - _val -= a._val; - for (idx_t i = 0; i < detail::globalGradSize; ++i) - _grad[i] -= a._grad[i]; + inline real_t getADValue(const idx_t idx) + { + return _grad[idx]; + } + inline const real_t getADValue(const idx_t idx) const + { + return _grad[idx]; + } + inline void setADValue(const idx_t idx, const real_t v) + { + _grad[idx] = v; + } + inline void setADValue(const real_t v) + { + fillADValue(v); + } - return *this; - } + inline void fillADValue(const real_t v) + { + fillADValue(0, detail::globalGradSize, v); + } + inline void fillADValue(const idx_t start, const real_t v) + { + fillADValue(start, detail::globalGradSize, v); + } + inline void fillADValue(const idx_t start, const idx_t end, const real_t v) + { + std::fill(_grad + start, _grad + end, v); + } - // Multiplication - inline Fwd& operator*=(const real_t v) - { - _val *= v; - for (idx_t i = 0; i < detail::globalGradSize; ++i) - _grad[i] *= v; - return *this; - } + // Modern C++ accessor - inline Fwd& operator*=(const Fwd& a) - { - for (idx_t i = 0; i < detail::globalGradSize; ++i) - _grad[i] = a._val * _grad[i] + _val * a._grad[i]; + inline real_t& operator[](const idx_t idx) + { + return _grad[idx]; + } + inline const real_t operator[](const idx_t idx) const + { + return _grad[idx]; + } - _val *= a._val; - return *this; - } - - // Division - inline Fwd& operator/=(const real_t v) - { - _val /= v; - for (idx_t i = 0; i < detail::globalGradSize; ++i) - _grad[i] /= v; - return *this; - } + explicit operator real_t() const SFAD_NOEXCEPT + { + return _val; + } - inline Fwd& operator/=(const Fwd& a) - { - for (idx_t i = 0; i < detail::globalGradSize; ++i) -// _grad[i] = (_grad[i] - _val / a._val * a._grad[i]) / a._val; - _grad[i] = (_grad[i] * a._val - _val * a._grad[i]) / (a._val * a._val); + // Operators with non-temporary results - _val /= a._val; - return *this; - } + // Assignment + inline Fwd& operator=(const real_t v) + { + _val = v; + setADValue(real_t(0)); - // Comparisons - inline bool operator!=(const Fwd& v) const SFAD_NOEXCEPT { return v != _val; } - inline bool operator!=(const real_t v) const SFAD_NOEXCEPT { return v != _val; } - inline friend bool operator!=(const real_t v, const Fwd& a) SFAD_NOEXCEPT { return v != a._val; } + return *this; + } - inline bool operator==(const Fwd& v) const SFAD_NOEXCEPT { return v == _val; } - inline bool operator==(const real_t v) const SFAD_NOEXCEPT { return v == _val; } - inline friend bool operator==(const real_t v, const Fwd& a) SFAD_NOEXCEPT { return v == a._val; } + // Addition + inline Fwd& operator+=(const real_t v) + { + _val += v; + return *this; + } - inline bool operator<=(const Fwd& v) const SFAD_NOEXCEPT { return _val <= v._val; } - inline bool operator<=(const real_t v) const SFAD_NOEXCEPT { return _val <= v; } - inline friend bool operator<=(const real_t v, const Fwd& a) SFAD_NOEXCEPT { return v <= a._val; } + inline Fwd& operator+=(const Fwd& a) + { + _val += a._val; + for (idx_t i = 0; i < detail::globalGradSize; ++i) + _grad[i] += a._grad[i]; - inline bool operator>=(const Fwd& v) const SFAD_NOEXCEPT { return _val >= v._val; } - inline bool operator>=(const real_t v) const SFAD_NOEXCEPT { return _val >= v; } - inline friend bool operator>= (const real_t v, const Fwd& a) SFAD_NOEXCEPT { return v >= a._val; } + return *this; + } - inline bool operator>(const Fwd& v) const SFAD_NOEXCEPT { return _val > v._val; } - inline bool operator>(const real_t v) const SFAD_NOEXCEPT { return _val > v; } - inline friend bool operator>(const real_t v, const Fwd& a) SFAD_NOEXCEPT { return v > a._val; } + // Substraction + inline Fwd& operator-=(const real_t v) + { + _val -= v; + return *this; + } - inline bool operator<(const Fwd& v) const SFAD_NOEXCEPT { return _val < v._val; } - inline bool operator<(const real_t v) const SFAD_NOEXCEPT { return _val < v; } - inline friend bool operator<(const real_t v, const Fwd& a) SFAD_NOEXCEPT { return v < a._val; } + inline Fwd& operator-=(const Fwd& a) + { + _val -= a._val; + for (idx_t i = 0; i < detail::globalGradSize; ++i) + _grad[i] -= a._grad[i]; - // Operators with temporary results - - // Unary sign - inline Fwd operator-() const - { - Fwd cpy(-_val, false); - for (idx_t i = 0; i < detail::globalGradSize; ++i) - cpy._grad[i] = -_grad[i]; + return *this; + } - return cpy; - } + // Multiplication + inline Fwd& operator*=(const real_t v) + { + _val *= v; + for (idx_t i = 0; i < detail::globalGradSize; ++i) + _grad[i] *= v; + return *this; + } - inline Fwd operator+() const { return *this; } + inline Fwd& operator*=(const Fwd& a) + { + for (idx_t i = 0; i < detail::globalGradSize; ++i) + _grad[i] = a._val * _grad[i] + _val * a._grad[i]; - // Addition - inline Fwd operator+(const real_t v) const - { - return Fwd(_val + v, _grad); - } + _val *= a._val; + return *this; + } - inline Fwd operator+(const Fwd& a) const - { - Fwd cpy(_val + a._val, false); - for (idx_t i = 0; i < detail::globalGradSize; ++i) - cpy._grad[i] = _grad[i] + a._grad[i]; - return cpy; - } + // Division + inline Fwd& operator/=(const real_t v) + { + _val /= v; + for (idx_t i = 0; i < detail::globalGradSize; ++i) + _grad[i] /= v; + return *this; + } - inline friend Fwd operator+(const real_t v, const Fwd& a) - { - return Fwd(v + a._val, a._grad); - } - - // Substraction - inline Fwd operator-(const real_t v) const - { - return Fwd(_val - v, _grad); - } + inline Fwd& operator/=(const Fwd& a) + { + for (idx_t i = 0; i < detail::globalGradSize; ++i) + // _grad[i] = (_grad[i] - _val / a._val * a._grad[i]) / a._val; + _grad[i] = (_grad[i] * a._val - _val * a._grad[i]) / (a._val * a._val); - inline Fwd operator-(const Fwd& a) const - { - Fwd cpy(_val - a._val, false); - for (idx_t i = 0; i < detail::globalGradSize; ++i) - cpy._grad[i] = _grad[i] - a._grad[i]; - return cpy; - } + _val /= a._val; + return *this; + } - inline friend Fwd operator-(const real_t v, const Fwd& a) - { - Fwd res(v - a._val, false); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = -a._grad[i]; - return res; - } - - // Multiplication - inline Fwd operator*(const real_t v) const - { - Fwd res(_val * v); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = v * _grad[i]; - return res; - } + // Comparisons + inline bool operator!=(const Fwd& v) const SFAD_NOEXCEPT + { + return v != _val; + } + inline bool operator!=(const real_t v) const SFAD_NOEXCEPT + { + return v != _val; + } + inline friend bool operator!=(const real_t v, const Fwd& a) SFAD_NOEXCEPT + { + return v != a._val; + } - inline Fwd operator*(const Fwd& a) const - { - Fwd cpy(_val * a._val, false); - for (idx_t i = 0; i < detail::globalGradSize; ++i) - cpy._grad[i] = a._val * _grad[i] + _val * a._grad[i]; - return cpy; - } + inline bool operator==(const Fwd& v) const SFAD_NOEXCEPT + { + return v == _val; + } + inline bool operator==(const real_t v) const SFAD_NOEXCEPT + { + return v == _val; + } + inline friend bool operator==(const real_t v, const Fwd& a) SFAD_NOEXCEPT + { + return v == a._val; + } - inline friend Fwd operator*(const real_t v, const Fwd& a) - { - Fwd res(v * a._val, false); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = v * a._grad[i]; - return res; - } - - // Division - inline Fwd operator/(const real_t v) const - { - Fwd res(_val / v, false); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = _grad[i] / v; - return res; - } + inline bool operator<=(const Fwd& v) const SFAD_NOEXCEPT + { + return _val <= v._val; + } + inline bool operator<=(const real_t v) const SFAD_NOEXCEPT + { + return _val <= v; + } + inline friend bool operator<=(const real_t v, const Fwd& a) SFAD_NOEXCEPT + { + return v <= a._val; + } - inline Fwd operator/(const Fwd& a) const - { - Fwd res(_val / a._val, false); - for (idx_t i = 0; i < detail::globalGradSize; ++i) -// res._grad[i] = (_grad[i] - _val / a._val * a._grad[i]) / a._val; - res._grad[i] = (_grad[i] * a._val - _val * a._grad[i]) / (a._val * a._val); - return res; - } + inline bool operator>=(const Fwd& v) const SFAD_NOEXCEPT + { + return _val >= v._val; + } + inline bool operator>=(const real_t v) const SFAD_NOEXCEPT + { + return _val >= v; + } + inline friend bool operator>=(const real_t v, const Fwd& a) SFAD_NOEXCEPT + { + return v >= a._val; + } - inline friend Fwd operator/(const real_t v, const Fwd& a) - { - Fwd res(v / a._val, false); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) -// res._grad[i] = -(v / (a._val * a._val) * a._grad[i]); - res._grad[i] = -v * a._grad[i] / (a._val * a._val); - return res; - } + inline bool operator>(const Fwd& v) const SFAD_NOEXCEPT + { + return _val > v._val; + } + inline bool operator>(const real_t v) const SFAD_NOEXCEPT + { + return _val > v; + } + inline friend bool operator>(const real_t v, const Fwd& a) SFAD_NOEXCEPT + { + return v > a._val; + } - // Math functions - template inline friend Fwd exp(const Fwd &a); - template inline friend Fwd log(const Fwd &a); - template inline friend Fwd log10(const Fwd &a); - template inline friend Fwd sqrt(const Fwd &a); - template inline friend Fwd sqr(const Fwd &a); + inline bool operator<(const Fwd& v) const SFAD_NOEXCEPT + { + return _val < v._val; + } + inline bool operator<(const real_t v) const SFAD_NOEXCEPT + { + return _val < v; + } + inline friend bool operator<(const real_t v, const Fwd& a) SFAD_NOEXCEPT + { + return v < a._val; + } - template inline friend Fwd sin(const Fwd &a); - template inline friend Fwd cos(const Fwd &a); - template inline friend Fwd tan(const Fwd &a); - template inline friend Fwd asin(const Fwd &a); - template inline friend Fwd acos(const Fwd &a); - template inline friend Fwd atan(const Fwd &a); + // Operators with temporary results - template inline friend Fwd pow(const Fwd &a, T v); - template inline friend Fwd pow(T v, const Fwd &a); - template inline friend Fwd pow(const Fwd &a, const Fwd &b); + // Unary sign + inline Fwd operator-() const + { + Fwd cpy(-_val, false); + for (idx_t i = 0; i < detail::globalGradSize; ++i) + cpy._grad[i] = -_grad[i]; - template inline friend Fwd sinh(const Fwd &a); - template inline friend Fwd cosh(const Fwd &a); - template inline friend Fwd tanh(const Fwd &a); + return cpy; + } - template inline friend Fwd fabs(const Fwd &a); + inline Fwd operator+() const + { + return *this; + } - template inline friend Fwd ceil(const Fwd &a); - template inline friend Fwd floor(const Fwd &a); + // Addition + inline Fwd operator+(const real_t v) const + { + return Fwd(_val + v, _grad); + } - template inline friend Fwd fmax(const Fwd &a, const Fwd &b); - template inline friend Fwd fmax(T v, const Fwd &a); - template inline friend Fwd fmax(const Fwd &a, T v); + inline Fwd operator+(const Fwd& a) const + { + Fwd cpy(_val + a._val, false); + for (idx_t i = 0; i < detail::globalGradSize; ++i) + cpy._grad[i] = _grad[i] + a._grad[i]; + return cpy; + } - template inline friend Fwd fmin(const Fwd &a, const Fwd &b); - template inline friend Fwd fmin(T v, const Fwd &a); - template inline friend Fwd fmin(const Fwd &a, T v); + inline friend Fwd operator+(const real_t v, const Fwd& a) + { + return Fwd(v + a._val, a._grad); + } - protected: - Fwd(const real_t val, bool dummy) : _val(val) { } + // Substraction + inline Fwd operator-(const real_t v) const + { + return Fwd(_val - v, _grad); + } - real_t _val; - real_t _grad[SFAD_DEFAULT_DIR]; - }; + inline Fwd operator-(const Fwd& a) const + { + Fwd cpy(_val - a._val, false); + for (idx_t i = 0; i < detail::globalGradSize; ++i) + cpy._grad[i] = _grad[i] - a._grad[i]; + return cpy; + } - template - inline Fwd exp(const Fwd &a) + inline friend Fwd operator-(const real_t v, const Fwd& a) { - Fwd res(std::exp(a._val), false); + Fwd res(v - a._val, false); for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = a._grad[i] * res._val; + res._grad[i] = -a._grad[i]; return res; } - template - inline Fwd log(const Fwd &a) + // Multiplication + inline Fwd operator*(const real_t v) const { -// using std::copysign; - - Fwd res(std::log(a._val), false); - if (sfad_likely(a._val > real_t(0))) - { - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = a._grad[i] / a._val; - } - else if (a._val == real_t(0)) - { - const real_t inf = std::numeric_limits::infinity(); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = copysign(inf, -a._grad[i]); - } - else - { - const real_t nAn = std::numeric_limits::quiet_NaN(); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = nAn; - } - + Fwd res(_val * v); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = v * _grad[i]; return res; } - template - inline Fwd log10(const Fwd &a) + inline Fwd operator*(const Fwd& a) const { -// using std::copysign; - - Fwd res(std::log10(a._val), false); - if (sfad_likely(a._val > real_t(0))) - { - const real_t tmp = std::log(real_t(10)) * a._val; - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = a._grad[i] / tmp; - } - else if (a._val == real_t(0)) - { - const real_t inf = std::numeric_limits::infinity(); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = copysign(inf, -a._grad[i]); - } - else - { - const real_t nAn = std::numeric_limits::quiet_NaN(); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = nAn; - } - - return res; + Fwd cpy(_val * a._val, false); + for (idx_t i = 0; i < detail::globalGradSize; ++i) + cpy._grad[i] = a._val * _grad[i] + _val * a._grad[i]; + return cpy; } - template - inline Fwd sqrt(const Fwd &a) + inline friend Fwd operator*(const real_t v, const Fwd& a) { -// using std::copysign; - - Fwd res(std::sqrt(a._val), false); - if (sfad_likely(a._val > real_t(0))) - { - const real_t tmp = real_t(2) * res._val; - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = a._grad[i] / tmp; - } - else if (a._val == real_t(0)) - { - const real_t inf = std::numeric_limits::infinity(); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = copysign(inf, a._grad[i]); - } - else - { - const real_t nAn = std::numeric_limits::quiet_NaN(); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = nAn; - } - + Fwd res(v * a._val, false); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = v * a._grad[i]; return res; } - template - inline Fwd sqr(const Fwd &a) + // Division + inline Fwd operator/(const real_t v) const { - Fwd res(a._val * a._val, false); - const real_t tmp = real_t(2) * a._val; + Fwd res(_val / v, false); for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = tmp * a._grad[i]; + res._grad[i] = _grad[i] / v; return res; } - template - inline Fwd sin(const Fwd &a) + inline Fwd operator/(const Fwd& a) const { - Fwd res(std::sin(a._val), false); - const real_t tmp = std::cos(a._val); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = a._grad[i] * tmp; + Fwd res(_val / a._val, false); + for (idx_t i = 0; i < detail::globalGradSize; ++i) + // res._grad[i] = (_grad[i] - _val / a._val * a._grad[i]) / a._val; + res._grad[i] = (_grad[i] * a._val - _val * a._grad[i]) / (a._val * a._val); return res; } - template - inline Fwd cos(const Fwd &a) + inline friend Fwd operator/(const real_t v, const Fwd& a) { - Fwd res(std::cos(a._val), false); - const real_t tmp = -std::sin(a._val); + Fwd res(v / a._val, false); for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = a._grad[i] * tmp; + // res._grad[i] = -(v / (a._val * a._val) * a._grad[i]); + res._grad[i] = -v * a._grad[i] / (a._val * a._val); return res; } - template - inline Fwd tan(const Fwd &a) + // Math functions + template inline friend Fwd exp(const Fwd& a); + template inline friend Fwd log(const Fwd& a); + template inline friend Fwd log10(const Fwd& a); + template inline friend Fwd sqrt(const Fwd& a); + template inline friend Fwd sqr(const Fwd& a); + + template inline friend Fwd sin(const Fwd& a); + template inline friend Fwd cos(const Fwd& a); + template inline friend Fwd tan(const Fwd& a); + template inline friend Fwd asin(const Fwd& a); + template inline friend Fwd acos(const Fwd& a); + template inline friend Fwd atan(const Fwd& a); + + template inline friend Fwd pow(const Fwd& a, T v); + template inline friend Fwd pow(T v, const Fwd& a); + template inline friend Fwd pow(const Fwd& a, const Fwd& b); + + template inline friend Fwd sinh(const Fwd& a); + template inline friend Fwd cosh(const Fwd& a); + template inline friend Fwd tanh(const Fwd& a); + + template inline friend Fwd fabs(const Fwd& a); + + template inline friend Fwd ceil(const Fwd& a); + template inline friend Fwd floor(const Fwd& a); + + template inline friend Fwd fmax(const Fwd& a, const Fwd& b); + template inline friend Fwd fmax(T v, const Fwd& a); + template inline friend Fwd fmax(const Fwd& a, T v); + + template inline friend Fwd fmin(const Fwd& a, const Fwd& b); + template inline friend Fwd fmin(T v, const Fwd& a); + template inline friend Fwd fmin(const Fwd& a, T v); + +protected: + Fwd(const real_t val, bool dummy) : _val(val) { - Fwd res(std::tan(a._val), false); + } + + real_t _val; + real_t _grad[SFAD_DEFAULT_DIR]; +}; + +template inline Fwd exp(const Fwd& a) +{ + Fwd res(std::exp(a._val), false); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = a._grad[i] * res._val; + return res; +} - const real_t tmpCos = std::cos(a._val); - const real_t tmp = tmpCos * tmpCos; +template inline Fwd log(const Fwd& a) +{ + // using std::copysign; + + Fwd res(std::log(a._val), false); + if (sfad_likely(a._val > real_t(0))) + { for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = a._grad[i] / tmp; - return res; + res._grad[i] = a._grad[i] / a._val; } - - template - inline Fwd asin(const Fwd &a) + else if (a._val == real_t(0)) { - Fwd res(std::asin(a._val), false); - const real_t tmp = std::sqrt(real_t(1) - a._val * a._val); + const real_t inf = std::numeric_limits::infinity(); for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = a._grad[i] / tmp; - return res; + res._grad[i] = copysign(inf, -a._grad[i]); } - - template - inline Fwd acos(const Fwd &a) + else { - Fwd res(std::acos(a._val), false); - const real_t tmp = std::sqrt(real_t(1) - a._val * a._val); + const real_t nAn = std::numeric_limits::quiet_NaN(); for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = -a._grad[i] / tmp; - return res; + res._grad[i] = nAn; } - template - inline Fwd atan(const Fwd &a) + return res; +} + +template inline Fwd log10(const Fwd& a) +{ + // using std::copysign; + + Fwd res(std::log10(a._val), false); + if (sfad_likely(a._val > real_t(0))) { - Fwd res(std::atan(a._val), false); - const real_t tmp = real_t(1) + a._val * a._val; + const real_t tmp = std::log(real_t(10)) * a._val; for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) res._grad[i] = a._grad[i] / tmp; - return res; } - - template - inline Fwd pow(const Fwd &a, real_t v) + else if (a._val == real_t(0)) { - Fwd res(std::pow(a._val, v), false); - const real_t tmp = v * std::pow(a._val, v - real_t(1)); + const real_t inf = std::numeric_limits::infinity(); for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = a._grad[i] * tmp; - return res; + res._grad[i] = copysign(inf, -a._grad[i]); } - - template - inline Fwd pow(real_t v, const Fwd &a) + else { - Fwd res(std::pow(v, a._val), false); - const real_t tmp = res._val * std::log(v); + const real_t nAn = std::numeric_limits::quiet_NaN(); for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = a._grad[i] * tmp; - return res; + res._grad[i] = nAn; } - template - inline Fwd pow(const Fwd &a, const Fwd &b) + return res; +} + +template inline Fwd sqrt(const Fwd& a) +{ + // using std::copysign; + + Fwd res(std::sqrt(a._val), false); + if (sfad_likely(a._val > real_t(0))) { - Fwd res(std::pow(a._val, b._val), false); - const real_t tmp1 = b._val * std::pow(a._val, b._val - real_t(1)); - const real_t tmp2 = res._val * std::log(a._val); + const real_t tmp = real_t(2) * res._val; for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = a._grad[i] * tmp1 + b._grad[i] * tmp2; - return res; + res._grad[i] = a._grad[i] / tmp; } - - template - inline Fwd sinh (const Fwd &a) + else if (a._val == real_t(0)) { - Fwd res(std::sinh(a._val), false); - const real_t tmp = std::cosh(a._val); + const real_t inf = std::numeric_limits::infinity(); for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = a._grad[i] * tmp; - return res; + res._grad[i] = copysign(inf, a._grad[i]); } - - template - inline Fwd cosh (const Fwd &a) + else { - Fwd res(std::cosh(a._val), false); - const real_t tmp = std::sinh(a._val); + const real_t nAn = std::numeric_limits::quiet_NaN(); for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = a._grad[i] * tmp; - return res; + res._grad[i] = nAn; } - template - inline Fwd tanh (const Fwd &a) + return res; +} + +template inline Fwd sqr(const Fwd& a) +{ + Fwd res(a._val * a._val, false); + const real_t tmp = real_t(2) * a._val; + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = tmp * a._grad[i]; + return res; +} + +template inline Fwd sin(const Fwd& a) +{ + Fwd res(std::sin(a._val), false); + const real_t tmp = std::cos(a._val); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = a._grad[i] * tmp; + return res; +} + +template inline Fwd cos(const Fwd& a) +{ + Fwd res(std::cos(a._val), false); + const real_t tmp = -std::sin(a._val); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = a._grad[i] * tmp; + return res; +} + +template inline Fwd tan(const Fwd& a) +{ + Fwd res(std::tan(a._val), false); + + const real_t tmpCos = std::cos(a._val); + const real_t tmp = tmpCos * tmpCos; + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = a._grad[i] / tmp; + return res; +} + +template inline Fwd asin(const Fwd& a) +{ + Fwd res(std::asin(a._val), false); + const real_t tmp = std::sqrt(real_t(1) - a._val * a._val); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = a._grad[i] / tmp; + return res; +} + +template inline Fwd acos(const Fwd& a) +{ + Fwd res(std::acos(a._val), false); + const real_t tmp = std::sqrt(real_t(1) - a._val * a._val); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = -a._grad[i] / tmp; + return res; +} + +template inline Fwd atan(const Fwd& a) +{ + Fwd res(std::atan(a._val), false); + const real_t tmp = real_t(1) + a._val * a._val; + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = a._grad[i] / tmp; + return res; +} + +template inline Fwd pow(const Fwd& a, real_t v) +{ + Fwd res(std::pow(a._val, v), false); + const real_t tmp = v * std::pow(a._val, v - real_t(1)); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = a._grad[i] * tmp; + return res; +} + +template inline Fwd pow(real_t v, const Fwd& a) +{ + Fwd res(std::pow(v, a._val), false); + const real_t tmp = res._val * std::log(v); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = a._grad[i] * tmp; + return res; +} + +template inline Fwd pow(const Fwd& a, const Fwd& b) +{ + Fwd res(std::pow(a._val, b._val), false); + const real_t tmp1 = b._val * std::pow(a._val, b._val - real_t(1)); + const real_t tmp2 = res._val * std::log(a._val); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = a._grad[i] * tmp1 + b._grad[i] * tmp2; + return res; +} + +template inline Fwd sinh(const Fwd& a) +{ + Fwd res(std::sinh(a._val), false); + const real_t tmp = std::cosh(a._val); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = a._grad[i] * tmp; + return res; +} + +template inline Fwd cosh(const Fwd& a) +{ + Fwd res(std::cosh(a._val), false); + const real_t tmp = std::sinh(a._val); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = a._grad[i] * tmp; + return res; +} + +template inline Fwd tanh(const Fwd& a) +{ + Fwd res(std::tanh(a._val), false); + /* + const real_t tmp = real_t(1) - res._val * res._val; + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = a._grad[i] * tmp; + */ + const real_t tmp = std::cosh(a._val); + const real_t tmp2 = tmp * tmp; + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = a._grad[i] / tmp2; + return res; +} + +template inline Fwd fabs(const Fwd& a) +{ + Fwd res(std::abs(a._val), false); + + if (a._val > real_t(0)) { - Fwd res(std::tanh(a._val), false); -/* - const real_t tmp = real_t(1) - res._val * res._val; for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = a._grad[i] * tmp; -*/ - const real_t tmp = std::cosh(a._val); - const real_t tmp2 = tmp * tmp; + res._grad[i] = a._grad[i]; + } + else if (a._val < real_t(0)) + { for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = a._grad[i] / tmp2; - return res; + res._grad[i] = -a._grad[i]; } - - template - inline Fwd fabs (const Fwd &a) + else { - Fwd res(std::abs(a._val), false); - - if (a._val > real_t(0)) + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) { - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + if (a._grad[i] > real_t(0)) res._grad[i] = a._grad[i]; - } - else if (a._val < real_t(0)) - { - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + else if (a._grad[i] < real_t(0)) res._grad[i] = -a._grad[i]; + else + res._grad[i] = a._grad[i]; } - else - { - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - { - if (a._grad[i] > real_t(0)) - res._grad[i] = a._grad[i]; - else if (a._grad[i] < real_t(0)) - res._grad[i] = -a._grad[i]; - else - res._grad[i] = a._grad[i]; - } - - } + } - return res; + return res; +} + +template inline Fwd ceil(const Fwd& a) +{ + Fwd res(std::ceil(a._val), false); + const real_t tmp(0); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = tmp; + return res; +} + +template inline Fwd floor(const Fwd& a) +{ + Fwd res(std::floor(a._val), false); + const real_t tmp(0); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = tmp; + return res; +} + +template inline Fwd fmax(const Fwd& a, const Fwd& b) +{ + Fwd res(real_t(0), false); + const real_t diff = a._val - b._val; + if (diff > real_t(0)) + { + res._val = a._val; + res.copyGradient(a._grad); + } + else if (diff < real_t(0)) + { + res._val = b._val; + res.copyGradient(b._grad); + } + else + { + res._val = b._val; + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = std::max(a._grad[i], b._grad[i]); } + return res; +} - template - inline Fwd ceil (const Fwd &a) +template inline Fwd fmax(real_t v, const Fwd& a) +{ + Fwd res(real_t(0), false); + const real_t diff = v - a._val; + if (diff > real_t(0)) + { + res._val = v; + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = real_t(0); + } + else if (diff < real_t(0)) + { + res._val = a._val; + res.copyGradient(a._grad); + } + else { - Fwd res(std::ceil(a._val), false); + res._val = a._val; const real_t tmp(0); for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = tmp; - return res; + res._grad[i] = std::max(tmp, a._grad[i]); } + return res; +} - template - inline Fwd floor (const Fwd &a) +template inline Fwd fmax(const Fwd& a, real_t v) +{ + Fwd res(real_t(0), false); + const real_t diff = a._val - v; + if (diff > real_t(0)) + { + res._val = a._val; + res.copyGradient(a._grad); + } + else if (diff < real_t(0)) + { + res._val = v; + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = real_t(0); + } + else { - Fwd res(std::floor(a._val), false); + res._val = a._val; const real_t tmp(0); for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = tmp; - return res; + res._grad[i] = std::max(tmp, a._grad[i]); } + return res; +} - template - inline Fwd fmax (const Fwd &a, const Fwd &b) +template inline Fwd fmin(const Fwd& a, const Fwd& b) +{ + Fwd res(real_t(0), false); + const real_t diff = a._val - b._val; + if (diff < real_t(0)) { - Fwd res(real_t(0), false); - const real_t diff = a._val - b._val; - if (diff > real_t(0)) - { - res._val = a._val; - res.copyGradient(a._grad); - } - else if (diff < real_t(0)) - { - res._val = b._val; - res.copyGradient(b._grad); - } - else - { - res._val = b._val; - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = std::max(a._grad[i], b._grad[i]); - } - return res; + res._val = a._val; + res.copyGradient(a._grad); } - - template - inline Fwd fmax (real_t v, const Fwd &a) + else if (diff > real_t(0)) { - Fwd res(real_t(0), false); - const real_t diff = v - a._val; - if (diff > real_t(0)) - { - res._val = v; - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = real_t(0); - } - else if (diff < real_t(0)) - { - res._val = a._val; - res.copyGradient(a._grad); - } - else - { - res._val = a._val; - const real_t tmp(0); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = std::max(tmp, a._grad[i]); - } - return res; + res._val = b._val; + res.copyGradient(b._grad); } + else + { + res._val = b._val; + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = std::min(a._grad[i], b._grad[i]); + } + return res; +} - template - inline Fwd fmax (const Fwd &a, real_t v) +template inline Fwd fmin(real_t v, const Fwd& a) +{ + Fwd res(real_t(0), false); + const real_t diff = v - a._val; + if (diff < real_t(0)) { - Fwd res(real_t(0), false); - const real_t diff = a._val - v; - if (diff > real_t(0)) - { - res._val = a._val; - res.copyGradient(a._grad); - } - else if (diff < real_t(0)) - { - res._val = v; - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = real_t(0); - } - else - { - res._val = a._val; - const real_t tmp(0); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = std::max(tmp, a._grad[i]); - } - return res; + res._val = v; + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = real_t(0); } - - template - inline Fwd fmin (const Fwd &a, const Fwd &b) + else if (diff > real_t(0)) { - Fwd res(real_t(0), false); - const real_t diff = a._val - b._val; - if (diff < real_t(0)) - { - res._val = a._val; - res.copyGradient(a._grad); - } - else if (diff > real_t(0)) - { - res._val = b._val; - res.copyGradient(b._grad); - } - else - { - res._val = b._val; - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = std::min(a._grad[i], b._grad[i]); - } - return res; + res._val = a._val; + res.copyGradient(a._grad); } - - template - inline Fwd fmin (real_t v, const Fwd &a) + else { - Fwd res(real_t(0), false); - const real_t diff = v - a._val; - if (diff < real_t(0)) - { - res._val = v; - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = real_t(0); - } - else if (diff > real_t(0)) - { - res._val = a._val; - res.copyGradient(a._grad); - } - else - { - res._val = a._val; - const real_t tmp(0); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = std::min(tmp, a._grad[i]); - } - return res; + res._val = a._val; + const real_t tmp(0); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = std::min(tmp, a._grad[i]); } + return res; +} - template - inline Fwd fmin (const Fwd &a, real_t v) +template inline Fwd fmin(const Fwd& a, real_t v) +{ + Fwd res(real_t(0), false); + const real_t diff = a._val - v; + if (diff < real_t(0)) { - Fwd res(real_t(0), false); - const real_t diff = a._val - v; - if (diff < real_t(0)) - { - res._val = a._val; - res.copyGradient(a._grad); - } - else if (diff > real_t(0)) - { - res._val = v; - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = real_t(0); - } - else - { - res._val = a._val; - const real_t tmp(0); - for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) - res._grad[i] = std::min(tmp, a._grad[i]); - } - return res; + res._val = a._val; + res.copyGradient(a._grad); } - - template inline Fwd max (const Fwd &a, const Fwd &b) { return fmax(a, b); } - template inline Fwd max (real_t v, const Fwd &a) { return fmax(v, a); } - template inline Fwd max (const Fwd &a, real_t v) { return fmax(a, v); } - template inline Fwd min (const Fwd &a, const Fwd &b) { return fmin(a, b); } - template inline Fwd min (real_t v, const Fwd &a) { return fmin(v, a); } - template inline Fwd min (const Fwd &a, real_t v) { return fmin(a, v); } - - template inline Fwd abs (const Fwd &a) { return fabs(a); } - - template - void swap(Fwd& x, Fwd& y) SFAD_NOEXCEPT + else if (diff > real_t(0)) { - using std::swap; - swap(x._val, y._val); - swap(x._grad, y._grad); + res._val = v; + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = real_t(0); + } + else + { + res._val = a._val; + const real_t tmp(0); + for (typename Fwd::idx_t i = 0; i < detail::globalGradSize; ++i) + res._grad[i] = std::min(tmp, a._grad[i]); } + return res; +} +template inline Fwd max(const Fwd& a, const Fwd& b) +{ + return fmax(a, b); +} +template inline Fwd max(real_t v, const Fwd& a) +{ + return fmax(v, a); +} +template inline Fwd max(const Fwd& a, real_t v) +{ + return fmax(a, v); } +template inline Fwd min(const Fwd& a, const Fwd& b) +{ + return fmin(a, b); +} +template inline Fwd min(real_t v, const Fwd& a) +{ + return fmin(v, a); +} +template inline Fwd min(const Fwd& a, real_t v) +{ + return fmin(a, v); +} + +template inline Fwd abs(const Fwd& a) +{ + return fabs(a); +} + +template void swap(Fwd& x, Fwd& y) SFAD_NOEXCEPT +{ + using std::swap; + swap(x._val, y._val); + swap(x._grad, y._grad); +} + +} // namespace sfad #endif diff --git a/include/cadet/Exceptions.hpp b/include/cadet/Exceptions.hpp index a6e66e28a..8834a07c0 100644 --- a/include/cadet/Exceptions.hpp +++ b/include/cadet/Exceptions.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines exceptions. */ @@ -31,22 +31,28 @@ namespace cadet class CADET_API InvalidParameterException : public std::domain_error { public: - explicit InvalidParameterException(const std::string& what_arg) : std::domain_error(what_arg) { } - explicit InvalidParameterException(const char* what_arg) : std::domain_error(what_arg) { } + explicit InvalidParameterException(const std::string& what_arg) : std::domain_error(what_arg) + { + } + explicit InvalidParameterException(const char* what_arg) : std::domain_error(what_arg) + { + } }; - /** * @brief Signals errors during the time integration process */ class CADET_API IntegrationException : public std::runtime_error { public: - explicit IntegrationException(const std::string& what_arg) : std::runtime_error(what_arg) { } - explicit IntegrationException(const char* what_arg) : std::runtime_error(what_arg) { } + explicit IntegrationException(const std::string& what_arg) : std::runtime_error(what_arg) + { + } + explicit IntegrationException(const char* what_arg) : std::runtime_error(what_arg) + { + } }; - } // namespace cadet -#endif // LIBCADET_EXCEPTIONS_HPP_ +#endif // LIBCADET_EXCEPTIONS_HPP_ diff --git a/include/cadet/ExternalFunction.hpp b/include/cadet/ExternalFunction.hpp index 5fe07f413..c696b2400 100644 --- a/include/cadet/ExternalFunction.hpp +++ b/include/cadet/ExternalFunction.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines an external function as data source. */ @@ -32,12 +32,14 @@ class IParameterProvider; class CADET_API IExternalFunction { public: - virtual ~IExternalFunction() CADET_NOEXCEPT { } + virtual ~IExternalFunction() CADET_NOEXCEPT + { + } /** * @brief Configures the external function by extracting all parameters from the given @p paramProvider * @details The scope of the cadet::IParameterProvider is left unchanged on return. - * + * * @param [in] paramProvider Pointer to parameter provider (may be @c nullptr) * @return @c true if the configuration was successful, otherwise @c false */ @@ -51,7 +53,7 @@ class CADET_API IExternalFunction /** * @brief Returns the function value at a given time and spatial position - * + * * @param [in] t Absolute simulation time * @param [in] z Normalized axial position in the column in [0,1] * @param [in] rho Normalized radial position in the column in [0,1] @@ -63,7 +65,7 @@ class CADET_API IExternalFunction /** * @brief Returns the time derivative of the function at a given time and spatial position - * + * * @param [in] t Absolute simulation time * @param [in] z Normalized axial position in the column in [0,1] * @param [in] rho Normalized radial position in the column in [0,1] @@ -80,7 +82,7 @@ class CADET_API IExternalFunction * provide means to implement discontinuous behavior (e.g., pulse injection profiles, * switching of valves). After initialization, the simulator notifies all entities * such as models or data sources of its section times. - * + * * The vector of section times consists of strictly increasing time points * @f[ t_0 < t_1 < t_2 < \dots t_N @f] * which mark the beginning and end of a section. The @f$ i@f$-th section is given by @@ -88,10 +90,10 @@ class CADET_API IExternalFunction * If a transition from one section to the next is continuous, the @p secContinuity flag * for that transition is @c true. In this case, the time integrator will not stop at the * transition time point and reinitialize consistently (which will be done for discontinuous - * transitions). - * + * transitions). + * * @param [in] secTimes Vector with section time points (length is @p nSections + 1) - * @param [in] secContinuity Vector of flags that indicate a continuous (@c true) or discontinuous (@c false) + * @param [in] secContinuity Vector of flags that indicate a continuous (@c true) or discontinuous (@c false) * transition from the current section to the next one (length is @p nSections - 1). For instance, * the first element indicates whether the transition from section @c 0 to @c 1 is continuous. * @param [in] nSections Number of sections @@ -101,4 +103,4 @@ class CADET_API IExternalFunction } // namespace cadet -#endif // LIBCADET_EXTERNALFUNCTION_HPP_ +#endif // LIBCADET_EXTERNALFUNCTION_HPP_ diff --git a/include/cadet/FactoryFuncs.hpp b/include/cadet/FactoryFuncs.hpp index b7e09c99e..b7cae53c2 100644 --- a/include/cadet/FactoryFuncs.hpp +++ b/include/cadet/FactoryFuncs.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides function for creating and destroying important classes. */ @@ -21,46 +21,45 @@ #include "cadet/LibExportImport.hpp" #include "cadet/cadetCompilerInfo.hpp" - namespace cadet { - class IModelBuilder; - class ISimulator; +class IModelBuilder; +class ISimulator; - /** - * @brief Creates an IModelBuilder object - * @sa cadetCreateModelBuilder() - * @return IModelBuilder object or @c NULL if something went wrong - */ - CADET_API IModelBuilder* createModelBuilder(); +/** + * @brief Creates an IModelBuilder object + * @sa cadetCreateModelBuilder() + * @return IModelBuilder object or @c NULL if something went wrong + */ +CADET_API IModelBuilder* createModelBuilder(); - /** - * @brief Destroys a given model builder - * @details Because a different memory space is assigned to dynamically loaded libraries, - * memory allocated by the library has to be freed in the library. Thus, users - * have to explicitly destroy their IModelBuilder objects here. - * @sa cadetDestroyModelBuilder() - * @param [in] builder IModelBuilder to be destroyed - */ - CADET_API void destroyModelBuilder(IModelBuilder* const builder) CADET_NOEXCEPT; +/** + * @brief Destroys a given model builder + * @details Because a different memory space is assigned to dynamically loaded libraries, + * memory allocated by the library has to be freed in the library. Thus, users + * have to explicitly destroy their IModelBuilder objects here. + * @sa cadetDestroyModelBuilder() + * @param [in] builder IModelBuilder to be destroyed + */ +CADET_API void destroyModelBuilder(IModelBuilder* const builder) CADET_NOEXCEPT; - /** - * @brief Creates an ISimulator object - * @sa cadetCreateSimulator() - * @return ISimulator object or @c NULL if something went wrong - */ - CADET_API ISimulator* createSimulator(); +/** + * @brief Creates an ISimulator object + * @sa cadetCreateSimulator() + * @return ISimulator object or @c NULL if something went wrong + */ +CADET_API ISimulator* createSimulator(); - /** - * @brief Destroys a given simulator - * @details Because a different memory space is assigned to dynamically loaded libraries, - * memory allocated by the library has to be freed in the library. Thus, users - * have to explicitly destroy their ISimulator objects here. - * @sa cadetDestroySimulator() - * @param [in] sim ISimulator to be destroyed - */ - CADET_API void destroySimulator(ISimulator* const sim) CADET_NOEXCEPT; +/** + * @brief Destroys a given simulator + * @details Because a different memory space is assigned to dynamically loaded libraries, + * memory allocated by the library has to be freed in the library. Thus, users + * have to explicitly destroy their ISimulator objects here. + * @sa cadetDestroySimulator() + * @param [in] sim ISimulator to be destroyed + */ +CADET_API void destroySimulator(ISimulator* const sim) CADET_NOEXCEPT; } // namespace cadet @@ -77,7 +76,7 @@ extern "C" * @details Because a different memory space is assigned to dynamically loaded libraries, * memory allocated by the library has to be freed in the library. Thus, users * have to explicitly destroy their IModelBuilder objects here. - * + * * @param [in] builder IModelBuilder to be destroyed */ CADET_API void cadetDestroyModelBuilder(cadet::IModelBuilder* const builder); @@ -93,11 +92,10 @@ extern "C" * @details Because a different memory space is assigned to dynamically loaded libraries, * memory allocated by the library has to be freed in the library. Thus, users * have to explicitly destroy their ISimulator objects here. - * + * * @param [in] sim ISimulator to be destroyed */ CADET_API void cadetDestroySimulator(cadet::ISimulator* const sim); } - -#endif // LIBCADET_FACTORYFUNCS_HPP_ +#endif // LIBCADET_FACTORYFUNCS_HPP_ diff --git a/include/cadet/HashUtil.hpp b/include/cadet/HashUtil.hpp index 6417c7c59..ca4b14c7e 100644 --- a/include/cadet/HashUtil.hpp +++ b/include/cadet/HashUtil.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides helper functions for hashing. */ @@ -28,197 +28,193 @@ namespace cadet { - /** - * @brief Combines a hash value into a cumulative hash value - * @details Based on HashLen16() function taken from CityHash (Copyright by Google Inc., - * see https://github.com/google/cityhash) protected by the MIT license. - * This is taken from http://stackoverflow.com/a/8980550 - * @todo Check if this works on 32 bit systems - * - * @param [in,out] seed Cumulative hash value to which the given element is added - * @param [in] hash Hash value added to the cumulative hash - */ - inline void hash_combine(uint64_t& seed, uint64_t hash) CADET_NOEXCEPT - { - const uint64_t kMul = 0x9ddfea08eb382d69ULL; - uint64_t a = (hash ^ seed) * kMul; - a ^= (a >> 47); - uint64_t b = (seed ^ a) * kMul; - b ^= (b >> 47); - seed = b * kMul; - } - - /** - * @brief Combines an element into a cumulative hash value - * @details Based on HashLen16() function taken from CityHash (Copyright by Google Inc., - * see https://github.com/google/cityhash) protected by the MIT license. - * The element to be added is hashed using std::hash template. - * - * @param [in,out] seed Cumulative hash value to which the given element is added - * @param [in] v Value added to the cumulative hash - * @tparam T Type of the element to be added to the hash - */ - template - inline void hash_combine(uint64_t& seed, const T& v) CADET_NOEXCEPT - { - std::hash hasher; - hash_combine(seed, hasher(v)); - } +/** + * @brief Combines a hash value into a cumulative hash value + * @details Based on HashLen16() function taken from CityHash (Copyright by Google Inc., + * see https://github.com/google/cityhash) protected by the MIT license. + * This is taken from http://stackoverflow.com/a/8980550 + * @todo Check if this works on 32 bit systems + * + * @param [in,out] seed Cumulative hash value to which the given element is added + * @param [in] hash Hash value added to the cumulative hash + */ +inline void hash_combine(uint64_t& seed, uint64_t hash) CADET_NOEXCEPT +{ + const uint64_t kMul = 0x9ddfea08eb382d69ULL; + uint64_t a = (hash ^ seed) * kMul; + a ^= (a >> 47); + uint64_t b = (seed ^ a) * kMul; + b ^= (b >> 47); + seed = b * kMul; +} + +/** + * @brief Combines an element into a cumulative hash value + * @details Based on HashLen16() function taken from CityHash (Copyright by Google Inc., + * see https://github.com/google/cityhash) protected by the MIT license. + * The element to be added is hashed using std::hash template. + * + * @param [in,out] seed Cumulative hash value to which the given element is added + * @param [in] v Value added to the cumulative hash + * @tparam T Type of the element to be added to the hash + */ +template inline void hash_combine(uint64_t& seed, const T& v) CADET_NOEXCEPT +{ + std::hash hasher; + hash_combine(seed, hasher(v)); +} #ifdef CADET_COMPILER_CXX_VARIADIC_TEMPLATES - // Hides implementation of the pack_ints template function - namespace impl - { - template - CADET_CONSTEXPR inline uint64_t pack_ints_impl(uint64_t val, std::size_t cumSize, T v) CADET_NOEXCEPT - { - // End of the recursive computation - return val | (static_cast(v) << cumSize); - } - - template - CADET_CONSTEXPR inline uint64_t pack_ints_impl(uint64_t val, std::size_t cumSize, T first, Args... args) CADET_NOEXCEPT - { - // Take current item, shift it to the corresponding position, and combine it with the result up to this point - // Increment the position by the size of the item - // Invoke the process once more for the next item - return pack_ints_impl(val | (static_cast(first) << cumSize), cumSize + std::numeric_limits::digits, args...); - } - } - - /** - * @brief Packs given integers in a bigger integer of type uint64_t - * @details Uses bit shifting to pack some small integers into a bigger one. - * The user has to make sure that the small integers fit (i.e., they - * do not exceed the size of the destination integer). - * The first item occupies the lowest bits and the last item is - * shifted to the higher bits (according to the size of all items). - * @todo Determine return type via template meta programming - * - * @param args Integers to pack - * @return Packed integer - */ - template - CADET_CONSTEXPR inline uint64_t pack_ints(Args... args) CADET_NOEXCEPT - { - return impl::pack_ints_impl(0u, 0, args...); - } +// Hides implementation of the pack_ints template function +namespace impl +{ +template +CADET_CONSTEXPR inline uint64_t pack_ints_impl(uint64_t val, std::size_t cumSize, T v) CADET_NOEXCEPT +{ + // End of the recursive computation + return val | (static_cast(v) << cumSize); +} + +template +CADET_CONSTEXPR inline uint64_t pack_ints_impl(uint64_t val, std::size_t cumSize, T first, Args... args) CADET_NOEXCEPT +{ + // Take current item, shift it to the corresponding position, and combine it with the result up to this point + // Increment the position by the size of the item + // Invoke the process once more for the next item + return pack_ints_impl(val | (static_cast(first) << cumSize), cumSize + std::numeric_limits::digits, + args...); +} +} // namespace impl + +/** + * @brief Packs given integers in a bigger integer of type uint64_t + * @details Uses bit shifting to pack some small integers into a bigger one. + * The user has to make sure that the small integers fit (i.e., they + * do not exceed the size of the destination integer). + * The first item occupies the lowest bits and the last item is + * shifted to the higher bits (according to the size of all items). + * @todo Determine return type via template meta programming + * + * @param args Integers to pack + * @return Packed integer + */ +template CADET_CONSTEXPR inline uint64_t pack_ints(Args... args) CADET_NOEXCEPT +{ + return impl::pack_ints_impl(0u, 0, args...); +} #else - /** - * @brief Packs given integers in a bigger integer of type uint64_t - * @details Uses bit shifting to pack some small integers into a bigger one. - * The user has to make sure that the small integers fit (i.e., they - * do not exceed the size of the destination integer). - * The first item occupies the lowest bits and the last item is - * shifted to the higher bits (according to the size of all items). - * @todo Determine return type via template meta programming - * - * @param a1 Integers to pack - * @return Packed integer - */ - template - CADET_CONSTEXPR inline uint64_t pack_ints(A1 a1) CADET_NOEXCEPT - { - return static_cast(a1); - } - - /** - * @brief Packs given integers in a bigger integer of type uint64_t - * @details Uses bit shifting to pack some small integers into a bigger one. - * The user has to make sure that the small integers fit (i.e., they - * do not exceed the size of the destination integer). - * The first item occupies the lowest bits and the last item is - * shifted to the higher bits (according to the size of all items). - * @todo Determine return type via template meta programming - * - * @param a1 Integers to pack - * @param a2 Integers to pack - * @return Packed integer - */ - template - CADET_CONSTEXPR inline uint64_t pack_ints(A1 a1, A2 a2) CADET_NOEXCEPT - { - return (static_cast(a1) << 0) | - (static_cast(a2) << std::numeric_limits::digits); - } - - /** - * @brief Packs given integers in a bigger integer of type uint64_t - * @details Uses bit shifting to pack some small integers into a bigger one. - * The user has to make sure that the small integers fit (i.e., they - * do not exceed the size of the destination integer). - * The first item occupies the lowest bits and the last item is - * shifted to the higher bits (according to the size of all items). - * @todo Determine return type via template meta programming - * - * @param a1 Integers to pack - * @param a2 Integers to pack - * @param a3 Integers to pack - * @return Packed integer - */ - template - CADET_CONSTEXPR inline uint64_t pack_ints(A1 a1, A2 a2, A3 a3) CADET_NOEXCEPT - { - return (static_cast(a1) << 0) | - (static_cast(a2) << std::numeric_limits::digits) | - (static_cast(a3) << (std::numeric_limits::digits + std::numeric_limits::digits)); - } - - /** - * @brief Packs given integers in a bigger integer of type uint64_t - * @details Uses bit shifting to pack some small integers into a bigger one. - * The user has to make sure that the small integers fit (i.e., they - * do not exceed the size of the destination integer). - * The first item occupies the lowest bits and the last item is - * shifted to the higher bits (according to the size of all items). - * @todo Determine return type via template meta programming - * - * @param a1 Integers to pack - * @param a2 Integers to pack - * @param a3 Integers to pack - * @param a4 Integers to pack - * @return Packed integer - */ - template - CADET_CONSTEXPR inline uint64_t pack_ints(A1 a1, A2 a2, A3 a3, A4 a4) CADET_NOEXCEPT - { - return (static_cast(a1) << 0) | - (static_cast(a2) << std::numeric_limits::digits) | - (static_cast(a3) << (std::numeric_limits::digits + std::numeric_limits::digits)) | - (static_cast(a4) << (std::numeric_limits::digits + std::numeric_limits::digits + std::numeric_limits::digits)); - } - - /** - * @brief Packs given integers in a bigger integer of type uint64_t - * @details Uses bit shifting to pack some small integers into a bigger one. - * The user has to make sure that the small integers fit (i.e., they - * do not exceed the size of the destination integer). - * The first item occupies the lowest bits and the last item is - * shifted to the higher bits (according to the size of all items). - * @todo Determine return type via template meta programming - * - * @param a1 Integers to pack - * @param a2 Integers to pack - * @param a3 Integers to pack - * @param a4 Integers to pack - * @param a5 Integers to pack - * @return Packed integer - */ - template - CADET_CONSTEXPR inline uint64_t pack_ints(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) CADET_NOEXCEPT - { - return (static_cast(a1) << 0) | - (static_cast(a2) << std::numeric_limits::digits) | - (static_cast(a3) << (std::numeric_limits::digits + std::numeric_limits::digits)) | - (static_cast(a4) << (std::numeric_limits::digits + std::numeric_limits::digits + std::numeric_limits::digits)) | - (static_cast(a5) << (std::numeric_limits::digits + std::numeric_limits::digits + std::numeric_limits::digits + std::numeric_limits::digits)); - } +/** + * @brief Packs given integers in a bigger integer of type uint64_t + * @details Uses bit shifting to pack some small integers into a bigger one. + * The user has to make sure that the small integers fit (i.e., they + * do not exceed the size of the destination integer). + * The first item occupies the lowest bits and the last item is + * shifted to the higher bits (according to the size of all items). + * @todo Determine return type via template meta programming + * + * @param a1 Integers to pack + * @return Packed integer + */ +template CADET_CONSTEXPR inline uint64_t pack_ints(A1 a1) CADET_NOEXCEPT +{ + return static_cast(a1); +} + +/** + * @brief Packs given integers in a bigger integer of type uint64_t + * @details Uses bit shifting to pack some small integers into a bigger one. + * The user has to make sure that the small integers fit (i.e., they + * do not exceed the size of the destination integer). + * The first item occupies the lowest bits and the last item is + * shifted to the higher bits (according to the size of all items). + * @todo Determine return type via template meta programming + * + * @param a1 Integers to pack + * @param a2 Integers to pack + * @return Packed integer + */ +template CADET_CONSTEXPR inline uint64_t pack_ints(A1 a1, A2 a2) CADET_NOEXCEPT +{ + return (static_cast(a1) << 0) | (static_cast(a2) << std::numeric_limits::digits); +} + +/** + * @brief Packs given integers in a bigger integer of type uint64_t + * @details Uses bit shifting to pack some small integers into a bigger one. + * The user has to make sure that the small integers fit (i.e., they + * do not exceed the size of the destination integer). + * The first item occupies the lowest bits and the last item is + * shifted to the higher bits (according to the size of all items). + * @todo Determine return type via template meta programming + * + * @param a1 Integers to pack + * @param a2 Integers to pack + * @param a3 Integers to pack + * @return Packed integer + */ +template +CADET_CONSTEXPR inline uint64_t pack_ints(A1 a1, A2 a2, A3 a3) CADET_NOEXCEPT +{ + return (static_cast(a1) << 0) | (static_cast(a2) << std::numeric_limits::digits) | + (static_cast(a3) << (std::numeric_limits::digits + std::numeric_limits::digits)); +} + +/** + * @brief Packs given integers in a bigger integer of type uint64_t + * @details Uses bit shifting to pack some small integers into a bigger one. + * The user has to make sure that the small integers fit (i.e., they + * do not exceed the size of the destination integer). + * The first item occupies the lowest bits and the last item is + * shifted to the higher bits (according to the size of all items). + * @todo Determine return type via template meta programming + * + * @param a1 Integers to pack + * @param a2 Integers to pack + * @param a3 Integers to pack + * @param a4 Integers to pack + * @return Packed integer + */ +template +CADET_CONSTEXPR inline uint64_t pack_ints(A1 a1, A2 a2, A3 a3, A4 a4) CADET_NOEXCEPT +{ + return (static_cast(a1) << 0) | (static_cast(a2) << std::numeric_limits::digits) | + (static_cast(a3) << (std::numeric_limits::digits + std::numeric_limits::digits)) | + (static_cast(a4) << (std::numeric_limits::digits + std::numeric_limits::digits + + std::numeric_limits::digits)); +} + +/** + * @brief Packs given integers in a bigger integer of type uint64_t + * @details Uses bit shifting to pack some small integers into a bigger one. + * The user has to make sure that the small integers fit (i.e., they + * do not exceed the size of the destination integer). + * The first item occupies the lowest bits and the last item is + * shifted to the higher bits (according to the size of all items). + * @todo Determine return type via template meta programming + * + * @param a1 Integers to pack + * @param a2 Integers to pack + * @param a3 Integers to pack + * @param a4 Integers to pack + * @param a5 Integers to pack + * @return Packed integer + */ +template +CADET_CONSTEXPR inline uint64_t pack_ints(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) CADET_NOEXCEPT +{ + return (static_cast(a1) << 0) | (static_cast(a2) << std::numeric_limits::digits) | + (static_cast(a3) << (std::numeric_limits::digits + std::numeric_limits::digits)) | + (static_cast(a4) << (std::numeric_limits::digits + std::numeric_limits::digits + + std::numeric_limits::digits)) | + (static_cast(a5) << (std::numeric_limits::digits + std::numeric_limits::digits + + std::numeric_limits::digits + std::numeric_limits::digits)); +} #endif } // namespace cadet -#endif // LIBCADET_HASHUTIL_HPP_ +#endif // LIBCADET_HASHUTIL_HPP_ diff --git a/include/cadet/InletProfile.hpp b/include/cadet/InletProfile.hpp index 358430bbf..1380e85f4 100644 --- a/include/cadet/InletProfile.hpp +++ b/include/cadet/InletProfile.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines an inlet profile. */ @@ -35,12 +35,14 @@ class IParameterProvider; class CADET_API IInletProfile { public: - virtual ~IInletProfile() CADET_NOEXCEPT { } + virtual ~IInletProfile() CADET_NOEXCEPT + { + } /** * @brief Configures the inlet profile by extracting all parameters from the given @p paramProvider * @details The scope of the cadet::IParameterProvider is left unchanged on return. - * + * * @param [in] paramProvider Pointer to parameter provider (may be @c nullptr) * @param [in] nComp Number of components * @return @c true if the configuration was successful, otherwise @c false @@ -70,10 +72,11 @@ class CADET_API IInletProfile /** * @brief Returns the inlet concentration at a given time for all components - * + * * @param [in] t Absolute simulation time * @param [in] sec Index of the current time section - * @param [out] inletConc Pointer to first element of contiguous array receiving the inlet concentration of all components + * @param [out] inletConc Pointer to first element of contiguous array receiving the inlet concentration of all + * components */ virtual void inletConcentration(double t, unsigned int sec, double* inletConc) = 0; @@ -81,20 +84,22 @@ class CADET_API IInletProfile * @brief Returns the derivative of all components with respect to a given parameter * @details The given parameter @p id matches one of the availableParameters() (when unit operation id is ignored). * In other words, this function is only called for parameters that belong to this IInletProfile. - * + * * @param [in] t Absolute simulation time * @param [in] sec Index of the current time section * @param [in] pId ID of the parameter to be differentiated with respect to - * @param [out] paramDeriv Pointer to first element of contiguous array receiving the parameter derivative of all components + * @param [out] paramDeriv Pointer to first element of contiguous array receiving the parameter derivative of all + * components */ virtual void parameterDerivative(double t, unsigned int sec, const ParameterId& pId, double* paramDeriv) = 0; /** * @brief Returns the time derivative of all components - * + * * @param [in] t Absolute simulation time * @param [in] sec Index of the current time section - * @param [out] timeDerivative Pointer to first element of contiguous array receiving the time derivative of all components + * @param [out] timeDerivative Pointer to first element of contiguous array receiving the time derivative of all + * components */ virtual void timeDerivative(double t, unsigned int sec, double* timeDerivative) = 0; @@ -102,7 +107,7 @@ class CADET_API IInletProfile * @brief Returns the second derivative of all components with respect to a given parameter and time * @details The given parameter @p id matches one of the availableParameters() (when unit operation id is ignored). * In other words, this function is only called for parameters that belong to this IInletProfile. - * + * * @param [in] t Absolute simulation time * @param [in] sec Index of the current time section * @param [in] pId ID of the parameter to be differentiated with respect to @@ -113,7 +118,7 @@ class CADET_API IInletProfile /** * @brief Returns the value of the given parameter * @details The given parameter @p id matches one of the availableParameters() (when unit operation id is ignored). - * + * * @param [in] id Parameter ID of the parameter to be returned * @return Value of the queried parameter */ @@ -135,7 +140,7 @@ class CADET_API IInletProfile * provide means to implement discontinuous behavior (e.g., pulse injection profiles, * switching of valves). After initialization, the simulator notifies all entities * such as models or data sources of its section times. - * + * * The vector of section times consists of strictly increasing time points * @f[ t_0 < t_1 < t_2 < \dots t_N @f] * which mark the beginning and end of a section. The @f$ i@f$-th section is given by @@ -143,17 +148,18 @@ class CADET_API IInletProfile * If a transition from one section to the next is continuous, the @p secContinuity flag * for that transition is @c true. In this case, the time integrator will not stop at the * transition time point and reinitialize consistently (which will be done for discontinuous - * transitions). - * + * transitions). + * * @param [in] secTimes Vector with section time points (length is @p nSections + 1) - * @param [in] secContinuity Vector of flags that indicate a continuous (@c true) or discontinuous (@c false) + * @param [in] secContinuity Vector of flags that indicate a continuous (@c true) or discontinuous (@c false) * transition from the current section to the next one (length is @p nSections - 1). For instance, * the first element indicates whether the transition from section @c 0 to @c 1 is continuous. * @param [in] nSections Number of sections */ - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) CADET_NOEXCEPT = 0; + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, + unsigned int nSections) CADET_NOEXCEPT = 0; }; } // namespace cadet -#endif // LIBCADET_INLETPROFILE_HPP_ +#endif // LIBCADET_INLETPROFILE_HPP_ diff --git a/include/cadet/LibExportImport.hpp b/include/cadet/LibExportImport.hpp index b7da48a3d..20ef8eff9 100644 --- a/include/cadet/LibExportImport.hpp +++ b/include/cadet/LibExportImport.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,25 +11,24 @@ // ============================================================================= /** - * @file + * @file * Defines preprocessor macros for importing and exporting symbols from and to dynamic libraries. */ #ifndef LIBCADET_LIBEXPORT_HPP_ #define LIBCADET_LIBEXPORT_HPP_ - // Export and import classes when using MS Visual Studio compiler #ifndef CADET_API - #ifdef _MSC_VER - #if defined(libcadet_shared_EXPORTS) || defined(libcadet_static_EXPORTS) || defined(libcadet_EXPORTS) - #define CADET_API _declspec(dllexport) - #else - #define CADET_API _declspec(dllimport) - #endif - #else - #define CADET_API __attribute__((visibility("default"))) - #endif +#ifdef _MSC_VER +#if defined(libcadet_shared_EXPORTS) || defined(libcadet_static_EXPORTS) || defined(libcadet_EXPORTS) +#define CADET_API _declspec(dllexport) +#else +#define CADET_API _declspec(dllimport) +#endif +#else +#define CADET_API __attribute__((visibility("default"))) +#endif #endif -#endif // LIBCADET_LIBEXPORT_HPP_ +#endif // LIBCADET_LIBEXPORT_HPP_ diff --git a/include/cadet/LibVersionInfo.hpp b/include/cadet/LibVersionInfo.hpp index 59aceef3e..3bc337aa5 100644 --- a/include/cadet/LibVersionInfo.hpp +++ b/include/cadet/LibVersionInfo.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides version info. */ @@ -24,63 +24,63 @@ namespace cadet { - /** - * @brief Returns the version string of the libcadet library - * @sa cadetGetLibraryVersion() - * @return Version string - */ - CADET_API const char* getLibraryVersion() CADET_NOEXCEPT; +/** + * @brief Returns the version string of the libcadet library + * @sa cadetGetLibraryVersion() + * @return Version string + */ +CADET_API const char* getLibraryVersion() CADET_NOEXCEPT; - /** - * @brief Returns the git commit hash of the source which was used to build the binaries - * @sa cadetGetLibraryCommitHash() - * @return Git commit hash as string - */ - CADET_API const char* getLibraryCommitHash() CADET_NOEXCEPT; +/** + * @brief Returns the git commit hash of the source which was used to build the binaries + * @sa cadetGetLibraryCommitHash() + * @return Git commit hash as string + */ +CADET_API const char* getLibraryCommitHash() CADET_NOEXCEPT; - /** - * @brief Returns the git refspec of the source which was used to build the binaries - * @sa cadetGetLibraryBranchRefspec() - * @return Git refspec - */ - CADET_API const char* getLibraryBranchRefspec() CADET_NOEXCEPT; +/** + * @brief Returns the git refspec of the source which was used to build the binaries + * @sa cadetGetLibraryBranchRefspec() + * @return Git refspec + */ +CADET_API const char* getLibraryBranchRefspec() CADET_NOEXCEPT; - /** - * @brief Returns the versions of the dependencies used for building the binaries - * @details The format is DEPNAME1=VERSION;DEPNAME2=VERSION; where each dependency is - * terminated by a semicolon. - * @sa cadetGetLibraryDependencyVersions() - * @return Dependency versions string - */ - CADET_API const char* getLibraryDependencyVersions() CADET_NOEXCEPT; +/** + * @brief Returns the versions of the dependencies used for building the binaries + * @details The format is DEPNAME1=VERSION;DEPNAME2=VERSION; where each dependency is + * terminated by a semicolon. + * @sa cadetGetLibraryDependencyVersions() + * @return Dependency versions string + */ +CADET_API const char* getLibraryDependencyVersions() CADET_NOEXCEPT; - /** - * @brief Returns the build type (Debug, Release, RelWithDebInfo, RelMinSize) - * @sa cadetGetLibraryBuildType() - * @return Build type - */ - CADET_API const char* getLibraryBuildType() CADET_NOEXCEPT; +/** + * @brief Returns the build type (Debug, Release, RelWithDebInfo, RelMinSize) + * @sa cadetGetLibraryBuildType() + * @return Build type + */ +CADET_API const char* getLibraryBuildType() CADET_NOEXCEPT; - /** - * @brief Returns the compiler including its version used for building the library - * @sa cadetGetLibraryCompiler() - * @return Compiler and its version - */ - CADET_API const char* getLibraryCompiler() CADET_NOEXCEPT; +/** + * @brief Returns the compiler including its version used for building the library + * @sa cadetGetLibraryCompiler() + * @return Compiler and its version + */ +CADET_API const char* getLibraryCompiler() CADET_NOEXCEPT; - /** - * @brief Returns the compiler flags used for building the library - * @sa cadetGetLibraryCompilerFlags() - * @return Compiler flags - */ - CADET_API const char* getLibraryCompilerFlags() CADET_NOEXCEPT; +/** + * @brief Returns the compiler flags used for building the library + * @sa cadetGetLibraryCompilerFlags() + * @return Compiler flags + */ +CADET_API const char* getLibraryCompilerFlags() CADET_NOEXCEPT; - /** - * @brief Returns the git refspec of the source which was used to build the binaries - * @sa cadetGetLibraryBuildHost() - * @return Git refspec - */ - CADET_API const char* getLibraryBuildHost() CADET_NOEXCEPT; +/** + * @brief Returns the git refspec of the source which was used to build the binaries + * @sa cadetGetLibraryBuildHost() + * @return Git refspec + */ +CADET_API const char* getLibraryBuildHost() CADET_NOEXCEPT; } // namespace cadet -#endif // LIBCADET_LIBVERSIONINFO_HPP_ +#endif // LIBCADET_LIBVERSIONINFO_HPP_ diff --git a/include/cadet/Logging.hpp b/include/cadet/Logging.hpp index 37da67935..824d37436 100644 --- a/include/cadet/Logging.hpp +++ b/include/cadet/Logging.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides logging functionality. */ @@ -24,145 +24,148 @@ #include namespace cadet +{ +/** + * @brief LogLevel represents the severity of log messages + * @details The levels are nested, such that the highest level (Trace) includes all lower levels. + */ +enum class LogLevel : unsigned int { /** - * @brief LogLevel represents the severity of log messages - * @details The levels are nested, such that the highest level (Trace) includes all lower levels. + * @brief Nothing is logged */ - enum class LogLevel : unsigned int - { - /** - * @brief Nothing is logged - */ - None = 0, - /** - * @brief Non-recoverable (fatal) error (terminates program / simulation) - */ - Fatal = 1, - /** - * @brief Error, which may be recoverable - */ - Error = 2, - /** - * @brief Warning - */ - Warning = 3, - /** - * @brief Normal output (informative) - */ - Normal = 4, - /** - * @brief Additional info output - */ - Info = 5, - /** - * @brief Debug messages and values of (intermediate) variables or calculations - */ - Debug = 6, - /** - * @brief Full trace including entering / leaving functions - */ - Trace = 7, - }; - + None = 0, /** - * @brief Converts a LogLevel to a string - * @param [in] lvl LogLevel to be converted - * @return String representation of the LogLevel + * @brief Non-recoverable (fatal) error (terminates program / simulation) */ - inline const char* to_string(LogLevel lvl) CADET_NOEXCEPT - { - switch (lvl) - { - case LogLevel::None: - return "None"; - case LogLevel::Fatal: - return "Fatal"; - case LogLevel::Error: - return "Error"; - case LogLevel::Warning: - return "Warning"; - case LogLevel::Normal: - return "Normal"; - case LogLevel::Info: - return "Info"; - case LogLevel::Debug: - return "Debug"; - case LogLevel::Trace: - return "Trace"; - } - return "Unkown"; - } - + Fatal = 1, + /** + * @brief Error, which may be recoverable + */ + Error = 2, + /** + * @brief Warning + */ + Warning = 3, /** - * @brief Converts a string to a LogLevel - * @param [in] ll LogLevel as string - * @return LogLevel corresponding to the given string + * @brief Normal output (informative) */ - inline LogLevel to_loglevel(const std::string& ll) CADET_NOEXCEPT + Normal = 4, + /** + * @brief Additional info output + */ + Info = 5, + /** + * @brief Debug messages and values of (intermediate) variables or calculations + */ + Debug = 6, + /** + * @brief Full trace including entering / leaving functions + */ + Trace = 7, +}; + +/** + * @brief Converts a LogLevel to a string + * @param [in] lvl LogLevel to be converted + * @return String representation of the LogLevel + */ +inline const char* to_string(LogLevel lvl) CADET_NOEXCEPT +{ + switch (lvl) { - if (ll == "None") - return LogLevel::None; - else if (ll == "Fatal") - return LogLevel::Fatal; - else if (ll == "Error") - return LogLevel::Error; - else if (ll == "Warning") - return LogLevel::Warning; - else if (ll == "Normal") - return LogLevel::Normal; - else if (ll == "Info") - return LogLevel::Info; - else if (ll == "Debug") - return LogLevel::Debug; - else if (ll == "Trace") - return LogLevel::Trace; + case LogLevel::None: + return "None"; + case LogLevel::Fatal: + return "Fatal"; + case LogLevel::Error: + return "Error"; + case LogLevel::Warning: + return "Warning"; + case LogLevel::Normal: + return "Normal"; + case LogLevel::Info: + return "Info"; + case LogLevel::Debug: + return "Debug"; + case LogLevel::Trace: + return "Trace"; + } + return "Unkown"; +} +/** + * @brief Converts a string to a LogLevel + * @param [in] ll LogLevel as string + * @return LogLevel corresponding to the given string + */ +inline LogLevel to_loglevel(const std::string& ll) CADET_NOEXCEPT +{ + if (ll == "None") return LogLevel::None; - } + else if (ll == "Fatal") + return LogLevel::Fatal; + else if (ll == "Error") + return LogLevel::Error; + else if (ll == "Warning") + return LogLevel::Warning; + else if (ll == "Normal") + return LogLevel::Normal; + else if (ll == "Info") + return LogLevel::Info; + else if (ll == "Debug") + return LogLevel::Debug; + else if (ll == "Trace") + return LogLevel::Trace; - /** - * @brief Interface for receiving log messages - */ - class CADET_API ILogReceiver - { - public: - virtual ~ILogReceiver() CADET_NOEXCEPT { } + return LogLevel::None; +} - /** - * @brief Receives a log message - * @param [in] file Filename in which the log message was raised - * @param [in] func Name of the function (implementation defined @c __func__ variable) - * @param [in] line Number of the line in which the log message was raised - * @param [in] lvl LogLevel representing the severity of the message - * @param [in] lvlStr String representation of the log level - * @param [in] message Message string - * @sa LogLevel - */ - virtual void message(const char* file, const char* func, const unsigned int line, LogLevel lvl, const char* lvlStr, const char* message) = 0; - }; +/** + * @brief Interface for receiving log messages + */ +class CADET_API ILogReceiver +{ +public: + virtual ~ILogReceiver() CADET_NOEXCEPT + { + } /** - * @brief Sets the log receiver replacing any previously set receiver - * @param [in] recv Pointer to ILogReceiver implementation or @c nullptr - * @sa cadetSetLogReceiver() + * @brief Receives a log message + * @param [in] file Filename in which the log message was raised + * @param [in] func Name of the function (implementation defined @c __func__ variable) + * @param [in] line Number of the line in which the log message was raised + * @param [in] lvl LogLevel representing the severity of the message + * @param [in] lvlStr String representation of the log level + * @param [in] message Message string + * @sa LogLevel */ - CADET_API void setLogReceiver(ILogReceiver* const recv); + virtual void message(const char* file, const char* func, const unsigned int line, LogLevel lvl, const char* lvlStr, + const char* message) = 0; +}; - /** - * @brief Sets the log level - * @details All messages on a lower log level (i.e., higher severity or information content) - * are filtered out. - * @param [in] lvl New log level - */ - CADET_API void setLogLevel(LogLevel lvl); +/** + * @brief Sets the log receiver replacing any previously set receiver + * @param [in] recv Pointer to ILogReceiver implementation or @c nullptr + * @sa cadetSetLogReceiver() + */ +CADET_API void setLogReceiver(ILogReceiver* const recv); - /** - * @brief Returns the current log level - * @return Current log level - */ - CADET_API LogLevel getLogLevel(); +/** + * @brief Sets the log level + * @details All messages on a lower log level (i.e., higher severity or information content) + * are filtered out. + * @param [in] lvl New log level + */ +CADET_API void setLogLevel(LogLevel lvl); + +/** + * @brief Returns the current log level + * @return Current log level + */ +CADET_API LogLevel getLogLevel(); } // namespace cadet -#endif // LIBCADET_LOGGING_HPP_ +#endif // LIBCADET_LOGGING_HPP_ diff --git a/include/cadet/Model.hpp b/include/cadet/Model.hpp index ec84c2164..7c0ff82d9 100644 --- a/include/cadet/Model.hpp +++ b/include/cadet/Model.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines model interfaces. */ @@ -20,7 +20,7 @@ #include #ifdef CADET_BENCHMARK_MODE - #include +#include #endif #include "cadet/LibExportImport.hpp" @@ -40,8 +40,9 @@ namespace cadet class CADET_API IModel { public: - - virtual ~IModel() CADET_NOEXCEPT { } + virtual ~IModel() CADET_NOEXCEPT + { + } /** * @brief Returns the unit operation Id, which is just an index @@ -59,10 +60,10 @@ class CADET_API IModel /** * @brief Sets a parameter value * @details The parameter identified by its unique parameter is set to the given value. - * + * * @param [in] pId ParameterId that identifies the parameter uniquely * @param [in] value Value of the parameter - * + * * @return @c true if the parameter has been successfully set to the given value, * otherwise @c false (e.g., if the parameter is not available in this model) */ @@ -93,7 +94,7 @@ class CADET_API IModel /** * @brief Determines whether analytical Jacobians are used instead of AD Jacobians - * + * * @param [in] analyticJac @c true if analytic Jacobians should be used (recommended), @c false for AD Jacobians */ virtual void useAnalyticJacobian(const bool analyticJac) = 0; @@ -116,4 +117,4 @@ class CADET_API IModel } // namespace cadet -#endif // LIBCADET_MODEL_HPP_ +#endif // LIBCADET_MODEL_HPP_ diff --git a/include/cadet/ModelBuilder.hpp b/include/cadet/ModelBuilder.hpp index 7ce104611..60b0f4365 100644 --- a/include/cadet/ModelBuilder.hpp +++ b/include/cadet/ModelBuilder.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the ModelBuilder interface. */ @@ -38,15 +38,15 @@ class IModelSystem; * @brief Provides functionality to build a model * @details Builds a system of unit operation models or creates single unit operation models * from given parameters. - * + * * The IModelBuilder owns all IModelSystem it created. The ownership of a specific system * can be released by calling detachSystem(). Detached IModelSystem objects can be destroyed * by calling destroySystem(). - * + * * Note that the IModelBuilder does not own the created IModel objects. Unit operation * models are supposed to be assigned to an IModelSystem which owns them. However, wrongfully * created IModel objects can be destroyed by calling destroyUnitOperation(). - * + * * Once registered, external functions are also owned by the IModelBuilder and can be released * via detachExternalFunction(). Otherwise they will be deleted when the IModelBuilder is * deleted. The external functions are indexed by the IModelBuilder, which serves as their ID. @@ -54,8 +54,9 @@ class IModelSystem; class CADET_API IModelBuilder { public: - - virtual ~IModelBuilder() CADET_NOEXCEPT { } + virtual ~IModelBuilder() CADET_NOEXCEPT + { + } /** * @brief Automatically constructs a system of unit operations and populates it from provided parameters @@ -63,11 +64,11 @@ class CADET_API IModelBuilder * configured unit operation models that are also initialized. If one of the unit operation * models could not be constructed from the parameters or an error occurred, @c nullptr * is returned. - * + * * The created IModelSystem is owned by the creating instance of IModelBuilder. * It is automatically destroyed when the IModelBuilder that created it is destroyed. * Ownership can be transferred by calling detachSystem(). - * + * * @param [in] paramProvider ParameterProvider from which all necessary information is read * @return A fully populated system of unit operation models or @c nullptr if an error occurred */ @@ -76,7 +77,7 @@ class CADET_API IModelBuilder /** * @brief Creates an empty IModelSystem * @details The created IModelSystem is empty and not configured - * + * * The created IModelSystem is owned by the creating instance of IModelBuilder. * It is automatically destroyed when the IModelBuilder that created it is destroyed. * Ownership can be transferred by calling detachSystem(). @@ -107,11 +108,11 @@ class CADET_API IModelBuilder * initializes it with the corresponding parameter values. If the model could * not be constructed from the provided parameters or an error occurred, @c nullptr is * returned. - * + * * The created unit operation model is not owned by the IModelBuilder. * Ownership of unit operation models is handled by IModelSystem. * Unit operation models can be destroyed by calling destroyUnitOperation(). - * + * * @param [in] paramProvider ParameterProvider from which all necessary information is read * @param [in] uoId Unit operation index assigned to the created unit operation * @return A fully populated unit operation model or @c nullptr if an error occurred @@ -128,7 +129,7 @@ class CADET_API IModelBuilder // * @param [in] uoId Unit operation index assigned to the created unit operation // * @return Uninitialized unit operation model or @c nullptr if an error occurred // */ - //virtual IModel* createUnitOperation(const std::string& uoType, UnitOpIdx uoId) = 0; + // virtual IModel* createUnitOperation(const std::string& uoType, UnitOpIdx uoId) = 0; /** * @brief Destroys the given IModel @@ -151,9 +152,10 @@ class CADET_API IModelBuilder * @param [in] name Name of the external function type * @param [in] factory Factory function that creates the registered IExternalFunction */ - virtual void registerExternalFunctionType(const std::string& name, std::function factory) = 0; + virtual void registerExternalFunctionType(const std::string& name, + std::function factory) = 0; }; } // namespace cadet -#endif // LIBCADET_MODELBUILDER_HPP_ +#endif // LIBCADET_MODELBUILDER_HPP_ diff --git a/include/cadet/ModelSystem.hpp b/include/cadet/ModelSystem.hpp index f8ec2621e..7e360b739 100644 --- a/include/cadet/ModelSystem.hpp +++ b/include/cadet/ModelSystem.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines model system interfaces. */ @@ -21,7 +21,7 @@ #include #include #ifdef CADET_BENCHMARK_MODE - #include +#include #endif #include "cadet/LibExportImport.hpp" @@ -43,8 +43,9 @@ class IExternalFunction; class CADET_API IModelSystem { public: - - virtual ~IModelSystem() CADET_NOEXCEPT { } + virtual ~IModelSystem() CADET_NOEXCEPT + { + } /** * @brief Returns the highest unit operation index in the model system @@ -55,10 +56,10 @@ class CADET_API IModelSystem /** * @brief Sets a parameter value * @details The parameter identified by its unique parameter is set to the given value. - * + * * @param [in] pId ParameterId that identifies the parameter uniquely * @param [in] value Value of the parameter - * + * * @return @c true if the parameter has been successfully set to the given value, * otherwise @c false (e.g., if the parameter is not available in this model) */ @@ -164,10 +165,10 @@ class CADET_API IModelSystem virtual void removeExternalFunction(IExternalFunction const* extFun) = 0; /** - * @brief Returns the start and end indices of a unit operation's local slice in the global state vector - * @param [in] unitOp Unit operation ID for which local state position is required - * @return Start and end indices of the local slice of model @p unitOp - */ + * @brief Returns the start and end indices of a unit operation's local slice in the global state vector + * @param [in] unitOp Unit operation ID for which local state position is required + * @return Start and end indices of the local slice of model @p unitOp + */ virtual std::tuple getModelStateOffsets(UnitOpIdx unitOp) const CADET_NOEXCEPT = 0; #ifdef CADET_BENCHMARK_MODE @@ -188,4 +189,4 @@ class CADET_API IModelSystem } // namespace cadet -#endif // LIBCADET_MODELSYSTEM_HPP_ +#endif // LIBCADET_MODELSYSTEM_HPP_ diff --git a/include/cadet/Notification.hpp b/include/cadet/Notification.hpp index 1782031b2..3cc727633 100644 --- a/include/cadet/Notification.hpp +++ b/include/cadet/Notification.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines interfaces for (progress) notification. */ @@ -29,7 +29,9 @@ namespace cadet class CADET_API INotificationCallback { public: - virtual ~INotificationCallback() CADET_NOEXCEPT { } + virtual ~INotificationCallback() CADET_NOEXCEPT + { + } /** * @brief Called when time integration starts @@ -63,7 +65,8 @@ class CADET_API INotificationCallback * @param[in] progress Progress in percent (between @c 0.0 and @c 1.0) * @return @c true if time integrator should continue, otherwise @c false */ - virtual bool timeIntegrationSection(unsigned int section, double time, double const* state, double const* stateDot, double progress) = 0; + virtual bool timeIntegrationSection(unsigned int section, double time, double const* state, double const* stateDot, + double progress) = 0; /** * @brief Called when the time integrator has finished a single time step @@ -75,9 +78,10 @@ class CADET_API INotificationCallback * @param[in] progress Progress in percent (between @c 0.0 and @c 1.0) * @return @c true if time integrator should continue, otherwise @c false */ - virtual bool timeIntegrationStep(unsigned int section, double time, double const* state, double const* stateDot, double progress) = 0; + virtual bool timeIntegrationStep(unsigned int section, double time, double const* state, double const* stateDot, + double progress) = 0; }; } // namespace cadet -#endif // LIBCADET_NOTIFICATION_HPP_ +#endif // LIBCADET_NOTIFICATION_HPP_ diff --git a/include/cadet/ParameterId.hpp b/include/cadet/ParameterId.hpp index 0cfaebe92..dd19ab292 100644 --- a/include/cadet/ParameterId.hpp +++ b/include/cadet/ParameterId.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the ParameterId and provides compile time and runtime hash functions for parameters. */ @@ -69,50 +69,50 @@ typedef uint64_t ParamIdHash; #if CADET_COMPILER_CXX_CONSTEXPR - /** - * @brief Computes the string hash at compiletime - * @details Use the hashStringRuntime for runtime hashing. - * - * @param [in] str String - * @return Hash of the string - */ - constexpr inline StringHash hashString(const util::hash::ConstString& cs) - { - return util::SipHash24(cs); - } +/** + * @brief Computes the string hash at compiletime + * @details Use the hashStringRuntime for runtime hashing. + * + * @param [in] str String + * @return Hash of the string + */ +constexpr inline StringHash hashString(const util::hash::ConstString& cs) +{ + return util::SipHash24(cs); +} #else - /** - * @brief Computes the string hash at runtime - * @details Use the _hash string literal for compile time hashing. - * - * @param [in] str String - * @return Hash of the string - */ - inline StringHash hashString(const std::string& str) - { - return util::SipHash24runtime(str); - } - - /** - * @brief Computes the string hash at runtime - * @details Use the _hash string literal for compile time hashing. - * - * @param [in] str String - * @return Hash of the string - */ - inline StringHash hashString(char const* const str) - { - return util::SipHash24runtime(str); - } +/** + * @brief Computes the string hash at runtime + * @details Use the _hash string literal for compile time hashing. + * + * @param [in] str String + * @return Hash of the string + */ +inline StringHash hashString(const std::string& str) +{ + return util::SipHash24runtime(str); +} + +/** + * @brief Computes the string hash at runtime + * @details Use the _hash string literal for compile time hashing. + * + * @param [in] str String + * @return Hash of the string + */ +inline StringHash hashString(char const* const str) +{ + return util::SipHash24runtime(str); +} #endif /** * @brief Computes the string hash at runtime * @details Use the _hash string literal for compile time hashing. - * + * * @param [in] str String * @return Hash of the string */ @@ -124,7 +124,7 @@ inline StringHash hashStringRuntime(const std::string& str) /** * @brief Computes the string hash at runtime * @details Use the _hash string literal for compile time hashing. - * + * * @param [in] str String * @return Hash of the string */ @@ -136,7 +136,7 @@ inline StringHash hashStringRuntime(char const* const str) /** * @brief Computes the unique parameter Id hash for a given parameter * @details This hash function can be used at compile- and runtime. - * + * * @param [in] nameHash Hash of the parameter name computed by hashString() * @param [in] unitOperation Index of the unit operation this parameter belongs to * @param [in] component Index of the component this parameter belongs to @@ -145,11 +145,12 @@ inline StringHash hashStringRuntime(char const* const str) * @param [in] section Index of the section this parameter belongs to * @return Unique parameter id hash */ -inline ParamIdHash hashParameter(const StringHash nameHash, const UnitOpIdx unitOperation, const ComponentIdx component, - const ParticleTypeIdx parType, const BoundStateIdx boundState, const ReactionIdx reaction, const SectionIdx section) +inline ParamIdHash hashParameter(const StringHash nameHash, const UnitOpIdx unitOperation, const ComponentIdx component, + const ParticleTypeIdx parType, const BoundStateIdx boundState, + const ReactionIdx reaction, const SectionIdx section) { ParamIdHash hash = nameHash; - + // Combine indices in one big integer by shifting them // Note that ParamIdHash has to be big enough to hold all indices const ParamIdHash combinedIdx = pack_ints(unitOperation, component, parType, boundState, reaction, section); @@ -161,7 +162,7 @@ inline ParamIdHash hashParameter(const StringHash nameHash, const UnitOpIdx unit /** * @brief Computes the unique parameter Id hash for a given parameter * @details This hash function can be used at compile- and runtime. - * + * * @param [in] name Name of the parameter * @param [in] unitOperation Index of the unit operation this parameter belongs to * @param [in] component Index of the component this parameter belongs to @@ -170,8 +171,9 @@ inline ParamIdHash hashParameter(const StringHash nameHash, const UnitOpIdx unit * @param [in] section Index of the section this parameter belongs to * @return Unique parameter id hash */ -inline ParamIdHash hashParameter(const std::string& name, const UnitOpIdx unitOperation, const ComponentIdx component, - const ParticleTypeIdx parType, const BoundStateIdx boundState, const ReactionIdx reaction, const SectionIdx section) +inline ParamIdHash hashParameter(const std::string& name, const UnitOpIdx unitOperation, const ComponentIdx component, + const ParticleTypeIdx parType, const BoundStateIdx boundState, + const ReactionIdx reaction, const SectionIdx section) { return hashParameter(hashStringRuntime(name), unitOperation, component, parType, boundState, reaction, section); } @@ -179,18 +181,19 @@ inline ParamIdHash hashParameter(const std::string& name, const UnitOpIdx unitOp /** * @brief Computes the unique parameter Id hash for a given parameter * @details This hash function is supposed to be used at runtime only. - * + * * @param [in] param Parameter Id * @return Unique parameter id hash */ inline ParamIdHash hashParameter(const ParameterId& param) { - return hashParameter(param.name, param.unitOperation, param.component, param.particleType, param.boundState, param.reaction, param.section); + return hashParameter(param.name, param.unitOperation, param.component, param.particleType, param.boundState, + param.reaction, param.section); } /** * @brief Builds the unique ParameterId for a given parameter - * + * * @param [in] name Name of the parameter * @param [in] unitOperation Index of the unit operation this parameter belongs to * @param [in] component Index of the component this parameter belongs to @@ -199,15 +202,16 @@ inline ParamIdHash hashParameter(const ParameterId& param) * @param [in] section Index of the section this parameter belongs to * @return Unique parameter id */ -inline ParameterId makeParamId(const std::string& name, const UnitOpIdx unitOperation, const ComponentIdx component, - const ParticleTypeIdx parType, const BoundStateIdx boundState, const ReactionIdx reaction, const SectionIdx section) +inline ParameterId makeParamId(const std::string& name, const UnitOpIdx unitOperation, const ComponentIdx component, + const ParticleTypeIdx parType, const BoundStateIdx boundState, + const ReactionIdx reaction, const SectionIdx section) { - return ParameterId{ hashStringRuntime(name), unitOperation, component, parType, boundState, reaction, section }; + return ParameterId{hashStringRuntime(name), unitOperation, component, parType, boundState, reaction, section}; } /** * @brief Builds the unique ParameterId for a given parameter - * + * * @param [in] name Hash of the parameter name * @param [in] unitOperation Index of the unit operation this parameter belongs to * @param [in] component Index of the component this parameter belongs to @@ -216,12 +220,13 @@ inline ParameterId makeParamId(const std::string& name, const UnitOpIdx unitOper * @param [in] section Index of the section this parameter belongs to * @return Unique parameter id */ -inline ParameterId makeParamId(const StringHash name, const UnitOpIdx unitOperation, const ComponentIdx component, - const ParticleTypeIdx parType, const BoundStateIdx boundState, const ReactionIdx reaction, const SectionIdx section) +inline ParameterId makeParamId(const StringHash name, const UnitOpIdx unitOperation, const ComponentIdx component, + const ParticleTypeIdx parType, const BoundStateIdx boundState, + const ReactionIdx reaction, const SectionIdx section) { - return ParameterId{ name, unitOperation, component, parType, boundState, reaction, section }; + return ParameterId{name, unitOperation, component, parType, boundState, reaction, section}; } } // namespace cadet -#endif // LIBCADET_PARAMETERID_HPP_ +#endif // LIBCADET_PARAMETERID_HPP_ diff --git a/include/cadet/ParameterProvider.hpp b/include/cadet/ParameterProvider.hpp index d9d01711d..d590273db 100644 --- a/include/cadet/ParameterProvider.hpp +++ b/include/cadet/ParameterProvider.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -29,12 +29,13 @@ namespace cadet class CADET_API IParameterProvider { public: - - virtual ~IParameterProvider() CADET_NOEXCEPT { } + virtual ~IParameterProvider() CADET_NOEXCEPT + { + } /** * @brief Returns the value of a parameter of type double - * + * * @param [in] paramName Name of the parameter * @return Parameter value */ @@ -42,7 +43,7 @@ class CADET_API IParameterProvider /** * @brief Returns the value of a parameter of type int - * + * * @param [in] paramName Name of the parameter * @return Parameter value */ @@ -50,7 +51,7 @@ class CADET_API IParameterProvider /** * @brief Returns the value of a parameter of type uint64_t - * + * * @param [in] paramName Name of the parameter * @return Parameter value */ @@ -58,7 +59,7 @@ class CADET_API IParameterProvider /** * @brief Returns the value of a parameter of type bool - * + * * @param [in] paramName Name of the parameter * @return Parameter value */ @@ -66,7 +67,7 @@ class CADET_API IParameterProvider /** * @brief Returns the value of a parameter of type string - * + * * @param [in] paramName Name of the parameter * @return Parameter value */ @@ -74,7 +75,7 @@ class CADET_API IParameterProvider /** * @brief Returns a parameter array of type double - * + * * @param [in] paramName Name of the parameter * @return Parameter values */ @@ -82,7 +83,7 @@ class CADET_API IParameterProvider /** * @brief Returns a parameter array of type int - * + * * @param [in] paramName Name of the parameter * @return Parameter values */ @@ -90,7 +91,7 @@ class CADET_API IParameterProvider /** * @brief Returns a parameter array of type uint64_t - * + * * @param [in] paramName Name of the parameter * @return Parameter values */ @@ -98,7 +99,7 @@ class CADET_API IParameterProvider /** * @brief Returns a parameter array of type bool - * + * * @param [in] paramName Name of the parameter * @return Parameter values */ @@ -106,7 +107,7 @@ class CADET_API IParameterProvider /** * @brief Returns a parameter array of type string - * + * * @param [in] paramName Name of the parameter * @return Parameter values */ @@ -114,7 +115,7 @@ class CADET_API IParameterProvider /** * @brief Checks whether a given parameter exists - * + * * @param [in] paramName Name of the parameter * @return @c true if the parameter exists, otherwise @c false */ @@ -122,7 +123,7 @@ class CADET_API IParameterProvider /** * @brief Checks whether a given parameter is an array - * + * * @param [in] paramName Name of the parameter * @return @c true if the parameter is an array, otherwise @c false */ @@ -150,4 +151,4 @@ class CADET_API IParameterProvider } // namespace cadet -#endif // LIBCADET_PARAMPROVIDER_HPP_ +#endif // LIBCADET_PARAMPROVIDER_HPP_ diff --git a/include/cadet/Simulator.hpp b/include/cadet/Simulator.hpp index 94ccbe216..fae8b777e 100644 --- a/include/cadet/Simulator.hpp +++ b/include/cadet/Simulator.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the Simulator interface. */ @@ -58,15 +58,18 @@ enum class ConsistentInitialization : int */ LeanFirstOnly = 4, /** - * @brief Perform full consistent initialization at the beginning and lean initialization on every following discontinuous section transition + * @brief Perform full consistent initialization at the beginning and lean initialization on every following + * discontinuous section transition */ FullOnceThenLean = 5, /** - * @brief Do not initialize at the beginning but perform full initialization on every following discontinuous section transition + * @brief Do not initialize at the beginning but perform full initialization on every following discontinuous + * section transition */ NoneOnceThenFull = 6, /** - * @brief Do not initialize at the beginning but perform lean initialization on every following discontinuous section transition + * @brief Do not initialize at the beginning but perform lean initialization on every following discontinuous + * section transition */ NoneOnceThenLean = 7, }; @@ -80,22 +83,22 @@ inline const char* to_string(ConsistentInitialization ci) CADET_NOEXCEPT { switch (ci) { - case ConsistentInitialization::None: - return "None"; - case ConsistentInitialization::FullFirstOnly: - return "FullFirstOnly"; - case ConsistentInitialization::LeanFirstOnly: - return "LeanFirstOnly"; - case ConsistentInitialization::Full: - return "Full"; - case ConsistentInitialization::Lean: - return "Lean"; - case ConsistentInitialization::FullOnceThenLean: - return "FullOnceThenLean"; - case ConsistentInitialization::NoneOnceThenFull: - return "NoneOnceThenFull"; - case ConsistentInitialization::NoneOnceThenLean: - return "NoneOnceThenLean"; + case ConsistentInitialization::None: + return "None"; + case ConsistentInitialization::FullFirstOnly: + return "FullFirstOnly"; + case ConsistentInitialization::LeanFirstOnly: + return "LeanFirstOnly"; + case ConsistentInitialization::Full: + return "Full"; + case ConsistentInitialization::Lean: + return "Lean"; + case ConsistentInitialization::FullOnceThenLean: + return "FullOnceThenLean"; + case ConsistentInitialization::NoneOnceThenFull: + return "NoneOnceThenFull"; + case ConsistentInitialization::NoneOnceThenLean: + return "NoneOnceThenLean"; } return "Unknown"; } @@ -134,24 +137,29 @@ inline ConsistentInitialization to_consistentinitialization(const std::string& c */ inline ConsistentInitialization toConsistentInitialization(int ci) CADET_NOEXCEPT { - switch(ci) + switch (ci) { - case static_cast::type>(ConsistentInitialization::None): - return ConsistentInitialization::None; - case static_cast::type>(ConsistentInitialization::Full): - return ConsistentInitialization::Full; - case static_cast::type>(ConsistentInitialization::FullFirstOnly): - return ConsistentInitialization::FullFirstOnly; - case static_cast::type>(ConsistentInitialization::Lean): - return ConsistentInitialization::Lean; - case static_cast::type>(ConsistentInitialization::LeanFirstOnly): - return ConsistentInitialization::LeanFirstOnly; - case static_cast::type>(ConsistentInitialization::FullOnceThenLean): - return ConsistentInitialization::FullOnceThenLean; - case static_cast::type>(ConsistentInitialization::NoneOnceThenFull): - return ConsistentInitialization::NoneOnceThenFull; - case static_cast::type>(ConsistentInitialization::NoneOnceThenLean): - return ConsistentInitialization::NoneOnceThenLean; + case static_cast::type>(ConsistentInitialization::None): + return ConsistentInitialization::None; + case static_cast::type>(ConsistentInitialization::Full): + return ConsistentInitialization::Full; + case static_cast::type>( + ConsistentInitialization::FullFirstOnly): + return ConsistentInitialization::FullFirstOnly; + case static_cast::type>(ConsistentInitialization::Lean): + return ConsistentInitialization::Lean; + case static_cast::type>( + ConsistentInitialization::LeanFirstOnly): + return ConsistentInitialization::LeanFirstOnly; + case static_cast::type>( + ConsistentInitialization::FullOnceThenLean): + return ConsistentInitialization::FullOnceThenLean; + case static_cast::type>( + ConsistentInitialization::NoneOnceThenFull): + return ConsistentInitialization::NoneOnceThenFull; + case static_cast::type>( + ConsistentInitialization::NoneOnceThenLean): + return ConsistentInitialization::NoneOnceThenLean; } return ConsistentInitialization::Full; } @@ -163,8 +171,10 @@ inline ConsistentInitialization toConsistentInitialization(int ci) CADET_NOEXCEP */ inline bool isValidConsistentInitialization(int ci) CADET_NOEXCEPT { - return (ci >= static_cast::type>(ConsistentInitialization::None)) - && (ci <= static_cast::type>(ConsistentInitialization::NoneOnceThenLean)); + return (ci >= static_cast::type>( + ConsistentInitialization::None)) && + (ci <= static_cast::type>( + ConsistentInitialization::NoneOnceThenLean)); } /** @@ -173,8 +183,9 @@ inline bool isValidConsistentInitialization(int ci) CADET_NOEXCEPT class CADET_API ISimulator { public: - - virtual ~ISimulator() CADET_NOEXCEPT { } + virtual ~ISimulator() CADET_NOEXCEPT + { + } //! \brief Sets a parameter sensitive //! @@ -214,16 +225,17 @@ class CADET_API ISimulator //! //! Multiple parameters are fused into a single parameter sensitivity that is a //! linear combination of the single parameters. Given two parameters @f$ p_1, p_2 @f$, this means setting - //! @f[ \begin{pmatrix} p_1 \\ p_2 \end{pmatrix} = g\left(p\right) = \begin{pmatrix} a_1 p \\ a_2 p \end{pmatrix} @f] - //! and computing the parameter sensitivity of @f$ p@f$: + //! @f[ \begin{pmatrix} p_1 \\ p_2 \end{pmatrix} = g\left(p\right) = \begin{pmatrix} a_1 p \\ a_2 p \end{pmatrix} + //! @f] and computing the parameter sensitivity of @f$ p@f$: //! @f[ \frac{d}{dp} f(g(p)) = a_1 \partial_1 f + a_2 \partial_2 f. @f] //! One additional system in size of the original DAE system is to be solved. //! //! \param [in] ids Parameter IDs of the fused sensitive parameters - //! \param [in] diffFactors Factors @f$ a_i @f$ of the linear combination + //! \param [in] diffFactors Factors @f$ a_i @f$ of the linear combination //! \param [in] numParams Number of fused parameters //! \param [in] absTolS Absolute tolerance used in the sensitivity computation for this parameter (try 1.0e-5) - virtual void setSensitiveParameter(ParameterId const* ids, double const* diffFactors, unsigned int numParams, double absTolS) = 0; + virtual void setSensitiveParameter(ParameterId const* ids, double const* diffFactors, unsigned int numParams, + double absTolS) = 0; //! \brief Reset the simulator to compute no sensitivity at all virtual void clearSensParams() = 0; @@ -314,12 +326,13 @@ class CADET_API ISimulator //! //! \param [in] sectionTimes A vector containing the timepoints of all sections //! \param [in] sectionContinuity A vector determining the continuity of section transitions - virtual void setSectionTimes(const std::vector& sectionTimes, const std::vector& sectionContinuity) = 0; + virtual void setSectionTimes(const std::vector& sectionTimes, + const std::vector& sectionContinuity) = 0; /** * @brief Initializes the time integrator with the given model system * @details Allocates internal memory and initializes and sets up the IDAS package. - * + * * @param [in] model Model system that is to be simulated */ virtual void initializeModel(IModelSystem& model) = 0; @@ -329,7 +342,7 @@ class CADET_API ISimulator * @details Initial conditions are set by calling setInitialCondition(IParameterProvider& paramProvider). * This function has to be called once for applyInitialCondition() to work, which will do nothing * otherwise. - * + * * This function applies the initial conditions to the state vectors. Consistent initialization * will be performed later according to the mode set by setConsistentInitialization(). */ @@ -339,39 +352,39 @@ class CADET_API ISimulator * @brief Reads initial condition of the model from the given parameter provider * @details The scope of the cadet::IParameterProvider is unchanged on return. * Note that the initial conditions are not applied, which is performed by applyInitialCondition(). - * + * * @param [in] paramProvider Parameter provider */ virtual void setInitialCondition(IParameterProvider& paramProvider) = 0; /** * @brief Applies the given initial state to the model - * @details The initial state consists of the state @f$ y_0 @f$ and its time derivative @f$\dot{y}_0@f$. + * @details The initial state consists of the state @f$ y_0 @f$ and its time derivative @f$\dot{y}_0@f$. * It has to be consistent, which means that * @f[ f\left( t_0, y_0, \dot{y}_0 ) = 0 @f] * has to hold. In other words, the initial condition has to fulfill the DAE at the initial * time point @f$ t_0@f$. - * + * * If only @f$ y_0 @f$ is given, the simulator will try to find a matching @f$ \dot{y}_0@f$ later. * This is done according to the consistent initialization mode set by setConsistentInitialization(). * Because of nonlinearities, it is usually faster to also provide @f$ \dot{y}_0@f$. - * + * * @param [in] initState Initial state @f$ y_0 @f$ of the model */ virtual void applyInitialCondition(double const* const initState) = 0; /** * @brief Applies the given initial state to the model - * @details The initial state consists of the state @f$ y_0 @f$ and its time derivative @f$\dot{y}_0@f$. + * @details The initial state consists of the state @f$ y_0 @f$ and its time derivative @f$\dot{y}_0@f$. * It has to be consistent, which means that * @f[ f\left( t_0, y_0, \dot{y}_0 ) = 0 @f] * has to hold. In other words, the initial condition has to fulfill the DAE at the initial * time point @f$ t_0@f$. - * + * * If only @f$ y_0 @f$ is given, the simulator will try to find a matching @f$ \dot{y}_0@f$ later. * This is done according to the consistent initialization mode set by setConsistentInitialization(). * Because of nonlinearities, it is usually faster to also provide @f$ \dot{y}_0@f$. - * + * * @param [in] initState Initial state @f$ y_0 @f$ of the model * @param [in] initStateDot Initial time derivative state @f$ \dot{y}_0 @f$ of the model */ @@ -381,20 +394,23 @@ class CADET_API ISimulator * @brief Applies the given initial state to the forward sensitivity systems * @details The initial sensitivities are given by the argument @p initSens and their time derivatives * by @p initSensDot. The model is not invoked to make changes to the initial values. - * + * * If one of the parameters (@p initSens or @p initSensDot) is @c nullptr, the state vector * and its time derivative will be set to zero and consistent values will be computed later. - * - * Consistent initialization is performed later according to the mode set by setConsistentInitializationSens(). - * + * + * Consistent initialization is performed later according to the mode set by + * setConsistentInitializationSens(). + * * @param [in] initSens Pointer to array with pointers to initial sensitivities or @c nullptr * @param [in] initSensDot Pointer to array with pointers to initial sensitivity time derivatives or @c nullptr */ - virtual void applyInitialConditionFwdSensitivities(double const * const* const initSens, double const * const* const initSensDot) = 0; + virtual void applyInitialConditionFwdSensitivities(double const* const* const initSens, + double const* const* const initSensDot) = 0; /** * @brief Initializes the forward sensitivity subsystems with zero initial values - * @details The initial sensitivities are set to zero and (later) handed over to the model for consistent initialization. + * @details The initial sensitivities are set to zero and (later) handed over to the model for consistent + * initialization. */ virtual void initializeFwdSensitivities() = 0; @@ -402,7 +418,7 @@ class CADET_API ISimulator * @brief Skips consistent initialization at the beginning of integrate() * @details By skipping consistent initialization at the beginning of integrate(), a previous * simulation can be resumed. - * + * * More detailed options can be applied with setConsistentInitialization(). */ virtual void skipConsistentInitialization() = 0; @@ -411,7 +427,7 @@ class CADET_API ISimulator * @brief Sets consistent initialization mode * @details Sets consistent initialization mode for the subsequent calls to integrate(). * Use skipConsistentInitialization() to skip the first consistent initialization. - * + * * @param [in] ci Consistent initialization mode */ virtual void setConsistentInitialization(ConsistentInitialization ci) = 0; @@ -420,7 +436,7 @@ class CADET_API ISimulator * @brief Sets consistent initialization mode of the sensitivity systems * @details Sets consistent initialization mode for the subsequent calls to integrate(). * Use skipConsistentInitialization() to skip the first consistent initialization. - * + * * @param [in] ci Consistent initialization mode of the sensitivity systems */ virtual void setConsistentInitializationSens(ConsistentInitialization ci) = 0; @@ -431,17 +447,18 @@ class CADET_API ISimulator * by @p initSensDot. The user is responsible for checking whether the initial values are * correct and consistent with the sensitive parameters. The model is not invoked to make * changes to the initial values. - * + * * @param [in] initSens Pointer to array with pointers to initial sensitivities * @param [in] initSensDot Pointer to array with pointers to initial sensitivity time derivatives */ - virtual void initializeFwdSensitivities(double const * const* const initSens, double const * const* const initSensDot) = 0; + virtual void initializeFwdSensitivities(double const* const* const initSens, + double const* const* const initSensDot) = 0; /** * @brief Sets the solution recorder which receives the solution of each time step * @details The solution recorder is invoked for every time step when a solution and its sensitivities * are to be saved. Setting the recorder to @c NULL disables recording the solution. - * + * * @param [in] recorder Implementation of the cadet::ISolutionRecorder interface */ virtual void setSolutionRecorder(ISolutionRecorder* recorder) = 0; @@ -455,11 +472,10 @@ class CADET_API ISimulator */ virtual void integrate() = 0; - /** * @brief Returns the bare state vector for the last timepoint * @details The method returns the last solution as it was written to the memory. - * The whole state vetor (including column, particle and flux parts) is returned. + * The whole state vetor (including column, particle and flux parts) is returned. * The ordering is rather unhandy, since the column, the particle, and the flux parts * are of different size. * @@ -471,7 +487,7 @@ class CADET_API ISimulator /** * @brief Returns the bare time derivative state vector for the last timepoint * @details See getLastSolution(). - * + * * @param [out] len Length of the state vector * @return Pointer to first element of the state vector */ @@ -480,7 +496,7 @@ class CADET_API ISimulator /** * @brief Returns the bare sensitivity state vectors for the last timepoint * @details The method returns the last solution of the sensitivity systems as it was written to memory. - * The whole state vetor (including column, particle and flux parts) is returned. + * The whole state vetor (including column, particle and flux parts) is returned. * The ordering is rather unhandy, since the column, the particle and the flux parts * are of different size. * @@ -492,7 +508,7 @@ class CADET_API ISimulator /** * @brief Returns the bare time derivative state vectors of the sensitivity subsystems for the last timepoint * @details See getLastSolutionDerivative(). - * + * * @param [out] len Length of the sensitivity state vector * @return Array with pointers to sensitivity state vectors */ @@ -514,7 +530,7 @@ class CADET_API ISimulator /** * @brief Reads all configuration values from the given parameter provider * @details The scope of the cadet::IParameterProvider is unchanged on return. - * + * * @param [in] paramProvider Parameter provider */ virtual void configure(IParameterProvider& paramProvider) = 0; @@ -522,7 +538,7 @@ class CADET_API ISimulator /** * @brief Rereads all configuration values from the given parameter provider * @details The scope of the cadet::IParameterProvider is unchanged on return. - * + * * @param [in] paramProvider Parameter provider */ virtual void reconfigure(IParameterProvider& paramProvider) = 0; @@ -557,8 +573,10 @@ class CADET_API ISimulator * @param [in] maxSteps Maximum number of time integration steps * @param [in] maxStepSize Maximum step size of the time integrator */ - virtual void configureTimeIntegrator(double relTol, double absTol, double initStepSize, unsigned int maxSteps, double maxStepSize) = 0; - virtual void configureTimeIntegrator(double relTol, double absTol, const std::vector& initStepSizes, unsigned int maxSteps, double maxStepSize) = 0; + virtual void configureTimeIntegrator(double relTol, double absTol, double initStepSize, unsigned int maxSteps, + double maxStepSize) = 0; + virtual void configureTimeIntegrator(double relTol, double absTol, const std::vector& initStepSizes, + unsigned int maxSteps, double maxStepSize) = 0; /** * @brief Sets the error tolerances of the sensitivity systems @@ -578,7 +596,7 @@ class CADET_API ISimulator /** * @brief Sets the relative error tolerance of the time integrator * @details This tolerance is used for all elements of the state vector. - * Each element @f$ r_i @f$ in the residual is weighted by + * Each element @f$ r_i @f$ in the residual is weighted by * @f[ \frac{1}{\text{relTol} \left|r_i\right| + \text{absTol}_i}. @f] * @param [in] relTol Relative error tolerance */ @@ -587,7 +605,7 @@ class CADET_API ISimulator /** * @brief Sets the absolute error tolerance of the time integrator * @details This tolerance is used for all elements of the state vector. - * Each element @f$ r_i @f$ in the residual is weighted by + * Each element @f$ r_i @f$ in the residual is weighted by * @f[ \frac{1}{\text{relTol} \left|r_i\right| + \text{absTol}_i}, @f] * which means that @f$ \text{absTol}_i @f$ does not depend on the * index @f$ i @f$. @@ -597,7 +615,7 @@ class CADET_API ISimulator /** * @brief Sets the absolute error tolerance of the time integrator - * @details Each element @f$ r_i @f$ in the residual is weighted by + * @details Each element @f$ r_i @f$ in the residual is weighted by * @f[ \frac{1}{\text{relTol} \left|r_i\right| + \text{absTol}_i}. @f] * @param [in] absTol Vector with absolute error tolerances for each component of the state vector */ @@ -650,12 +668,13 @@ class CADET_API ISimulator virtual void setRelativeErrorToleranceSens(double relTol) = 0; /** - * @brief Controls whether forward sensitivity systems are taken into account for the local error test in time integration + * @brief Controls whether forward sensitivity systems are taken into account for the local error test in time + * integration * @details The error in time integration can be estimated and is used for step size * and order selection. This function controls whether forward sensitivity * systems are taken into account in the local error test, which they are * by default (@c true). - * + * * @param [in] enabled Determines whether sensitivities are taken into account in local error test */ virtual void setSensitivityErrorControl(bool enabled) = 0; @@ -668,7 +687,7 @@ class CADET_API ISimulator * solution) a maximum of 3 iterations is used by default (see SUNDIALS 2.7.0). * The iteration is stopped when the specified number of iterations is * exceeded. A final convergence test is performed. - * + * * @param [in] nIter Maximum number of Newton iterations for a time step */ virtual void setMaxNewtonIteration(unsigned int nIter) = 0; @@ -679,7 +698,7 @@ class CADET_API ISimulator * and order selection. The maximum number of failed local time step error * tests defaults to 7 (see SUNDIALS 2.7.0). Time integration fails when * the number of failures exceeds the specified value. - * + * * @param [in] nFails Maximum number of local error test failures for a time step */ virtual void setMaxErrorTestFails(unsigned int nFails) = 0; @@ -692,19 +711,20 @@ class CADET_API ISimulator * for the number of failed convergence tests (i.e., Newton iterations) is set * to 10 (see SUNDIALS 2.7.0). When the specified number of failures is exceeded, * time integration fails. - * + * * @param [in] nFails Maximum number of convergence test failures */ virtual void setMaxConvergenceFails(unsigned int nFails) = 0; /** - * @brief Sets the maximum number of Newton iterations for each forward sensitivity system in a time integration step + * @brief Sets the maximum number of Newton iterations for each forward sensitivity system in a time integration + * step * @details Each time step requires the solution of a nonlinear equation system which * is performed by Newton iteration. After the original system has been solved, * each forward sensitivity system is solved using a separate Newton iteration. * The maximum number of iterations is controlled by this function and defaults * to 3 (see SUNDIALS 2.7.0). After the iteration a convergence test is performed. - * + * * @param [in] nIter Maximum number of Newton iterations for a time step */ virtual void setMaxSensNewtonIteration(unsigned int nIter) = 0; @@ -730,4 +750,4 @@ class CADET_API ISimulator } // namespace cadet -#endif // LIBCADET_SIMULATOR_HPP_ +#endif // LIBCADET_SIMULATOR_HPP_ diff --git a/include/cadet/SolutionExporter.hpp b/include/cadet/SolutionExporter.hpp index ae286b01c..7c95ee25a 100644 --- a/include/cadet/SolutionExporter.hpp +++ b/include/cadet/SolutionExporter.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines interfaces which export the solution to the user space. */ @@ -32,7 +32,6 @@ namespace cadet class CADET_API ISolutionExporter { public: - /** * @brief Returns whether the associated model has a flux into the particles * @return @c true if flux into the particles is present, otherwise @c false @@ -68,7 +67,8 @@ class CADET_API ISolutionExporter /** * @brief Returns whether the primary coordinate is always a single element * @details If the primary coordinate is always a single element, the singleton dimension can be removed. - * @return @c true if the state in the primary coordinate direction is always represented by a single element, otherwise @c false + * @return @c true if the state in the primary coordinate direction is always represented by a single element, + * otherwise @c false */ virtual bool hasPrimaryExtent() const CADET_NOEXCEPT = 0; @@ -131,14 +131,16 @@ class CADET_API ISolutionExporter virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT = 0; /** - * @brief Returns the total number of mobile phase DOFs in the particles of the given type if particles are supported + * @brief Returns the total number of mobile phase DOFs in the particles of the given type if particles are + * supported * @param [in] parType Particle type index * @return Total number of particle mobile phase DOFs */ virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT = 0; /** - * @brief Returns the total number of mobile phase DOFs in the particles of the given type if particles are supported + * @brief Returns the total number of mobile phase DOFs in the particles of the given type if particles are + * supported * @details This includes all particle types. * @return Total number of particle mobile phase DOFs */ @@ -170,19 +172,18 @@ class CADET_API ISolutionExporter */ virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT = 0; - /** * @brief Writes the solution of the (bulk) mobile phase to the given buffer * @details The data is written in the order primary-secondary-component, * where the last index changes the fastest. - * + * * For a system with 3 primary coordinates, 2 secondary coordinates, * and 4 components, the data is written as * p0s0c0, p0s0c1, p0s0c2, p0s0c3, - * p0s1c0, p0s1c1, p0s1c2, p0s1c3, + * p0s1c0, p0s1c1, p0s1c2, p0s1c3, * p1s0c0, p1s0c1, p1s0c2, p1s0c3, * p1s1c0, p1s1c1, p1s1c2, p1s1c3, ... - * + * * @param [out] buffer Pointer to buffer that receives the data * @return Number of written items */ @@ -193,7 +194,7 @@ class CADET_API ISolutionExporter * @details The data is written in the order particletype-primary-secondary-particle-component-boundstate, * where the last index changes the fastest. * The solution is written for all particle types. - * + * * @param [out] buffer Pointer to buffer that receives the data * @return Number of written items */ @@ -204,7 +205,7 @@ class CADET_API ISolutionExporter * @details The data is written in the order particletype-primary-secondary-particle-component, * where the last index changes the fastest. * The solution is written for all particle types. - * + * * @param [out] buffer Pointer to buffer that receives the data * @return Number of written items */ @@ -215,7 +216,7 @@ class CADET_API ISolutionExporter * @details The data is written in the order primary-secondary-particle-component-boundstate, * where the last index changes the fastest. * The solution is written for the given particle type only. - * + * * For a system with 2 primary coordinates, 2 secondary coordinates, 2 particle * coordinates, 2 components, and 2 bound states per component, the data is written as * p0s0p0c0s0, p0s0p0c0s1, p0s0p0c1s0, p0s0p0c1s1, @@ -226,7 +227,7 @@ class CADET_API ISolutionExporter * p1s0p1c0s0, p1s0p1c0s1, p1s0p1c1s0, p1s0p1c1s1, * p1s1p0c0s0, p1s1p0c0s1, p1s1p0c1s0, p1s1p0c1s1, * p1s1p1c0s0, p1s1p1c0s1, p1s1p1c1s0, p1s1p1c1s1, ... - * + * * @param [in] parType Index of the particle type to be written * @param [out] buffer Pointer to buffer that receives the data * @return Number of written items @@ -238,7 +239,7 @@ class CADET_API ISolutionExporter * @details The data is written in the order primary-secondary-particle-component, * where the last index changes the fastest. * The solution is written for the given particle type only. - * + * * For a system with 2 primary coordinates, 2 secondary coordinates, 2 particle * coordinates, 2 components, and 2 bound states per component, the data is written as * p0s0p0c0s0, p0s0p0c0s1, p0s0p0c1s0, p0s0p0c1s1, @@ -249,7 +250,7 @@ class CADET_API ISolutionExporter * p1s0p1c0s0, p1s0p1c0s1, p1s0p1c1s0, p1s0p1c1s1, * p1s1p0c0s0, p1s1p0c0s1, p1s1p0c1s0, p1s1p0c1s1, * p1s1p1c0s0, p1s1p1c0s1, p1s1p1c1s0, p1s1p1c1s1, ... - * + * * @param [in] parType Index of the particle type to be written * @param [out] buffer Pointer to buffer that receives the data * @return Number of written items @@ -261,17 +262,18 @@ class CADET_API ISolutionExporter * @details The data is written in the order particletype-primary-secondary-component, * where the last index changes the fastest. * The solution is written for all particle types. - * + * * @param [out] buffer Pointer to buffer that receives the data * @return Number of written items */ virtual int writeParticleFlux(double* buffer) const = 0; /** - * @brief Writes the solution of the flux between primary mobile phase and particle mobile phase of the selected particle type to the given buffer + * @brief Writes the solution of the flux between primary mobile phase and particle mobile phase of the selected + * particle type to the given buffer * @details The data is written in the order particletype-primary-secondary-component, * where the last index changes the fastest. - * + * * @param [in] parType Index of the particle type to be written * @param [out] buffer Pointer to buffer that receives the data * @return Number of written items @@ -288,7 +290,7 @@ class CADET_API ISolutionExporter /** * @brief Writes the solution of the inlet at the given port into the provided buffer * @details Writes all components of the selected port to the provided buffer. - * + * * @param [in] port Index of the port * @param [out] buffer Pointer to buffer that receives the data * @return Number of written items @@ -306,7 +308,7 @@ class CADET_API ISolutionExporter /** * @brief Writes the solution of the outlet at the given port into the provided buffer * @details Writes all components of the selected port to the provided buffer. - * + * * @param [in] port Index of the port * @param [out] buffer Pointer to buffer that receives the data * @return Number of written items @@ -321,7 +323,6 @@ class CADET_API ISolutionExporter */ virtual int writeOutlet(double* buffer) const = 0; - /** * @brief Returns primary coordinates (e.g., axial for axial flow columns) * @param [out] coords Pointer to array that is filled with primary coordinates @@ -347,4 +348,4 @@ class CADET_API ISolutionExporter } // namespace cadet -#endif // LIBCADET_SOLUTIONEXPORTER_HPP_ +#endif // LIBCADET_SOLUTIONEXPORTER_HPP_ diff --git a/include/cadet/SolutionRecorder.hpp b/include/cadet/SolutionRecorder.hpp index 354711874..61ba4bed2 100644 --- a/include/cadet/SolutionRecorder.hpp +++ b/include/cadet/SolutionRecorder.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines interfaces which control the solution export to the user space. */ @@ -33,7 +33,7 @@ class IModel; * @details Library users implement this interface which is then used by the cadet::ISimulator * to signal the user that a timestep has been finished and the solution is to be * recorded. - * + * * First, the solution of the original system is reported by all unit operations. This * is signaled by beginSolution(). Then, the solution of the forward sensitivity systems * is reported, which is indicated by beginSensitivity(). @@ -41,8 +41,9 @@ class IModel; class CADET_API ISolutionRecorder { public: - - virtual ~ISolutionRecorder() CADET_NOEXCEPT { } + virtual ~ISolutionRecorder() CADET_NOEXCEPT + { + } /** * @brief Clears existing data from memory @@ -53,7 +54,7 @@ class CADET_API ISolutionRecorder /** * @brief Prepares the recorder and allows it to allocate memory in advance * @details This function is called after the recorder is passed to ISimulator. - * + * * @param [in] numDofs Number of DOFs in the model * @param [in] numSens Number of forward sensitivities * @param [in] numTimesteps Number of anticipated timesteps, can be @c 0 if no estimate is available @@ -65,7 +66,7 @@ class CADET_API ISolutionRecorder * @details This function is called before ISimulator starts the time integration process. * The ISolutionRecorder can choose to simply append the results of the time integration * or clear all existing results from memory. - * + * * @param [in] numDofs Number of DOFs in the model * @param [in] numSens Number of forward sensitivities * @param [in] numTimesteps Number of anticipated timesteps, can be @c 0 if no estimate is available @@ -74,8 +75,9 @@ class CADET_API ISolutionRecorder /** * @brief Provides the internal structure of a unit operation which can be used for memory allocation - * @details After prepare() or notifyIntegrationStart() has been called, this function is called by each unit operation. - * + * @details After prepare() or notifyIntegrationStart() has been called, this function is called by each unit + * operation. + * * @param [in] idx Index of the unit operation * @param [in] model Unit operation model * @param [in] exporter Solution exporter providing access to the solution structure (no solution data is available) @@ -87,16 +89,16 @@ class CADET_API ISolutionRecorder * @details After a timestep has been finished, the solution is exported to the user space. * This function is called once before all unit operations report their solutions * via beginUnitOperation(). - * + * * @param [in] t Current timepoint */ virtual void beginTimestep(double t) = 0; /** * @brief Signals the export of the given unit operation - * @details The solution of the given unit operation identified by its index @p idx is - * provided by the corresponding @p exporter. - * + * @details The solution of the given unit operation identified by its index @p idx is + * provided by the corresponding @p exporter. + * * @param [in] idx Index of the unit operation * @param [in] model Unit operation model * @param [in] exporter Solution exporter providing access to the solution @@ -116,11 +118,10 @@ class CADET_API ISolutionRecorder */ virtual void endTimestep() = 0; - /** * @brief Signals the export of the solution of the original system * @details This function is called once before all unit operations report their - * solutions via beginUnitOperation(). + * solutions via beginUnitOperation(). */ virtual void beginSolution() = 0; @@ -134,7 +135,7 @@ class CADET_API ISolutionRecorder /** * @brief Signals the export of the time derivative of the solution of the original system * @details This function is called once before all unit operations report their - * solutions via beginUnitOperation(). + * solutions via beginUnitOperation(). */ virtual void beginSolutionDerivative() = 0; @@ -149,7 +150,7 @@ class CADET_API ISolutionRecorder * @brief Signals the beginning of the export of the sensitivity of the given parameter * @details This function is called once for every sensitive parameter and is succeeded * by calls reporting the sensitivity in each unit operation (beginUnitOperation()). - * + * * @param [in] pId Parameter Id of the sensitive parameter * @param [in] sensIdx Index of the sensitive parameter (among all sensitive parameters) */ @@ -159,7 +160,7 @@ class CADET_API ISolutionRecorder * @brief Signals the end of the sensitivity export. * @details This function is called once for each sensitive parameter after all unit * operations have reported their sensitivities. - * + * * @param [in] pId Parameter Id of the sensitive parameter * @param [in] sensIdx Index of the sensitive parameter (among all sensitive parameters) */ @@ -169,7 +170,7 @@ class CADET_API ISolutionRecorder * @brief Signals the beginning of the export of the parameter sensitivity's time derivative * @details This function is called once for every sensitive parameter and is succeeded * by calls reporting the sensitivity in each unit operation (beginUnitOperation()). - * + * * @param [in] pId Parameter Id of the sensitive parameter * @param [in] sensIdx Index of the sensitive parameter (among all sensitive parameters) */ @@ -179,7 +180,7 @@ class CADET_API ISolutionRecorder * @brief Signals the end of the sensitivity time derivative export. * @details This function is called once for each sensitive parameter after all unit * operations have reported their sensitivities. - * + * * @param [in] pId Parameter Id of the sensitive parameter * @param [in] sensIdx Index of the sensitive parameter (among all sensitive parameters) */ @@ -188,4 +189,4 @@ class CADET_API ISolutionRecorder } // namespace cadet -#endif // LIBCADET_SOLUTIONRECORDER_HPP_ +#endif // LIBCADET_SOLUTIONRECORDER_HPP_ diff --git a/include/cadet/StringUtil.hpp b/include/cadet/StringUtil.hpp index 1c454a83b..5b141471c 100644 --- a/include/cadet/StringUtil.hpp +++ b/include/cadet/StringUtil.hpp @@ -46,176 +46,185 @@ CADET_CONSTEXPR static inline sip_word rotl64(const sip_word word, const unsigne #if CADET_COMPILER_CXX_CONSTEXPR +/** + * @brief Compiletime string with SipHash24 hashing + */ +class ConstString +{ +public: + template constexpr ConstString(const char (&s)[N]) : m_Str(s), m_Size(N - 1) + { + } + constexpr ConstString(const char* s, std::size_t len) : m_Str(s), m_Size(len) + { + } + + constexpr std::size_t size() const + { + return m_Size; + } + + constexpr unsigned char operator[](std::size_t n) const + { + return static_cast(m_Str[n]); + } + /** - * @brief Compiletime string with SipHash24 hashing + * @brief Computes the SipHash value of the compiletime string (without NULL terminator) + * @param [in] key0 Arbitrary key, defaults to 0 + * @param [in] key1 Arbitrary key, defaults to 0 + * @return SipHash of the string */ - class ConstString + constexpr inline sip_word hash(const sip_word key0 = 0, const sip_word key1 = 0) const { - public: - template - constexpr ConstString(const char(&s)[N]) : m_Str(s), m_Size(N - 1) { } - constexpr ConstString(const char* s, std::size_t len) : m_Str(s), m_Size(len) { } - - constexpr std::size_t size() const { return m_Size; } + return lastPhase(mainLoop(makeState(key0, key1), *this, 0, m_Size - (m_Size & 7)), + tailWord(*this, m_Size - (m_Size & 7), m_Size)); + } - constexpr unsigned char operator[] (std::size_t n) const +private: + /** + * @brief 256 bit state + */ + struct state4 + { + constexpr state4() : state0(0), state1(0), state2(0), state3(0) { - return static_cast(m_Str[n]); } - - /** - * @brief Computes the SipHash value of the compiletime string (without NULL terminator) - * @param [in] key0 Arbitrary key, defaults to 0 - * @param [in] key1 Arbitrary key, defaults to 0 - * @return SipHash of the string - */ - constexpr inline sip_word hash(const sip_word key0 = 0, const sip_word key1 = 0) const + constexpr state4(const sip_word a, const sip_word b, const sip_word c, const sip_word d) + : state0(a), state1(b), state2(c), state3(d) { - return lastPhase(mainLoop(makeState(key0, key1), *this, 0, m_Size - (m_Size & 7)), tailWord(*this, m_Size - (m_Size & 7), m_Size)); } + sip_word state0; + sip_word state1; + sip_word state2; + sip_word state3; + }; - private: - - /** - * @brief 256 bit state - */ - struct state4 - { - constexpr state4() : state0(0), state1(0), state2(0), state3(0) { } - constexpr state4(const sip_word a, const sip_word b, const sip_word c, const sip_word d) : state0(a), state1(b), state2(c), state3(d) { } - sip_word state0; - sip_word state1; - sip_word state2; - sip_word state3; - }; - - constexpr inline sip_word getSipWord(const unsigned int offset, const unsigned int idx) const - { - return static_cast((*this)[offset + idx]); - } + constexpr inline sip_word getSipWord(const unsigned int offset, const unsigned int idx) const + { + return static_cast((*this)[offset + idx]); + } - constexpr static inline sip_word read64(const ConstString& cs, const unsigned int offset) - { - return ((cs.getSipWord(offset, 0) ) | - (cs.getSipWord(offset, 1) << 8) | - (cs.getSipWord(offset, 2) << 16) | - (cs.getSipWord(offset, 3) << 24) | - (cs.getSipWord(offset, 4) << 32) | - (cs.getSipWord(offset, 5) << 40) | - (cs.getSipWord(offset, 6) << 48) | - (cs.getSipWord(offset, 7) << 56)); - } + constexpr static inline sip_word read64(const ConstString& cs, const unsigned int offset) + { + return ((cs.getSipWord(offset, 0)) | (cs.getSipWord(offset, 1) << 8) | (cs.getSipWord(offset, 2) << 16) | + (cs.getSipWord(offset, 3) << 24) | (cs.getSipWord(offset, 4) << 32) | (cs.getSipWord(offset, 5) << 40) | + (cs.getSipWord(offset, 6) << 48) | (cs.getSipWord(offset, 7) << 56)); + } - constexpr static inline state4 mixFirst(const state4& s) - { - return { s.state0 + s.state1, rotl64(s.state1, 13), s.state2 + s.state3, rotl64(s.state3, 16) }; - } + constexpr static inline state4 mixFirst(const state4& s) + { + return {s.state0 + s.state1, rotl64(s.state1, 13), s.state2 + s.state3, rotl64(s.state3, 16)}; + } - constexpr static inline state4 mixSecond(const state4& s) - { - return { rotl64(s.state0, 32), s.state1 ^ s.state0, s.state2, s.state3 ^ s.state2 }; - } + constexpr static inline state4 mixSecond(const state4& s) + { + return {rotl64(s.state0, 32), s.state1 ^ s.state0, s.state2, s.state3 ^ s.state2}; + } - constexpr static inline state4 mixThird(const state4& s) - { - return { s.state0 + s.state3, rotl64(s.state1, 17), s.state2 + s.state1, rotl64(s.state3, 21) }; - } + constexpr static inline state4 mixThird(const state4& s) + { + return {s.state0 + s.state3, rotl64(s.state1, 17), s.state2 + s.state1, rotl64(s.state3, 21)}; + } - constexpr static inline state4 mixFourth(const state4& s) - { - return { s.state0, s.state1 ^ s.state2, rotl64(s.state2, 32), s.state3 ^ s.state0 }; - } + constexpr static inline state4 mixFourth(const state4& s) + { + return {s.state0, s.state1 ^ s.state2, rotl64(s.state2, 32), s.state3 ^ s.state0}; + } - constexpr static inline state4 mix(const state4& s) - { - return mixFourth(mixThird(mixSecond(mixFirst(s)))); - } + constexpr static inline state4 mix(const state4& s) + { + return mixFourth(mixThird(mixSecond(mixFirst(s)))); + } - constexpr static inline state4 mixLoop(const state4& s, const unsigned int rounds) - { - return (rounds > 0) ? mixLoop(mix(s), rounds - 1) : s; - } + constexpr static inline state4 mixLoop(const state4& s, const unsigned int rounds) + { + return (rounds > 0) ? mixLoop(mix(s), rounds - 1) : s; + } - constexpr static inline sip_word tailWordInternal(const ConstString& cs, const sip_word tailWord, const std::size_t offset, const unsigned int tailSize) - { - return (tailSize == 0) ? tailWord : tailWordInternal(cs, tailWord | (static_cast(cs[offset + tailSize - 1]) << ((tailSize - 1) * 8)), offset, tailSize - 1); - } + constexpr static inline sip_word tailWordInternal(const ConstString& cs, const sip_word tailWord, + const std::size_t offset, const unsigned int tailSize) + { + return (tailSize == 0) + ? tailWord + : tailWordInternal( + cs, tailWord | (static_cast(cs[offset + tailSize - 1]) << ((tailSize - 1) * 8)), + offset, tailSize - 1); + } - constexpr static inline sip_word tailWord(const ConstString& cs, const std::size_t offset, const std::size_t insize) - { - return tailWordInternal(cs, static_cast(insize) << 56, offset, insize & 7); - } + constexpr static inline sip_word tailWord(const ConstString& cs, const std::size_t offset, const std::size_t insize) + { + return tailWordInternal(cs, static_cast(insize) << 56, offset, insize & 7); + } - constexpr static inline state4 mixin(const state4& s, const sip_word tailWord) - { - // SipHash24, so take 2 rounds here (mixing of tail word) - return mixLoop({s.state0, s.state1, s.state2, s.state3 ^ tailWord}, 2); - } + constexpr static inline state4 mixin(const state4& s, const sip_word tailWord) + { + // SipHash24, so take 2 rounds here (mixing of tail word) + return mixLoop({s.state0, s.state1, s.state2, s.state3 ^ tailWord}, 2); + } - constexpr static inline state4 finalMix(const state4& s, const sip_word tailWord) - { - // SipHash24, so take 4 rounds here (finalize) - return mixLoop({s.state0 ^ tailWord, s.state1, s.state2 ^ 0xff, s.state3}, 4); - } + constexpr static inline state4 finalMix(const state4& s, const sip_word tailWord) + { + // SipHash24, so take 4 rounds here (finalize) + return mixLoop({s.state0 ^ tailWord, s.state1, s.state2 ^ 0xff, s.state3}, 4); + } - constexpr static inline sip_word finalizeHash(const state4& s) - { - return (s.state0 ^ s.state1 ^ s.state2 ^ s.state3) & 0xffffffffffffffffull; - } + constexpr static inline sip_word finalizeHash(const state4& s) + { + return (s.state0 ^ s.state1 ^ s.state2 ^ s.state3) & 0xffffffffffffffffull; + } - constexpr static inline state4 bottomMainLoop(const state4& s, const sip_word curWord) - { - return {s.state0 ^ curWord, s.state1, s.state2, s.state3}; - } + constexpr static inline state4 bottomMainLoop(const state4& s, const sip_word curWord) + { + return {s.state0 ^ curWord, s.state1, s.state2, s.state3}; + } - constexpr static inline state4 execMainLoop(const state4& s, const sip_word curWord) - { - // SipHash24, so take 2 rounds here (main loop) - return bottomMainLoop(mixLoop({s.state0, s.state1, s.state2, s.state3 ^ curWord}, 2), curWord); - } + constexpr static inline state4 execMainLoop(const state4& s, const sip_word curWord) + { + // SipHash24, so take 2 rounds here (main loop) + return bottomMainLoop(mixLoop({s.state0, s.state1, s.state2, s.state3 ^ curWord}, 2), curWord); + } - constexpr static inline state4 mainLoop(const state4& s, const ConstString& cs, const unsigned int offset, const unsigned int end) - { - return (offset < end) ? mainLoop(execMainLoop(s, read64(cs, offset)), cs, offset + 8, end) : s; - } + constexpr static inline state4 mainLoop(const state4& s, const ConstString& cs, const unsigned int offset, + const unsigned int end) + { + return (offset < end) ? mainLoop(execMainLoop(s, read64(cs, offset)), cs, offset + 8, end) : s; + } - constexpr static inline state4 makeState(const sip_word key0, const sip_word key1) - { - return {key0 ^ 0x736f6d6570736575ull, key1 ^ 0x646f72616e646f6dull, key0 ^ 0x6c7967656e657261ull, key1 ^ 0x7465646279746573ull}; - } + constexpr static inline state4 makeState(const sip_word key0, const sip_word key1) + { + return {key0 ^ 0x736f6d6570736575ull, key1 ^ 0x646f72616e646f6dull, key0 ^ 0x6c7967656e657261ull, + key1 ^ 0x7465646279746573ull}; + } - constexpr static inline sip_word lastPhase(const state4& s, const sip_word tailWord) - { - return finalizeHash(finalMix(mixin(s, tailWord), tailWord)); - } + constexpr static inline sip_word lastPhase(const state4& s, const sip_word tailWord) + { + return finalizeHash(finalMix(mixin(s, tailWord), tailWord)); + } - const char* m_Str; - std::size_t m_Size; - }; + const char* m_Str; + std::size_t m_Size; +}; #endif /** * @brief SipHash reading - * @details Implementation taken from https://chiselapp.com/user/Justin_be_my_guide/repository/siple/artifact/f7d1b255a09c1d67 + * @details Implementation taken from + * https://chiselapp.com/user/Justin_be_my_guide/repository/siple/artifact/f7d1b255a09c1d67 */ static inline sip_word read64(unsigned char const* const p) { - return (((sip_word)p[0] ) | - ((sip_word)p[1] << 8) | - ((sip_word)p[2] << 16) | - ((sip_word)p[3] << 24) | - ((sip_word)p[4] << 32) | - ((sip_word)p[5] << 40) | - ((sip_word)p[6] << 48) | - ((sip_word)p[7] << 56)); + return (((sip_word)p[0]) | ((sip_word)p[1] << 8) | ((sip_word)p[2] << 16) | ((sip_word)p[3] << 24) | + ((sip_word)p[4] << 32) | ((sip_word)p[5] << 40) | ((sip_word)p[6] << 48) | ((sip_word)p[7] << 56)); } /** * @brief SipHash mixing - * @details Implementation taken from https://chiselapp.com/user/Justin_be_my_guide/repository/siple/artifact/f7d1b255a09c1d67 + * @details Implementation taken from + * https://chiselapp.com/user/Justin_be_my_guide/repository/siple/artifact/f7d1b255a09c1d67 */ -static inline void mix(sip_word &state0, sip_word &state1, sip_word &state2, sip_word &state3) +static inline void mix(sip_word& state0, sip_word& state1, sip_word& state2, sip_word& state3) { state0 += state1; state2 += state3; @@ -235,7 +244,8 @@ static inline void mix(sip_word &state0, sip_word &state1, sip_word &state2, sip /** * @brief Computes the SipHash value of the given data at runtime - * @details Implementation taken from https://chiselapp.com/user/Justin_be_my_guide/repository/siple/artifact/f7d1b255a09c1d67 + * @details Implementation taken from + * https://chiselapp.com/user/Justin_be_my_guide/repository/siple/artifact/f7d1b255a09c1d67 * * @param [in] in Input data * @param [in] insize Size of the input data (in byte) @@ -245,17 +255,18 @@ static inline void mix(sip_word &state0, sip_word &state1, sip_word &state2, sip * @tparam int finalisation_rounds Number of final mixing rounds * @return SipHash value of the given data */ -template -static inline sip_word siphash(const void *in, const std::size_t insize, const sip_word key0 = 0, const sip_word key1 = 0) +template +static inline sip_word siphash(const void* in, const std::size_t insize, const sip_word key0 = 0, + const sip_word key1 = 0) { sip_word state0 = key0 ^ 0x736f6d6570736575ull; sip_word state1 = key1 ^ 0x646f72616e646f6dull; sip_word state2 = key0 ^ 0x6c7967656e657261ull; sip_word state3 = key1 ^ 0x7465646279746573ull; - const unsigned char *char_in = (const unsigned char*)in; + const unsigned char* char_in = (const unsigned char*)in; const int tail_size = insize & 7; - const unsigned char *main_loop_end = char_in + insize - tail_size; + const unsigned char* main_loop_end = char_in + insize - tail_size; // The main loop for (; char_in != main_loop_end; char_in += 8) @@ -269,18 +280,33 @@ static inline sip_word siphash(const void *in, const std::size_t insize, const s } // We're left with 0..7 bytes. - sip_word tail_word = ((sip_word) insize) << 56; + sip_word tail_word = ((sip_word)insize) << 56; switch (tail_size) { - case 7: tail_word |= (sip_word)char_in[6] << 48; [[fallthrough]]; - case 6: tail_word |= (sip_word)char_in[5] << 40; [[fallthrough]]; - case 5: tail_word |= (sip_word)char_in[4] << 32; [[fallthrough]]; - case 4: tail_word |= (sip_word)char_in[3] << 24; [[fallthrough]]; - case 3: tail_word |= (sip_word)char_in[2] << 16; [[fallthrough]]; - case 2: tail_word |= (sip_word)char_in[1] << 8; [[fallthrough]]; - case 1: tail_word |= (sip_word)char_in[0]; [[fallthrough]]; - case 0: break; + case 7: + tail_word |= (sip_word)char_in[6] << 48; + [[fallthrough]]; + case 6: + tail_word |= (sip_word)char_in[5] << 40; + [[fallthrough]]; + case 5: + tail_word |= (sip_word)char_in[4] << 32; + [[fallthrough]]; + case 4: + tail_word |= (sip_word)char_in[3] << 24; + [[fallthrough]]; + case 3: + tail_word |= (sip_word)char_in[2] << 16; + [[fallthrough]]; + case 2: + tail_word |= (sip_word)char_in[1] << 8; + [[fallthrough]]; + case 1: + tail_word |= (sip_word)char_in[0]; + [[fallthrough]]; + case 0: + break; } // Mix-in the tail block. @@ -294,13 +320,11 @@ static inline sip_word siphash(const void *in, const std::size_t insize, const s for (int i = 0; i < finalisation_rounds; ++i) mix(state0, state1, state2, state3); - return (state0 ^ state1 ^ state2 ^ state3) & 0xffffffffffffffffull; + return (state0 ^ state1 ^ state2 ^ state3) & 0xffffffffffffffffull; } - } // namespace hash - /** * @brief Computes the SipHash24 value of the given string at runtime * @details Computes a 64 bit hash value of the string at runtime @@ -312,7 +336,7 @@ static inline sip_word siphash(const void *in, const std::size_t insize, const s */ inline StringHash SipHash24runtime(const std::string& str, const StringHash key0 = 0, const StringHash key1 = 0) { - return util::hash::siphash<2,4>(str.c_str(), str.size(), key0, key1); + return util::hash::siphash<2, 4>(str.c_str(), str.size(), key0, key1); } /** @@ -327,41 +351,43 @@ inline StringHash SipHash24runtime(const std::string& str, const StringHash key0 inline StringHash SipHash24runtime(char const* const str, const StringHash key0 = 0, const StringHash key1 = 0) { unsigned int len = 0; - for (; str[len] != 0; ++len); - return util::hash::siphash<2,4>(str, len, key0, key1); + for (; str[len] != 0; ++len) + ; + return util::hash::siphash<2, 4>(str, len, key0, key1); } #if CADET_COMPILER_CXX_CONSTEXPR - /** - * @brief Computes the SipHash24 value of the given string - * @details Computes a 64 bit hash value of the string at compiletime - * - * @param [in] cs Compiletime string - * @param [in] key0 Arbitrary key (defaults to 0) - * @param [in] key1 Arbitrary key (defaults to 0) - * @return SipHash24 value of the given compiletime string - */ - constexpr inline StringHash SipHash24(const util::hash::ConstString& cs, const StringHash key0 = 0, const StringHash key1 = 0) - { - return cs.hash(key0, key1); - } +/** + * @brief Computes the SipHash24 value of the given string + * @details Computes a 64 bit hash value of the string at compiletime + * + * @param [in] cs Compiletime string + * @param [in] key0 Arbitrary key (defaults to 0) + * @param [in] key1 Arbitrary key (defaults to 0) + * @return SipHash24 value of the given compiletime string + */ +constexpr inline StringHash SipHash24(const util::hash::ConstString& cs, const StringHash key0 = 0, + const StringHash key1 = 0) +{ + return cs.hash(key0, key1); +} #else - /** - * @brief Computes the SipHash24 value of the given string at runtime - * @details Computes a 64 bit hash value of the string at runtime - * - * @param [in] str String - * @param [in] key0 Arbitrary key (defaults to 0) - * @param [in] key1 Arbitrary key (defaults to 0) - * @return SipHash24 value of the given string - */ - inline StringHash SipHash24(const std::string& str, const StringHash key0 = 0, const StringHash key1 = 0) - { - return SipHash24runtime(str, key0, key1); - } +/** + * @brief Computes the SipHash24 value of the given string at runtime + * @details Computes a 64 bit hash value of the string at runtime + * + * @param [in] str String + * @param [in] key0 Arbitrary key (defaults to 0) + * @param [in] key1 Arbitrary key (defaults to 0) + * @return SipHash24 value of the given string + */ +inline StringHash SipHash24(const std::string& str, const StringHash key0 = 0, const StringHash key1 = 0) +{ + return SipHash24runtime(str, key0, key1); +} #endif @@ -388,21 +414,21 @@ inline bool caseInsensitiveEquals(const std::string& a, const std::string& b) #if CADET_COMPILETIME_HASH - /** - * @brief Computes the SipHash24 value of the given string - * @details Computes a 64 bit hash value of the string at compiletime using SipHash24 with both keys 0 - * - * @param [in] str Compiletime string - * @param [in] len Length of the compiletime string - * @return SipHash24 value of the given compiletime string - */ - constexpr StringHash operator "" _hash(const char* str, std::size_t len) - { - return util::hash::ConstString(str, len).hash(); - } +/** + * @brief Computes the SipHash24 value of the given string + * @details Computes a 64 bit hash value of the string at compiletime using SipHash24 with both keys 0 + * + * @param [in] str Compiletime string + * @param [in] len Length of the compiletime string + * @return SipHash24 value of the given compiletime string + */ +constexpr StringHash operator"" _hash(const char* str, std::size_t len) +{ + return util::hash::ConstString(str, len).hash(); +} #endif } // namespace cadet -#endif // LIBCADET_STRINGUTIL_HPP_ +#endif // LIBCADET_STRINGUTIL_HPP_ diff --git a/include/cadet/StrongTypes.hpp b/include/cadet/StrongTypes.hpp index 2c67008c3..2b53661d3 100644 --- a/include/cadet/StrongTypes.hpp +++ b/include/cadet/StrongTypes.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines strong types for interfaces. */ @@ -23,20 +23,35 @@ namespace cadet { - namespace model - { +namespace model +{ - struct ComponentIndex { unsigned int value; }; +struct ComponentIndex +{ + unsigned int value; +}; - struct AxialCellIndex { unsigned int value; }; +struct AxialCellIndex +{ + unsigned int value; +}; - struct ParticleTypeIndex { unsigned int value; }; +struct ParticleTypeIndex +{ + unsigned int value; +}; - struct ParticleIndex { unsigned int value; }; +struct ParticleIndex +{ + unsigned int value; +}; - struct ShellIndex { unsigned int value; }; +struct ShellIndex +{ + unsigned int value; +}; - } // namespace model +} // namespace model } // namespace cadet -#endif // LIBCADET_STRONGTYPES_HPP_ +#endif // LIBCADET_STRONGTYPES_HPP_ diff --git a/include/cadet/cadet.h b/include/cadet/cadet.h index 0cd88efda..1de179ee2 100644 --- a/include/cadet/cadet.h +++ b/include/cadet/cadet.h @@ -1,9 +1,9 @@ // ============================================================================= // CADET - The Chromatography Analysis and Design Toolkit -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the C API of the library. */ @@ -78,9 +78,6 @@ extern "C" */ CADET_API const char* cdtGetLibraryBuildHost(); - - - /** * @brief LogLevel represents the severity of log messages * @details The levels are nested, such that the highest level (Trace) includes all lower levels. @@ -121,7 +118,6 @@ extern "C" cdtLogLevelTrace = 7, }; - /** * @brief Callback for each log message * @param [in] file Name of the file @@ -131,7 +127,8 @@ extern "C" * @param [in] lvlStr Name of the log level * @param [in] message Actual log message */ - typedef void (*cdtLogHandler)(const char* file, const char* func, const unsigned int line, int lvl, const char* lvlStr, const char* message); + typedef void (*cdtLogHandler)(const char* file, const char* func, const unsigned int line, int lvl, + const char* lvlStr, const char* message); /** * @brief Sets the log receiver replacing any previously set receiver @@ -153,8 +150,6 @@ extern "C" */ CADET_API int cdtGetLogLevel(); - - /** * @brief Return and error codes */ @@ -179,8 +174,6 @@ extern "C" */ typedef int cdtResult; - - /** * @brief ParameterProvider interface is used for querying parameters and data */ @@ -349,9 +342,6 @@ extern "C" } cdtParameterProvider; - - - /** * Driver object */ @@ -394,7 +384,8 @@ extern "C" * @param [out] nPort Number of ports * @param [out] nComp Number of components */ - cdtResult (*getSolutionOutlet)(cdtDriver* drv, int unitOpId, double const** time, double const** data, int* nTime, int* nPort, int* nComp); + cdtResult (*getSolutionOutlet)(cdtDriver* drv, int unitOpId, double const** time, double const** data, + int* nTime, int* nPort, int* nComp); } cdtAPIv010000; @@ -404,7 +395,6 @@ extern "C" * @return Success (>= 0) if the API is available, otherwise error (< 0) */ CADET_API cdtResult cdtGetAPIv010000(cdtAPIv010000* ptr); - } -#endif // LIBCADET_CAPI_HPP_ +#endif // LIBCADET_CAPI_HPP_ diff --git a/include/cadet/cadet.hpp b/include/cadet/cadet.hpp index 9b3ff2138..2f17c399d 100644 --- a/include/cadet/cadet.hpp +++ b/include/cadet/cadet.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -13,7 +13,7 @@ /** * @mainpage CADET * The library provides a fast and accurate forward simulator for the general rate model of column liquid chromatography - * + * * @authors Eric von Lieres, Joel Andersson, Andreas Puettmann, Sebastian Schnittert, Samuel Leweke, William Heymann * @version 4.4.0 * @date 2008-2023 diff --git a/include/common/CompilerSpecific.hpp b/include/common/CompilerSpecific.hpp index b7856d029..aa9f404d5 100644 --- a/include/common/CompilerSpecific.hpp +++ b/include/common/CompilerSpecific.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -12,59 +12,59 @@ // CADET_STRONG_INLINE uses __forceinline on MSVC and Intel ICPC, but doens't use always_inline of GCC. #if (defined _MSC_VER) || (defined __INTEL_COMPILER) - #define CADET_STRONG_INLINE __forceinline +#define CADET_STRONG_INLINE __forceinline #else - #define CADET_STRONG_INLINE inline +#define CADET_STRONG_INLINE inline #endif // CADET_ALWAYS_INLINE makes the function inline by force, use with care #ifdef __GNUC__ - #define CADET_ALWAYS_INLINE __attribute__((always_inline)) inline +#define CADET_ALWAYS_INLINE __attribute__((always_inline)) inline #else - #define CADET_ALWAYS_INLINE CADET_STRONG_INLINE +#define CADET_ALWAYS_INLINE CADET_STRONG_INLINE #endif // CADET_DONT_INLINE explicitly prevents inlining #if (defined __GNUC__) - #define CADET_DONT_INLINE __attribute__((noinline)) +#define CADET_DONT_INLINE __attribute__((noinline)) #elif (defined _MSC_VER) - #define CADET_DONT_INLINE __declspec(noinline) +#define CADET_DONT_INLINE __declspec(noinline) #else - #define CADET_DONT_INLINE +#define CADET_DONT_INLINE #endif // Assume release build as default #if defined(NDEBUG) || !defined(DEBUG) - #ifndef CADET_NO_DEBUG - #define CADET_NO_DEBUG - #endif - #ifdef CADET_DEBUG - #undef CADET_DEBUG - #endif +#ifndef CADET_NO_DEBUG +#define CADET_NO_DEBUG +#endif +#ifdef CADET_DEBUG +#undef CADET_DEBUG +#endif #endif #if defined(DEBUG) || defined(CADET_DEBUG) - #define CADET_DEBUG - #undef CADET_NO_DEBUG - #include +#define CADET_DEBUG +#undef CADET_NO_DEBUG +#include #endif #ifdef CADET_NO_DEBUG - // Disable assert in release - #define cadet_assert_impl(x) +// Disable assert in release +#define cadet_assert_impl(x) #else - // Use standard assert - #define cadet_assert_impl(x) assert(x) +// Use standard assert +#define cadet_assert_impl(x) assert(x) #endif // Allow user supplied cadet_assert #ifndef cadet_assert - #define cadet_assert(x) cadet_assert_impl(x) +#define cadet_assert(x) cadet_assert_impl(x) #endif #ifdef CADET_NO_DEBUG - #define CADET_ONLY_USED_FOR_DEBUG(x) (void)x +#define CADET_ONLY_USED_FOR_DEBUG(x) (void)x #else - #define CADET_ONLY_USED_FOR_DEBUG(x) +#define CADET_ONLY_USED_FOR_DEBUG(x) #endif // Improve branch prediction by marking likely and unlikely execution paths @@ -73,11 +73,11 @@ // transforms to // if (cadet_unlikely(some_unlikely_condition)) { ... } #ifdef __GNUC__ - #define cadet_likely(x) __builtin_expect(!!(x), 1) - #define cadet_unlikely(x) __builtin_expect(!!(x), 0) +#define cadet_likely(x) __builtin_expect(!!(x), 1) +#define cadet_unlikely(x) __builtin_expect(!!(x), 0) #endif #ifndef cadet_likely - #define cadet_likely(x) (x) - #define cadet_unlikely(x) (x) +#define cadet_likely(x) (x) +#define cadet_unlikely(x) (x) #endif diff --git a/include/common/Driver.hpp b/include/common/Driver.hpp index 8c3711147..7083e9ff4 100644 --- a/include/common/Driver.hpp +++ b/include/common/Driver.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides a driver for configuring and running a CADET simulation */ @@ -62,14 +62,14 @@ void readDataOutputConfig(ParamProvider_t& pp, StorageConfig_t& cfg, const std:: else cfg.storeFlux = false; -/* - if (pp.exists("WRITE_" + dataType + "_ALL")) - { - cfg.storeColumn = pp.getBool("WRITE_" + dataType + "_ALL"); - cfg.storeParticle = cfg.storeColumn; - cfg.storeFlux = cfg.storeColumn; - } -*/ + /* + if (pp.exists("WRITE_" + dataType + "_ALL")) + { + cfg.storeColumn = pp.getBool("WRITE_" + dataType + "_ALL"); + cfg.storeParticle = cfg.storeColumn; + cfg.storeFlux = cfg.storeColumn; + } + */ if (pp.exists("WRITE_" + dataType + "_INLET")) cfg.storeInlet = pp.getBool("WRITE_" + dataType + "_INLET"); @@ -88,7 +88,7 @@ void readDataOutputConfig(ParamProvider_t& pp, StorageConfig_t& cfg, const std:: if (pp.exists("WRITE_" + dataType + "_COLUMN_OUTLET")) cfg.storeOutlet = pp.getBool("WRITE_" + dataType + "_COLUMN_OUTLET"); else - cfg.storeOutlet = false; + cfg.storeOutlet = false; } if (pp.exists("WRITE_" + dataType + "_VOLUME")) @@ -98,7 +98,8 @@ void readDataOutputConfig(ParamProvider_t& pp, StorageConfig_t& cfg, const std:: } template -void configureSystemRecorder(cadet::InternalStorageSystemRecorder& recorder, ParamProvider_t& pp, unsigned int maxUnitOperationId) +void configureSystemRecorder(cadet::InternalStorageSystemRecorder& recorder, ParamProvider_t& pp, + unsigned int maxUnitOperationId) { bool splitComponents = true; if (pp.exists("SPLIT_COMPONENTS_DATA")) @@ -127,7 +128,7 @@ void configureSystemRecorder(cadet::InternalStorageSystemRecorder& recorder, Par pp.pushScope(oss.str()); cadet::InternalStorageUnitOpRecorder* const subRec = new cadet::InternalStorageUnitOpRecorder(i); - + readDataOutputConfig(pp, cfg, "SOLUTION"); subRec->solutionConfig(cfg); @@ -153,12 +154,13 @@ void configureSystemRecorder(cadet::InternalStorageSystemRecorder& recorder, Par } template -void readSensitivityInitialState(ParamProvider_t& pp, const char* prefix, std::vector& out, std::vector>& data) +void readSensitivityInitialState(ParamProvider_t& pp, const char* prefix, std::vector& out, + std::vector>& data) { unsigned int i = 0; std::ostringstream oss; oss << prefix << std::setfill('0') << std::setw(3) << std::setprecision(0) << i; - while(pp.exists(oss.str())) + while (pp.exists(oss.str())) { data.push_back(pp.getDoubleArray(oss.str())); out.push_back(data.back().data()); @@ -190,7 +192,7 @@ class Driver if (_sim) cadetDestroySimulator(_sim); - + cadetDestroyModelBuilder(_builder); } @@ -211,7 +213,7 @@ class Driver cadetDestroySimulator(_sim); _sim = nullptr; } - + cadetDestroyModelBuilder(_builder); _builder = cadetCreateModelBuilder(); } @@ -224,8 +226,7 @@ class Driver * @param [in] pp Implementation of cadet::IParameterProvider used as input * @tparam ParamProvider_t Type of the parameter provider */ - template - void configure(ParamProvider_t& pp) + template void configure(ParamProvider_t& pp) { // Create storage delete _storage; @@ -240,7 +241,7 @@ class Driver // Configure main solver parameters pp.pushScope("solver"); _sim->configure(pp); - + // Configure section times std::vector secTimes; std::vector secCont; @@ -334,7 +335,8 @@ class Driver std::vector sensParams; sensParams.reserve(sensName.size()); for (std::size_t i = 0; i < sensName.size(); ++i) - sensParams.push_back(cadet::makeParamId(sensName[i], sensUnit[i], sensComp[i], sensParType[i], sensBoundState[i], sensReaction[i], sensSection[i])); + sensParams.push_back(cadet::makeParamId(sensName[i], sensUnit[i], sensComp[i], sensParType[i], + sensBoundState[i], sensReaction[i], sensSection[i])); double sensTol = 1e-05; if (pp.exists("SENS_ABSTOL")) @@ -372,8 +374,7 @@ class Driver * @param [in] pp Implementation of cadet::IParameterProvider used as input * @tparam ParamProvider_t Type of the parameter provider */ - template - void setInitialCondition(ParamProvider_t& pp) + template void setInitialCondition(ParamProvider_t& pp) { if (pp.exists("INIT_STATE_Y") && pp.exists("INIT_STATE_YDOT")) { @@ -418,8 +419,7 @@ class Driver * @param [in] pp Implementation of cadet::IParameterProvider used as input * @tparam ParamProvider_t Type of the parameter provider */ - template - void setSectionTimes(ParamProvider_t& pp) + template void setSectionTimes(ParamProvider_t& pp) { std::vector secTimes; std::vector secCont; @@ -428,14 +428,14 @@ class Driver } /** - * @brief Reads the return configuration of the ModelSystem and its UnitOperation models from the given parameter provider + * @brief Reads the return configuration of the ModelSystem and its UnitOperation models from the given parameter + * provider * @details Does nothing if the simulator or model have not been configured yet. * @param [in] pp Implementation of cadet::IParameterProvider used as input * @param [in] applyInSimulator Determines whether the storage is set in the simulator (@c true), or not (@c false) * @tparam ParamProvider_t Type of the parameter provider */ - template - void setReturnConfiguration(ParamProvider_t& pp, bool applyInSimulator) + template void setReturnConfiguration(ParamProvider_t& pp, bool applyInSimulator) { if (!_storage || !_sim || !_sim->model()) return; @@ -449,7 +449,7 @@ class Driver _storage->storeTime(pp.getBool("WRITE_SOLUTION_TIMES")); else _storage->storeTime(true); - + if (pp.exists("WRITE_SOLUTION_LAST")) _writeLastState = pp.getBool("WRITE_SOLUTION_LAST"); else @@ -502,8 +502,7 @@ class Driver * @param [in] writer Writer to write to * @tparam Writer_t Type of the writer */ - template - void write(Writer_t& writer) + template void write(Writer_t& writer) { if (!_sim || !_storage) return; @@ -511,7 +510,7 @@ class Driver LOG(Debug) << "Writing " << _storage->numDataPoints() << " data points to file"; writer.unlinkGroup("output"); - + writer.extendibleFields(false); writer.compressFields(true); @@ -626,13 +625,23 @@ class Driver _storage->clear(); } - inline cadet::ISimulator* simulator() const CADET_NOEXCEPT { return _sim; } - inline cadet::IModelBuilder* modelBuilder() const CADET_NOEXCEPT { return _builder; } - inline cadet::IModelSystem* model() const { return _sim->model(); } + inline cadet::ISimulator* simulator() const CADET_NOEXCEPT + { + return _sim; + } + inline cadet::IModelBuilder* modelBuilder() const CADET_NOEXCEPT + { + return _builder; + } + inline cadet::IModelSystem* model() const + { + return _sim->model(); + } inline void setWriteLastStateOfUnit(UnitOpIdx uid, bool writeLastStateUnit) { - const std::vector::iterator it = std::find(_writeLastStateUnitId.begin(), _writeLastStateUnitId.end(), uid); + const std::vector::iterator it = + std::find(_writeLastStateUnitId.begin(), _writeLastStateUnitId.end(), uid); if (writeLastStateUnit) { if (it == _writeLastStateUnitId.end()) @@ -651,20 +660,32 @@ class Driver } } - inline void setWriteLastState(bool writeLastState) CADET_NOEXCEPT { _writeLastState = writeLastState; } - inline void setWriteLastStateSens(bool writeLastState) CADET_NOEXCEPT { _writeLastStateSens = writeLastState; } + inline void setWriteLastState(bool writeLastState) CADET_NOEXCEPT + { + _writeLastState = writeLastState; + } + inline void setWriteLastStateSens(bool writeLastState) CADET_NOEXCEPT + { + _writeLastStateSens = writeLastState; + } inline void setWriteSolutionTimes(bool solTimes) CADET_NOEXCEPT { if (_storage) _storage->storeTime(solTimes); } - inline cadet::InternalStorageSystemRecorder* solution() CADET_NOEXCEPT { return _storage; } - inline cadet::InternalStorageSystemRecorder const* solution() const CADET_NOEXCEPT { return _storage; } + inline cadet::InternalStorageSystemRecorder* solution() CADET_NOEXCEPT + { + return _storage; + } + inline cadet::InternalStorageSystemRecorder const* solution() const CADET_NOEXCEPT + { + return _storage; + } protected: - cadet::ISimulator* _sim; //!< Simulator owned by this driver - cadet::IModelBuilder* _builder; //!< Model builder owned by this driver + cadet::ISimulator* _sim; //!< Simulator owned by this driver + cadet::IModelBuilder* _builder; //!< Model builder owned by this driver cadet::InternalStorageSystemRecorder* _storage; //!< Storage for results bool _writeLastState; @@ -685,7 +706,7 @@ class Driver pp.pushScope("sections"); secTimes = pp.getDoubleArray("SECTION_TIMES"); - if (pp.exists("SECTION_CONTINUITY")) + if (pp.exists("SECTION_CONTINUITY")) secCont = pp.getBoolArray("SECTION_CONTINUITY"); else secCont = std::vector(secTimes.size() - 2, false); @@ -699,4 +720,4 @@ class Driver } // namespace cadet -#endif // CADET_DRIVER_HPP_ +#endif // CADET_DRIVER_HPP_ diff --git a/include/common/JsonParameterProvider.hpp b/include/common/JsonParameterProvider.hpp index d9f3a5959..4f7916338 100644 --- a/include/common/JsonParameterProvider.hpp +++ b/include/common/JsonParameterProvider.hpp @@ -32,7 +32,6 @@ namespace cadet class JsonParameterProvider : public cadet::IParameterProvider { public: - JsonParameterProvider(const char* data); JsonParameterProvider(const std::string& data); JsonParameterProvider(const nlohmann::json& data); @@ -77,11 +76,18 @@ class JsonParameterProvider : public cadet::IParameterProvider void copy(const std::string& src, const std::string& dest); - inline nlohmann::json* data() { return _root; } - inline nlohmann::json const* data() const { return _root; } + inline nlohmann::json* data() + { + return _root; + } + inline nlohmann::json const* data() const + { + return _root; + } void toFile(const std::string& fileName) const; static JsonParameterProvider fromFile(const std::string& fileName); + private: JsonParameterProvider(); JsonParameterProvider(nlohmann::json* data); @@ -97,4 +103,4 @@ class JsonParameterProvider : public cadet::IParameterProvider std::ostream& operator<<(std::ostream& out, const JsonParameterProvider& jpp); } // namespace cadet -#endif // CADET_JSONPARAMETERPROVIDER_HPP_ +#endif // CADET_JSONPARAMETERPROVIDER_HPP_ diff --git a/include/common/Logger.hpp b/include/common/Logger.hpp index d7e7963ba..6140974d4 100644 --- a/include/common/Logger.hpp +++ b/include/common/Logger.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,12 +11,12 @@ // ============================================================================= /** - * @file + * @file * Implementation of a logging mechanism that can filter messages at compile- and runtime. - * + * * This implementation is based on templog, a logging library created by Hendrik Schober * distributed under the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt). - * Templog can be found at http://templog.sourceforge.net/ + * Templog can be found at http://templog.sourceforge.net/ */ #ifndef CADET_LOGGER_HPP_ @@ -30,69 +30,78 @@ namespace cadet { namespace log { - /** - * @brief Sends all messages to std::cout - */ - class StdOutWritePolicy : public BufferedWritePolicyBase +/** + * @brief Sends all messages to std::cout + */ +class StdOutWritePolicy : public BufferedWritePolicyBase +{ +public: + static inline void begin(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl) { - public: - static inline void begin(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl) { } + } - static inline void end(LogLevel lvl) { std::cout << std::endl; } + static inline void end(LogLevel lvl) + { + std::cout << std::endl; + } - template - static inline void writeObj(LogLevel lvl, const T& obj) - { - std::cout << obj; - } - }; + template static inline void writeObj(LogLevel lvl, const T& obj) + { + std::cout << obj; + } +}; - /** - * @brief Sends all messages to std::cerr - */ - class StdErrWritePolicy : public BufferedWritePolicyBase +/** + * @brief Sends all messages to std::cerr + */ +class StdErrWritePolicy : public BufferedWritePolicyBase +{ +public: + static inline void begin(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl) { - public: - static inline void begin(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl) { } + } - static inline void end(LogLevel lvl) { std::cerr << std::endl; } + static inline void end(LogLevel lvl) + { + std::cerr << std::endl; + } - template - static inline void writeObj(LogLevel lvl, const T& obj) - { - std::cerr << obj; - } - }; + template static inline void writeObj(LogLevel lvl, const T& obj) + { + std::cerr << obj; + } +}; - /** - * @brief Sends messages up to a specified log level to std::cerr and the rest to std::cout - * @tparam switchLvl Defines the first log level that is sent to std::cout - */ - template - class SelectiveStdWritePolicy : public BufferedWritePolicyBase> +/** + * @brief Sends messages up to a specified log level to std::cerr and the rest to std::cout + * @tparam switchLvl Defines the first log level that is sent to std::cout + */ +template +class SelectiveStdWritePolicy : public BufferedWritePolicyBase> +{ +public: + static inline void begin(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl) { - public: - static inline void begin(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl) { } + } - static inline void end(LogLevel lvl) - { - if (lvl < switchLvl) - std::cerr << std::endl; - else - std::cout << std::endl; - } + static inline void end(LogLevel lvl) + { + if (lvl < switchLvl) + std::cerr << std::endl; + else + std::cout << std::endl; + } - template - static inline void writeObj(LogLevel lvl, const T& obj) - { - if (lvl < switchLvl) - std::cerr << std::endl; - else - std::cout << std::endl; - } - }; + template static inline void writeObj(LogLevel lvl, const T& obj) + { + if (lvl < switchLvl) + std::cerr << std::endl; + else + std::cout << std::endl; + } +}; } // namespace log } // namespace cadet -#endif // CADET_LOGGER_HPP_ +#endif // CADET_LOGGER_HPP_ diff --git a/include/common/LoggerBase.hpp b/include/common/LoggerBase.hpp index ad67bb3b1..1bbfed2da 100644 --- a/include/common/LoggerBase.hpp +++ b/include/common/LoggerBase.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,12 +11,12 @@ // ============================================================================= /** - * @file + * @file * Implementation of a logging mechanism that can filter messages at compile- and runtime. - * + * * This implementation is based on templog, a logging library created by Hendrik Schober * distributed under the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt). - * Templog can be found at http://templog.sourceforge.net/ + * Templog can be found at http://templog.sourceforge.net/ */ #ifndef CADET_LOGGERBASE_HPP_ @@ -41,457 +41,489 @@ inline const char* to_string(LogLevel lvl) CADET_NOEXCEPT; namespace log { - namespace detail +namespace detail +{ +/** + * @brief Empty type used as @c nullptr pendant + */ +struct NullType +{ +}; + +template struct NestedList +{ + typedef T1 left_t; + typedef T2 right_t; + T1 left; + T2 right; + + NestedList(const T1& l, const T2& r) CADET_NOEXCEPT : left(l), right(r) { - /** - * @brief Empty type used as @c nullptr pendant - */ - struct NullType { }; + } +}; - template - struct NestedList - { - typedef T1 left_t; - typedef T2 right_t; - T1 left; - T2 right; +/** + * @brief Stores a log message including all parameters / variables + * @details If the log statement is not passed on, it will be removed. Otherwise, the + * statement and the stored variables are assembled to a string and passed on + * to a receiver. + * @tparam lvl LogLevel of this message + * @tparam passOn Determines whether the message is passed on or filtered out + * @tparam params_t Parameters of the statement (type list) + */ +template struct LogMessage +{ + params_t params; - NestedList(const T1& l, const T2& r) CADET_NOEXCEPT : left(l), right(r) { } - }; + LogMessage(const params_t& p = params_t()) CADET_NOEXCEPT : params(p) + { + } +}; - /** - * @brief Stores a log message including all parameters / variables - * @details If the log statement is not passed on, it will be removed. Otherwise, the - * statement and the stored variables are assembled to a string and passed on - * to a receiver. - * @tparam lvl LogLevel of this message - * @tparam passOn Determines whether the message is passed on or filtered out - * @tparam params_t Parameters of the statement (type list) - */ - template - struct LogMessage - { - params_t params; +/** + * @brief Adds a parameter to a log statement + * @details In this version, the statement is discarded. Thus, no parameters are saved. + */ +template +inline LogMessage operator<<(const LogMessage&, + const param_t&) CADET_NOEXCEPT +{ + return LogMessage(); +} - LogMessage(const params_t& p = params_t()) CADET_NOEXCEPT : params(p) { } - }; +/** + * @brief Adds a parameter to a log statement + * @details In this version, the statement is passed on. Thus, the parameter is saved in a (nested) pair. + */ +template +inline LogMessage> operator<<( + const LogMessage& lm, const param_t& p) CADET_NOEXCEPT +{ + return LogMessage>( + NestedList(lm.params, &p)); +} - /** - * @brief Adds a parameter to a log statement - * @details In this version, the statement is discarded. Thus, no parameters are saved. - */ - template - inline LogMessage operator<<(const LogMessage&, const param_t&) CADET_NOEXCEPT - { - return LogMessage(); - } - - /** - * @brief Adds a parameter to a log statement - * @details In this version, the statement is passed on. Thus, the parameter is saved in a (nested) pair. - */ - template - inline LogMessage> operator<<(const LogMessage& lm, const param_t& p) CADET_NOEXCEPT - { - return LogMessage>(NestedList(lm.params, &p)); - } - - /** - * @brief Compiles a full log statement consisting of positional information and a log mesage that is sent to the @p logger_t - * @details LogStatement holds positional information about a log statement (filename, function name, line number) - * and, upon assignment of a LogMessage, forwards the entire statement (including the message) to the - * message() function of the logger_t type. - * @tparam logger_t Logger to forward messages to - */ - template - struct LogStatement - { - const char* fileName; - const char* funcName; - unsigned int line; - - LogStatement(const char* fin, const char* fun, unsigned int ln) CADET_NOEXCEPT : fileName(fin), funcName(fun), line(ln) { } - - template - inline void operator=(const LogMessage& lm) - { - logger_t::forward(fileName, funcName, line, lm); - } - - inline LogStatement& operator=(const LogStatement& cpy) CADET_NOEXCEPT - { - fileName = cpy.fileName; - funcName = cpy.funcName; - line = cpy.line; - } - }; +/** + * @brief Compiles a full log statement consisting of positional information and a log mesage that is sent to the @p + * logger_t + * @details LogStatement holds positional information about a log statement (filename, function name, line number) + * and, upon assignment of a LogMessage, forwards the entire statement (including the message) to the + * message() function of the logger_t type. + * @tparam logger_t Logger to forward messages to + */ +template struct LogStatement +{ + const char* fileName; + const char* funcName; + unsigned int line; - } // namespace detail + LogStatement(const char* fin, const char* fun, unsigned int ln) CADET_NOEXCEPT : fileName(fin), + funcName(fun), + line(ln) + { + } + template + inline void operator=(const LogMessage& lm) + { + logger_t::forward(fileName, funcName, line, lm); + } - template - inline std::ostream& operator<<(std::ostream& os, const std::vector& v) + inline LogStatement& operator=(const LogStatement& cpy) CADET_NOEXCEPT { - os << "["; - if (!v.empty()) - { - for (std::size_t i = 0; i < v.size()-1; ++i) - os << v[i] << ","; - os << v.back(); - } - os << "]"; - return os; + fileName = cpy.fileName; + funcName = cpy.funcName; + line = cpy.line; } +}; +} // namespace detail +template inline std::ostream& operator<<(std::ostream& os, const std::vector& v) +{ + os << "["; + if (!v.empty()) + { + for (std::size_t i = 0; i < v.size() - 1; ++i) + os << v[i] << ","; + os << v.back(); + } + os << "]"; + return os; +} + +/** + * @brief Logger class that filters log messages at compile- or runtime + * @details A hierarchy of loggers is used to filter out messages. Only the logger at the very + * top of the chain actually writes messages to its destination. This logger should + * always be the bottom one in the chain (i.e., the first to see a LogMessage) in order + * for compile-time filtering to work. The loggers messages are passed on to by this + * logger has to provide two functions with the following signatures: + *
+ *              template 
+ *              static inline void forward(const char* fileName, const char* funcName, unsigned int line, const
+ * LogMessage& lm)
+ *
+ *              template 
+ *              static inline void forward(const char* fileName, const char* funcName, unsigned int line, const
+ * LogMessage& lm)
+ *          
+ * These functions receive log messages that have not been filtered out or have been filtered out + * (respectively) at compile time. + */ +template class Logger +{ +private: /** - * @brief Logger class that filters log messages at compile- or runtime - * @details A hierarchy of loggers is used to filter out messages. Only the logger at the very - * top of the chain actually writes messages to its destination. This logger should - * always be the bottom one in the chain (i.e., the first to see a LogMessage) in order - * for compile-time filtering to work. The loggers messages are passed on to by this - * logger has to provide two functions with the following signatures: - *
-	 *              template 
-	 *              static inline void forward(const char* fileName, const char* funcName, unsigned int line, const LogMessage& lm)
-	 *
-	 *              template 
-	 *              static inline void forward(const char* fileName, const char* funcName, unsigned int line, const LogMessage& lm)
-	 *          
- * These functions receive log messages that have not been filtered out or have been filtered out (respectively) at compile time. + * @brief Compile-time template construct for determining whether a message will be discarded */ - template - class Logger + template struct ForwardMessage { - private: - - /** - * @brief Compile-time template construct for determining whether a message will be discarded - */ - template - struct ForwardMessage + enum { - enum - { - result = (lvl >= queryLvl) - }; + result = (lvl >= queryLvl) }; + }; - public: +public: + /** + * @brief This logger type + */ + typedef Logger this_logger_t; - /** - * @brief This logger type - */ - typedef Logger this_logger_t; + /** + * @brief Type of logger the messages are passed on to + */ + typedef nextLogger_t forward_logger_t; - /** - * @brief Type of logger the messages are passed on to - */ - typedef nextLogger_t forward_logger_t; + /** + * @brief Creates a LogStatement with positional information + */ + static inline detail::LogStatement statement(const char* fileName, const char* funcName, + unsigned int line) + { + return detail::LogStatement(fileName, funcName, line); + } - /** - * @brief Creates a LogStatement with positional information - */ - static inline detail::LogStatement statement(const char* fileName, const char* funcName, unsigned int line) - { - return detail::LogStatement(fileName, funcName, line); - } - - /** - * @brief Creates a LogMessage which is subsequently constructed and then assigned to a LogStatement - * @details Here, it is decided whether the LogStatement / LogMessage is filtered out at compile time. - * If ForwardMessage::result if @c true, then the message is passed on. - * Otherwise it is filtered out by not passing it on to the next logger. - */ - template - static inline detail::LogMessage::result, detail::NullType> createMessage() - { - return detail::LogMessage::result, detail::NullType>(); - } - - /** - * @brief Passes a given LogMessage on to the next logger. Overload for discarded messages (no forwarding). - */ - template - static inline void forward(const char*, const char*, unsigned int, const detail::LogMessage&) { } - - /** - * @brief Passes a given LogMessage on to the next logger. Overload for passed on messages (forwarding). - */ - template - static inline void forward(const char* fileName, const char* funcName, unsigned int line, const detail::LogMessage& lm) - { - forward_logger_t::forward(fileName, funcName, line, lm); - } - }; + /** + * @brief Creates a LogMessage which is subsequently constructed and then assigned to a LogStatement + * @details Here, it is decided whether the LogStatement / LogMessage is filtered out at compile time. + * If ForwardMessage::result if @c true, then the message is passed on. + * Otherwise it is filtered out by not passing it on to the next logger. + */ + template + static inline detail::LogMessage::result, detail::NullType> createMessage() + { + return detail::LogMessage::result, detail::NullType>(); + } /** - * @brief Provides base for formatting of log messages - * @details Formats a log message and uses a write policy to send the message to a receiver. - * The formatting policy collaborates closely with the write policy in order to - * achieve a decent performance via compile time optimization. - * - * Implementations have to implement the function - *
-	 *             template 
-	 *             static inline void format(receiver_t& recv, const char* fileName, const char* funcName, unsigned int line, const paramList_t& p)
-	 *          
- * which uses the methods writeObj() and writeParams() of this class to format the given - * log message. - * - * @sa StandardFormattingPolicy - * @tparam subFormattingPolicy_t Actual formatting policy implementation (CRTP) + * @brief Passes a given LogMessage on to the next logger. Overload for discarded messages (no forwarding). */ - template - class FormattingPolicyBase + template + static inline void forward(const char*, const char*, unsigned int, + const detail::LogMessage&) { - public: - - /** - * @brief Formats and writes a log message - * @details This function is called by the write policy and serves as an entry point to - * the formatting policy mechanism. - */ - template - static inline void formatMessage(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, const paramList_t& p) - { - formatImpl(fileName, funcName, line, lvl, p, typename writePolicy_t::hasBuffer()); - } + } - protected: + /** + * @brief Passes a given LogMessage on to the next logger. Overload for passed on messages (forwarding). + */ + template + static inline void forward(const char* fileName, const char* funcName, unsigned int line, + const detail::LogMessage& lm) + { + forward_logger_t::forward(fileName, funcName, line, lm); + } +}; - /** - * @brief Writes an object (e.g., string) using the given write policy - */ - template - static inline void writeObj(receiver_t& recv, LogLevel lvl, const T& obj) - { - writeObjImpl(recv, lvl, obj); - } - - /** - * @brief Writes the parameters of the log message (i.e., the message itself) using the given write policy - */ - template - static inline void writeParams(receiver_t& recv, LogLevel lvl, const detail::NestedList& p) - { - writeParamsImpl(recv, lvl, p); - } +/** + * @brief Provides base for formatting of log messages + * @details Formats a log message and uses a write policy to send the message to a receiver. + * The formatting policy collaborates closely with the write policy in order to + * achieve a decent performance via compile time optimization. + * + * Implementations have to implement the function + *
+ *             template 
+ *             static inline void format(receiver_t& recv, const char* fileName, const char* funcName, unsigned int
+ * line, const paramList_t& p)
+ *          
+ * which uses the methods writeObj() and writeParams() of this class to format the given + * log message. + * + * @sa StandardFormattingPolicy + * @tparam subFormattingPolicy_t Actual formatting policy implementation (CRTP) + */ +template class FormattingPolicyBase +{ +public: + /** + * @brief Formats and writes a log message + * @details This function is called by the write policy and serves as an entry point to + * the formatting policy mechanism. + */ + template + static inline void formatMessage(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, + const paramList_t& p) + { + formatImpl(fileName, funcName, line, lvl, p, typename writePolicy_t::hasBuffer()); + } + +protected: + /** + * @brief Writes an object (e.g., string) using the given write policy + */ + template + static inline void writeObj(receiver_t& recv, LogLevel lvl, const T& obj) + { + writeObjImpl(recv, lvl, obj); + } - private: + /** + * @brief Writes the parameters of the log message (i.e., the message itself) using the given write policy + */ + template + static inline void writeParams(receiver_t& recv, LogLevel lvl, const detail::NestedList& p) + { + writeParamsImpl(recv, lvl, p); + } - /** - * @brief Boiler plate code for the implementation in the @c subFormattingPolicy_t - * @details Buffered write policy version. - */ - template - static inline void formatImpl(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, const paramList_t& p, std::true_type inc) - { - writePolicy_t::begin(fileName, funcName, line, lvl); +private: + /** + * @brief Boiler plate code for the implementation in the @c subFormattingPolicy_t + * @details Buffered write policy version. + */ + template + static inline void formatImpl(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, + const paramList_t& p, std::true_type inc) + { + writePolicy_t::begin(fileName, funcName, line, lvl); - detail::NullType dummy; - subFormattingPolicy_t::template format(dummy, fileName, funcName, line, lvl, p); + detail::NullType dummy; + subFormattingPolicy_t::template format(dummy, fileName, funcName, line, lvl, + p); - writePolicy_t::end(lvl); - } + writePolicy_t::end(lvl); + } - /** - * @brief Boiler plate code for the implementation in the @c subFormattingPolicy_t - * @details Non-buffered write policy version. Buffers everything in a stringstream - * which is then handed over at once to the write policy. - */ - template - static inline void formatImpl(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, const paramList_t& p, std::false_type inc) - { - std::ostringstream oss; - subFormattingPolicy_t::template format(oss, fileName, funcName, line, lvl, p); - oss << "\n"; - writePolicy_t::writeLine(fileName, funcName, line, lvl, oss.str()); - } - - /** - * @brief Writes an object using the given write policy - * @details Non-buffered write policy version. - */ - template - static inline void writeObjImpl(detail::NullType& dummy, LogLevel lvl, const T& obj) - { - writePolicy_t::writeObj(lvl, obj); - } - - /** - * @brief Writes an object using the given write policy - * @details Buffered write policy version. - */ - template - static inline void writeObjImpl(std::ostream& os, LogLevel lvl, const T& obj) - { - os << obj; - } - - /** - * @brief Writes all parameters (i.e., the actual log message) - * @details The parameters are saved in a nested chain of std::pair. Recursion is - * applied to write all parameters in the correct order. Given an std::pair, - * the parameter is placed in the second argument and the rest of the chain in - * the first. Thus, the first parameter handed to the LogMessage object is the - * most left leaf in the tree. - */ - template - static inline void writeParamsImpl(receiver_t& recv, LogLevel lvl, const detail::NestedList& p) - { - writeParamsImpl(recv, lvl, p.left); - writeParamsImpl(recv, lvl, p.right); - } - - /** - * @brief Writes all parameters (i.e., the actual log message) - * @details Terminates the recursion. - */ - template - static inline void writeParamsImpl(receiver_t& recv, LogLevel lvl, detail::NullType p) { } - - /** - * @brief Writes all parameters (i.e., the actual log message) - * @details Terminates the recursion. - */ - template - static inline void writeParamsImpl(receiver_t& recv, LogLevel lvl, const detail::NullType* p) { } - - /** - * @brief Writes all parameters (i.e., the actual log message) - * @details Writes an object to a receiver using the given write policy. Considering - * the chain of std::pairs as a tree, this function handles all the right - * leaves containing parameters. - */ - template - static inline void writeParamsImpl(receiver_t& recv, LogLevel lvl, const T* p) - { - writeObj(recv, lvl, *p); - } - }; + /** + * @brief Boiler plate code for the implementation in the @c subFormattingPolicy_t + * @details Non-buffered write policy version. Buffers everything in a stringstream + * which is then handed over at once to the write policy. + */ + template + static inline void formatImpl(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, + const paramList_t& p, std::false_type inc) + { + std::ostringstream oss; + subFormattingPolicy_t::template format(oss, fileName, funcName, line, lvl, + p); + oss << "\n"; + writePolicy_t::writeLine(fileName, funcName, line, lvl, oss.str()); + } /** - * @brief Implements a standard formatting policy + * @brief Writes an object using the given write policy + * @details Non-buffered write policy version. */ - class StandardFormattingPolicy : public FormattingPolicyBase + template + static inline void writeObjImpl(detail::NullType& dummy, LogLevel lvl, const T& obj) { - public: - template - static inline void format(receiver_t& recv, const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, const paramList_t& p) - { - writeObj(recv, lvl, '['); - writeObj(recv, lvl, to_string(lvl)); - writeObj(recv, lvl, ": "); - writeObj(recv, lvl, fileName); - writeObj(recv, lvl, "::"); - writeObj(recv, lvl, funcName); - writeObj(recv, lvl, "::"); - writeObj(recv, lvl, line); - writeObj(recv, lvl, "] "); - writeParams(recv, lvl, p); - } - }; + writePolicy_t::writeObj(lvl, obj); + } /** - * @brief Base class for write policies - * @details Write policies can either use an internal buffer that allows for incremental writing - * like streams (think of std::cout, for example), or use direct writing without an internal - * buffer (e.g., printf()). - * - * The write policy calls the formatting policy which in turn then uses the write policy to - * send the formatted message to the receiver. Thus, write and formatting policies collaborate - * closely. - * - * Derive your implementation from BufferedWritePolicyBase or NonBufferedWritePolicyBase - * for convenience. - * - * @sa BufferedWritePolicyBase - * @sa NonBufferedWritePolicyBase - * @tparam subWritePolicy_t Actual write policy implementation (CRTP) - * @tparam incWrite @c true for buffered and @c false for direct writing + * @brief Writes an object using the given write policy + * @details Buffered write policy version. */ - template - class WritePolicyBase + template + static inline void writeObjImpl(std::ostream& os, LogLevel lvl, const T& obj) { - public: - typedef typename std::integral_constant hasBuffer; + os << obj; + } - template - static inline void write(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, const paramList_t& p) - { - writeImpl(fileName, funcName, line, lvl, p); - } - private: + /** + * @brief Writes all parameters (i.e., the actual log message) + * @details The parameters are saved in a nested chain of std::pair. Recursion is + * applied to write all parameters in the correct order. Given an std::pair, + * the parameter is placed in the second argument and the rest of the chain in + * the first. Thus, the first parameter handed to the LogMessage object is the + * most left leaf in the tree. + */ + template + static inline void writeParamsImpl(receiver_t& recv, LogLevel lvl, const detail::NestedList& p) + { + writeParamsImpl(recv, lvl, p.left); + writeParamsImpl(recv, lvl, p.right); + } - template - static inline void writeImpl(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, const paramList_t& p) - { - formattingPolicy_t::template formatMessage(fileName, funcName, line, lvl, p); - } - }; + /** + * @brief Writes all parameters (i.e., the actual log message) + * @details Terminates the recursion. + */ + template + static inline void writeParamsImpl(receiver_t& recv, LogLevel lvl, detail::NullType p) + { + } /** - * @brief Buffered write policies have an internal buffer - * @details Derive from this class to implement your own write policy that has an internal buffer - * and an interface such as std::ostream. You need to implement the following functions: - *
-	 *              static inline void beginWrite(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl);
-	 *              static inline void endWrite(LogLevel lvl);
-	 *              template  static inline void writeObj(LogLevel lvl, const T& obj);
-	 *          
- * The functions are called at the beginning of a log statement, its end, and for every - * object that is written. The endWrite() function should append a newline '\n' character. - * @sa WritePolicyBase + * @brief Writes all parameters (i.e., the actual log message) + * @details Terminates the recursion. */ - template - class BufferedWritePolicyBase : public WritePolicyBase { }; + template + static inline void writeParamsImpl(receiver_t& recv, LogLevel lvl, const detail::NullType* p) + { + } /** - * @brief Non-buffered write policies do not have an internal buffer - * @details Derive from this class to implement your own write policy that does not have an internal - * buffer. Instead, buffering is done by the logging library and full strings / lines are handed - * to the writer. You need to implement the following functions: - *
-	 *              static inline void writeLine(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, const std::string& msg);
-	 *          
- * The lines send to write() already contain a final newline '\n' character. - * @sa WritePolicyBase + * @brief Writes all parameters (i.e., the actual log message) + * @details Writes an object to a receiver using the given write policy. Considering + * the chain of std::pairs as a tree, this function handles all the right + * leaves containing parameters. */ - template - class NonBufferedWritePolicyBase : public WritePolicyBase { }; + template + static inline void writeParamsImpl(receiver_t& recv, LogLevel lvl, const T* p) + { + writeObj(recv, lvl, *p); + } +}; - template - class RuntimeFilteringLogger +/** + * @brief Implements a standard formatting policy + */ +class StandardFormattingPolicy : public FormattingPolicyBase +{ +public: + template + static inline void format(receiver_t& recv, const char* fileName, const char* funcName, unsigned int line, + LogLevel lvl, const paramList_t& p) { - public: - template - static inline void forward(const char* fileName, const char* funcName, unsigned int line, const detail::LogMessage& lm) - { - if (lvl <= _minLvl) - forward_logger_t::forward(fileName, funcName, line, lm); - } + writeObj(recv, lvl, '['); + writeObj(recv, lvl, to_string(lvl)); + writeObj(recv, lvl, ": "); + writeObj(recv, lvl, fileName); + writeObj(recv, lvl, "::"); + writeObj(recv, lvl, funcName); + writeObj(recv, lvl, "::"); + writeObj(recv, lvl, line); + writeObj(recv, lvl, "] "); + writeParams(recv, lvl, p); + } +}; - static inline LogLevel level() CADET_NOEXCEPT { return _minLvl; } - static inline void level(LogLevel newLvl) CADET_NOEXCEPT { _minLvl = newLvl; } +/** + * @brief Base class for write policies + * @details Write policies can either use an internal buffer that allows for incremental writing + * like streams (think of std::cout, for example), or use direct writing without an internal + * buffer (e.g., printf()). + * + * The write policy calls the formatting policy which in turn then uses the write policy to + * send the formatted message to the receiver. Thus, write and formatting policies collaborate + * closely. + * + * Derive your implementation from BufferedWritePolicyBase or NonBufferedWritePolicyBase + * for convenience. + * + * @sa BufferedWritePolicyBase + * @sa NonBufferedWritePolicyBase + * @tparam subWritePolicy_t Actual write policy implementation (CRTP) + * @tparam incWrite @c true for buffered and @c false for direct writing + */ +template class WritePolicyBase +{ +public: + typedef typename std::integral_constant hasBuffer; - private: - static LogLevel _minLvl; - }; + template + static inline void write(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, + const paramList_t& p) + { + writeImpl(fileName, funcName, line, lvl, p); + } - template - class NonFilteringLogger +private: + template + static inline void writeImpl(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, + const paramList_t& p) { - public: - template - static inline void forward(const char* fileName, const char* funcName, unsigned int line, const detail::LogMessage& lm) { } + formattingPolicy_t::template formatMessage(fileName, funcName, line, lvl, p); + } +}; - template - static inline void forward(const char* fileName, const char* funcName, unsigned int line, const detail::LogMessage& lm) - { - writePolicy_t::template write(fileName, funcName, line, lvl, lm.params); - } - }; +/** + * @brief Buffered write policies have an internal buffer + * @details Derive from this class to implement your own write policy that has an internal buffer + * and an interface such as std::ostream. You need to implement the following functions: + *
+ *              static inline void beginWrite(const char* fileName, const char* funcName, unsigned int line, LogLevel
+ * lvl); static inline void endWrite(LogLevel lvl); template  static inline void writeObj(LogLevel lvl, const
+ * T& obj);
+ *          
+ * The functions are called at the beginning of a log statement, its end, and for every + * object that is written. The endWrite() function should append a newline '\n' character. + * @sa WritePolicyBase + */ +template class BufferedWritePolicyBase : public WritePolicyBase +{ +}; + +/** + * @brief Non-buffered write policies do not have an internal buffer + * @details Derive from this class to implement your own write policy that does not have an internal + * buffer. Instead, buffering is done by the logging library and full strings / lines are handed + * to the writer. You need to implement the following functions: + *
+ *              static inline void writeLine(const char* fileName, const char* funcName, unsigned int line, LogLevel
+ * lvl, const std::string& msg);
+ *          
+ * The lines send to write() already contain a final newline '\n' character. + * @sa WritePolicyBase + */ +template class NonBufferedWritePolicyBase : public WritePolicyBase +{ +}; + +template class RuntimeFilteringLogger +{ +public: + template + static inline void forward(const char* fileName, const char* funcName, unsigned int line, + const detail::LogMessage& lm) + { + if (lvl <= _minLvl) + forward_logger_t::forward(fileName, funcName, line, lm); + } + + static inline LogLevel level() CADET_NOEXCEPT + { + return _minLvl; + } + static inline void level(LogLevel newLvl) CADET_NOEXCEPT + { + _minLvl = newLvl; + } + +private: + static LogLevel _minLvl; +}; + +template class NonFilteringLogger +{ +public: + template + static inline void forward(const char* fileName, const char* funcName, unsigned int line, + const detail::LogMessage& lm) + { + } + + template + static inline void forward(const char* fileName, const char* funcName, unsigned int line, + const detail::LogMessage& lm) + { + writePolicy_t::template write(fileName, funcName, line, lvl, lm.params); + } +}; } // namespace log } // namespace cadet @@ -502,6 +534,7 @@ namespace log *
LOG_BASE(myLogger, Info) << "My log line " << arg1;
* no semicolon is appended. */ -#define LOG_BASE(logger_t, lvl) logger_t::statement(__FILE__, __func__, __LINE__) = logger_t::template createMessage() +#define LOG_BASE(logger_t, lvl) \ + logger_t::statement(__FILE__, __func__, __LINE__) = logger_t::template createMessage() -#endif // CADET_LOGGERBASE_HPP_ +#endif // CADET_LOGGERBASE_HPP_ diff --git a/include/common/OrderingConverter.hpp b/include/common/OrderingConverter.hpp index 2ace9932d..cbfe88ee2 100644 --- a/include/common/OrderingConverter.hpp +++ b/include/common/OrderingConverter.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides means to convert between column-major and row-major storage ordering of tensors */ @@ -32,7 +32,7 @@ namespace cadet * appended to a long array. Column-major, on the contrary, means that the first * subscript changes the fastest and the last the slowest. This would mean that * the columns of a matrix are appended to form a long array. - * + * * This class helps in converting between those to orderings. Given the rank and * the dimensions of the tensor, a linear index in a column- or row-major array * is converted to a subscript index (index for each dimension). This subscript @@ -52,8 +52,7 @@ class OrderingConverter * @param [in] dims Vector with the dimensions of the tensor (length of this vector is the rank) * @tparam T Datatype of the dimensions, has to fit into @c std::size_t */ - template - OrderingConverter(const std::vector dims) : _subscript(dims.size(), 0) + template OrderingConverter(const std::vector dims) : _subscript(dims.size(), 0) { prepareConversionToCol(dims.data(), dims.size()); prepareConversionToRow(dims.data(), dims.size()); @@ -65,8 +64,7 @@ class OrderingConverter * @param [in] size Length of the @p dims array, rank of the tensor * @tparam T Datatype of the dimensions, has to fit into @c std::size_t */ - template - OrderingConverter(T const* dims, unsigned int size) : _subscript(size, 0) + template OrderingConverter(T const* dims, unsigned int size) : _subscript(size, 0) { cadet_assert(size > 0); prepareConversionToCol(dims, size); @@ -200,8 +198,7 @@ class OrderingConverter * @return Linear index to row-major storage * @tparam T Datatype of the subscript indices, has to fit into @c std::size_t */ - template - inline index_t subscriptToRowMajor(T const* idx) const + template inline index_t subscriptToRowMajor(T const* idx) const { return subscriptToLinear(idx, _prodToRow); } @@ -223,8 +220,7 @@ class OrderingConverter * @return Linear index to column-major storage * @tparam T Datatype of the subscript indices, has to fit into @c std::size_t */ - template - inline index_t subscriptToColMajor(T const* idx) const + template inline index_t subscriptToColMajor(T const* idx) const { return subscriptToLinear(idx, _prodToCol); } @@ -233,15 +229,17 @@ class OrderingConverter * @brief Returns the current subscript index as set by the last call to colToRow() or rowToCol() * @return Subscript index produced by the last call to colToRow() or rowToCol() */ - inline const std::vector subscriptIndex() const { return _subscript; } + inline const std::vector subscriptIndex() const + { + return _subscript; + } protected: - std::vector _prodToRow; //!< Helper vector for conversions from / to row-major - std::vector _prodToCol; //!< Helper vector for conversions from / to column-major + std::vector _prodToRow; //!< Helper vector for conversions from / to row-major + std::vector _prodToCol; //!< Helper vector for conversions from / to column-major mutable std::vector _subscript; //!< Cache for subscript index - template - void prepareConversionToCol(T const* dims, unsigned int size) + template void prepareConversionToCol(T const* dims, unsigned int size) { _prodToCol.resize(size); unsigned int p = 1; @@ -253,8 +251,7 @@ class OrderingConverter } } - template - void prepareConversionToRow(T const* dims, unsigned int size) + template void prepareConversionToRow(T const* dims, unsigned int size) { _prodToRow.resize(size); unsigned int p = 1; @@ -274,8 +271,7 @@ class OrderingConverter * @param [in] dimsProd Helper array for the requested target storage order * @return Linear index to the requested target storage */ - template - inline index_t subscriptToLinear(T const* idx, const std::vector& dimsProd) const + template inline index_t subscriptToLinear(T const* idx, const std::vector& dimsProd) const { index_t retIndex = 0; for (std::size_t i = 0; i < dimsProd.size(); ++i) @@ -286,4 +282,4 @@ class OrderingConverter } // namespace cadet -#endif // CADET_ORDERINGCONVERTER_HPP_ +#endif // CADET_ORDERINGCONVERTER_HPP_ diff --git a/include/common/ParameterProviderImpl.hpp b/include/common/ParameterProviderImpl.hpp index 5207e5ef3..17cb2de6f 100644 --- a/include/common/ParameterProviderImpl.hpp +++ b/include/common/ParameterProviderImpl.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides an implementation of the cadet::IParameterProvider interface */ @@ -25,12 +25,12 @@ namespace cadet { -template -class ParameterProviderImpl : public cadet::IParameterProvider +template class ParameterProviderImpl : public cadet::IParameterProvider { public: - - ParameterProviderImpl(Reader_t& reader) : ParameterProviderImpl(reader, true) { } + ParameterProviderImpl(Reader_t& reader) : ParameterProviderImpl(reader, true) + { + } ParameterProviderImpl(Reader_t& reader, bool inputPrefix) : _reader(reader) { @@ -38,7 +38,9 @@ class ParameterProviderImpl : public cadet::IParameterProvider _reader.setGroup("input"); } - virtual ~ParameterProviderImpl() CADET_NOEXCEPT { } + virtual ~ParameterProviderImpl() CADET_NOEXCEPT + { + } virtual double getDouble(const std::string& paramName) { @@ -146,10 +148,11 @@ class ParameterProviderImpl : public cadet::IParameterProvider LOG(Debug) << "SCOPE POP"; _reader.popGroup(); } + private: Reader_t& _reader; }; } // namespace cadet -#endif // CADET_PARAMPROVIDER_HPP_ +#endif // CADET_PARAMPROVIDER_HPP_ diff --git a/include/common/SolutionRecorderImpl.hpp b/include/common/SolutionRecorderImpl.hpp index b9a64a8c9..21f4f3db0 100644 --- a/include/common/SolutionRecorderImpl.hpp +++ b/include/common/SolutionRecorderImpl.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides several implementations of ISolutionRecorder. */ @@ -30,7 +30,7 @@ namespace cadet namespace detail { - CADET_CONST_OR_CONSTEXPR unsigned int numDefaultRecorderTimesteps = 100u; +CADET_CONST_OR_CONSTEXPR unsigned int numDefaultRecorderTimesteps = 100u; } /** @@ -41,7 +41,6 @@ namespace detail class InternalStorageUnitOpRecorder : public ISolutionRecorder { public: - struct StorageConfig { bool storeBulk; @@ -53,13 +52,19 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder bool storeVolume; }; - InternalStorageUnitOpRecorder() : InternalStorageUnitOpRecorder(UnitOpIndep) { } + InternalStorageUnitOpRecorder() : InternalStorageUnitOpRecorder(UnitOpIndep) + { + } - InternalStorageUnitOpRecorder(UnitOpIdx idx) : _cfgSolution({false, false, false, true, false, false, false}), - _cfgSolutionDot({false, false, false, false, false, false, false}), _cfgSensitivity({false, false, false, true, false, false, false}), - _cfgSensitivityDot({false, false, false, true, false, false, false}), _storeTime(false), _storeCoordinates(false), _splitComponents(true), _splitPorts(true), - _singleAsMultiPortUnitOps(false), _keepBulkSingletonDim(true), _keepParticleSingletonDim(true), _curCfg(nullptr), _nComp(0), _nVolumeDof(0), _nAxialCells(0), _nRadialCells(0), - _nInletPorts(0), _nOutletPorts(0), _numTimesteps(0), _numSens(0), _unitOp(idx), _needsReAlloc(false), _axialCoords(0), _radialCoords(0), _particleCoords(0) + InternalStorageUnitOpRecorder(UnitOpIdx idx) + : _cfgSolution({false, false, false, true, false, false, false}), + _cfgSolutionDot({false, false, false, false, false, false, false}), + _cfgSensitivity({false, false, false, true, false, false, false}), + _cfgSensitivityDot({false, false, false, true, false, false, false}), _storeTime(false), + _storeCoordinates(false), _splitComponents(true), _splitPorts(true), _singleAsMultiPortUnitOps(false), + _keepBulkSingletonDim(true), _keepParticleSingletonDim(true), _curCfg(nullptr), _nComp(0), _nVolumeDof(0), + _nAxialCells(0), _nRadialCells(0), _nInletPorts(0), _nOutletPorts(0), _numTimesteps(0), _numSens(0), + _unitOp(idx), _needsReAlloc(false), _axialCoords(0), _radialCoords(0), _particleCoords(0) { } @@ -102,7 +107,7 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder clear(); _numTimesteps = numTimesteps; - + if (numSens != _numSens) { // Allocate sensitivity storage @@ -175,11 +180,11 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder // Allocate space for solution beginSolution(); - allocateMemory(exporter); + allocateMemory(exporter); endSolution(); beginSolutionDerivative(); - allocateMemory(exporter); + allocateMemory(exporter); endSolution(); // Allocate space for sensitivities @@ -213,7 +218,8 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder _time.push_back(t); } - virtual void beginUnitOperation(cadet::UnitOpIdx idx, const cadet::IModel& model, const cadet::ISolutionExporter& exporter) + virtual void beginUnitOperation(cadet::UnitOpIdx idx, const cadet::IModel& model, + const cadet::ISolutionExporter& exporter) { // Only record one unit operation if ((idx != _unitOp) || !_curCfg) @@ -289,9 +295,13 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder } } - virtual void endUnitOperation() { } + virtual void endUnitOperation() + { + } - virtual void endTimestep() { } + virtual void endTimestep() + { + } virtual void beginSolution() { @@ -311,7 +321,10 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder _curStorage = &_dataDot; } - virtual void endSolutionDerivative() { endSolution(); } + virtual void endSolutionDerivative() + { + endSolution(); + } virtual void beginSensitivity(const cadet::ParameterId& pId, unsigned int sensIdx) { @@ -322,7 +335,7 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder { endSolution(); } - + virtual void beginSensitivityDerivative(const cadet::ParameterId& pId, unsigned int sensIdx) { beginSensitivityDot(sensIdx); @@ -333,8 +346,7 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder endSolution(); } - template - void writeCoordinates(Writer_t& writer) + template void writeCoordinates(Writer_t& writer) { if (!_storeCoordinates) return; @@ -363,8 +375,7 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder } } - template - void writeSolution(Writer_t& writer) + template void writeSolution(Writer_t& writer) { std::ostringstream oss; @@ -380,8 +391,7 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder endSolution(); } - template - void writeSensitivity(Writer_t& writer) + template void writeSensitivity(Writer_t& writer) { std::ostringstream oss; @@ -403,8 +413,7 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder } } - template - void writeSensitivity(Writer_t& writer, unsigned int param) + template void writeSensitivity(Writer_t& writer, unsigned int param) { std::ostringstream oss; @@ -417,82 +426,265 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder endSolution(); } - inline StorageConfig& solutionConfig() CADET_NOEXCEPT { return _cfgSolution; } - inline const StorageConfig& solutionConfig() const CADET_NOEXCEPT { return _cfgSolution; } - inline void solutionConfig(const StorageConfig& cfg) CADET_NOEXCEPT { _cfgSolution = cfg; } - - inline StorageConfig& solutionDotConfig() CADET_NOEXCEPT { return _cfgSolutionDot; } - inline const StorageConfig& solutionDotConfig() const CADET_NOEXCEPT { return _cfgSolutionDot; } - inline void solutionDotConfig(const StorageConfig& cfg) CADET_NOEXCEPT { _cfgSolutionDot = cfg; } - - inline StorageConfig& sensitivityConfig() CADET_NOEXCEPT { return _cfgSensitivity; } - inline const StorageConfig& sensitivityConfig() const CADET_NOEXCEPT { return _cfgSensitivity; } - inline void sensitivityConfig(const StorageConfig& cfg) CADET_NOEXCEPT { _cfgSensitivity = cfg; } - - inline StorageConfig& sensitivityDotConfig() CADET_NOEXCEPT { return _cfgSensitivityDot; } - inline const StorageConfig& sensitivityDotConfig() const CADET_NOEXCEPT { return _cfgSensitivityDot; } - inline void sensitivityDotConfig(const StorageConfig& cfg) CADET_NOEXCEPT { _cfgSensitivityDot = cfg; } - - inline bool storeTime() const CADET_NOEXCEPT { return _storeTime; } - inline void storeTime(bool st) CADET_NOEXCEPT { _storeTime = st; } - - inline bool storeCoordinates() const CADET_NOEXCEPT { return _storeCoordinates; } - inline void storeCoordinates(bool sc) CADET_NOEXCEPT { _storeCoordinates = sc; } - - inline bool splitComponents() const CADET_NOEXCEPT { return _splitComponents; } - inline void splitComponents(bool st) CADET_NOEXCEPT { _splitComponents = st; } - - inline bool splitPorts() const CADET_NOEXCEPT { return _splitPorts; } - inline void splitPorts(bool st) CADET_NOEXCEPT { _splitPorts = st; } - - inline bool treatSingleAsMultiPortUnitOps() const CADET_NOEXCEPT { return _singleAsMultiPortUnitOps; } - inline void treatSingleAsMultiPortUnitOps(bool smp) CADET_NOEXCEPT { _singleAsMultiPortUnitOps = smp; } - - inline bool keepBulkSingletonDim() const CADET_NOEXCEPT { return _keepBulkSingletonDim; } - inline void keepBulkSingletonDim(bool keepSingleton) CADET_NOEXCEPT { _keepBulkSingletonDim = keepSingleton; } - - inline bool keepParticleSingletonDim() const CADET_NOEXCEPT { return _keepParticleSingletonDim; } - inline void keepParticleSingletonDim(bool keepSingleton) CADET_NOEXCEPT { _keepParticleSingletonDim = keepSingleton; } - - inline UnitOpIdx unitOperation() const CADET_NOEXCEPT { return _unitOp; } - inline void unitOperation(UnitOpIdx idx) CADET_NOEXCEPT { _unitOp = idx; } - - inline unsigned int numDataPoints() const CADET_NOEXCEPT { return _numTimesteps; } - inline unsigned int numComponents() const CADET_NOEXCEPT { return _nComp; } - inline unsigned int numInletPorts() const CADET_NOEXCEPT { return _nInletPorts; } - inline unsigned int numOutletPorts() const CADET_NOEXCEPT { return _nOutletPorts; } - - inline double const* time() const CADET_NOEXCEPT { return _time.data(); } - inline double const* inlet() const CADET_NOEXCEPT { return _data.inlet.data(); } - inline double const* outlet() const CADET_NOEXCEPT { return _data.outlet.data(); } - inline double const* bulk() const CADET_NOEXCEPT { return _data.bulk.data(); } - inline double const* particle(unsigned int parType = 0) const CADET_NOEXCEPT { return _data.particle[parType].data(); } - inline double const* solid(unsigned int parType = 0) const CADET_NOEXCEPT { return _data.solid[parType].data(); } - inline double const* flux() const CADET_NOEXCEPT { return _data.flux.data(); } - inline double const* volume() const CADET_NOEXCEPT { return _data.volume.data(); } - inline double const* inletDot() const CADET_NOEXCEPT { return _dataDot.inlet.data(); } - inline double const* outletDot() const CADET_NOEXCEPT { return _dataDot.outlet.data(); } - inline double const* bulkDot() const CADET_NOEXCEPT { return _dataDot.bulk.data(); } - inline double const* particleDot(unsigned int parType = 0) const CADET_NOEXCEPT { return _dataDot.particle[parType].data(); } - inline double const* solidDot(unsigned int parType = 0) const CADET_NOEXCEPT { return _dataDot.solid[parType].data(); } - inline double const* fluxDot() const CADET_NOEXCEPT { return _dataDot.flux.data(); } - inline double const* volumeDot() const CADET_NOEXCEPT { return _dataDot.volume.data(); } - inline double const* sensInlet(unsigned int idx) const CADET_NOEXCEPT { return _sens[idx].inlet.data(); } - inline double const* sensOutlet(unsigned int idx) const CADET_NOEXCEPT { return _sens[idx].outlet.data(); } - inline double const* sensBulk(unsigned int idx) const CADET_NOEXCEPT { return _sens[idx].bulk.data(); } - inline double const* sensParticle(unsigned int idx, unsigned int parType = 0) const CADET_NOEXCEPT { return _sens[idx].particle[parType].data(); } - inline double const* sensSolid(unsigned int idx, unsigned int parType = 0) const CADET_NOEXCEPT { return _sens[idx].solid[parType].data(); } - inline double const* sensFlux(unsigned int idx) const CADET_NOEXCEPT { return _sens[idx].flux.data(); } - inline double const* sensVolume(unsigned int idx) const CADET_NOEXCEPT { return _sens[idx].volume.data(); } - inline double const* sensInletDot(unsigned int idx) const CADET_NOEXCEPT { return _sensDot[idx].inlet.data(); } - inline double const* sensOutletDot(unsigned int idx) const CADET_NOEXCEPT { return _sensDot[idx].outlet.data(); } - inline double const* sensBulkDot(unsigned int idx) const CADET_NOEXCEPT { return _sensDot[idx].bulk.data(); } - inline double const* sensParticleDot(unsigned int idx, unsigned int parType = 0) const CADET_NOEXCEPT { return _sensDot[idx].particle[parType].data(); } - inline double const* sensSolidDot(unsigned int idx, unsigned int parType = 0) const CADET_NOEXCEPT { return _sensDot[idx].solid[parType].data(); } - inline double const* sensFluxDot(unsigned int idx) const CADET_NOEXCEPT { return _sensDot[idx].flux.data(); } - inline double const* sensVolumeDot(unsigned int idx) const CADET_NOEXCEPT { return _sensDot[idx].volume.data(); } -protected: + inline StorageConfig& solutionConfig() CADET_NOEXCEPT + { + return _cfgSolution; + } + inline const StorageConfig& solutionConfig() const CADET_NOEXCEPT + { + return _cfgSolution; + } + inline void solutionConfig(const StorageConfig& cfg) CADET_NOEXCEPT + { + _cfgSolution = cfg; + } + inline StorageConfig& solutionDotConfig() CADET_NOEXCEPT + { + return _cfgSolutionDot; + } + inline const StorageConfig& solutionDotConfig() const CADET_NOEXCEPT + { + return _cfgSolutionDot; + } + inline void solutionDotConfig(const StorageConfig& cfg) CADET_NOEXCEPT + { + _cfgSolutionDot = cfg; + } + + inline StorageConfig& sensitivityConfig() CADET_NOEXCEPT + { + return _cfgSensitivity; + } + inline const StorageConfig& sensitivityConfig() const CADET_NOEXCEPT + { + return _cfgSensitivity; + } + inline void sensitivityConfig(const StorageConfig& cfg) CADET_NOEXCEPT + { + _cfgSensitivity = cfg; + } + + inline StorageConfig& sensitivityDotConfig() CADET_NOEXCEPT + { + return _cfgSensitivityDot; + } + inline const StorageConfig& sensitivityDotConfig() const CADET_NOEXCEPT + { + return _cfgSensitivityDot; + } + inline void sensitivityDotConfig(const StorageConfig& cfg) CADET_NOEXCEPT + { + _cfgSensitivityDot = cfg; + } + + inline bool storeTime() const CADET_NOEXCEPT + { + return _storeTime; + } + inline void storeTime(bool st) CADET_NOEXCEPT + { + _storeTime = st; + } + + inline bool storeCoordinates() const CADET_NOEXCEPT + { + return _storeCoordinates; + } + inline void storeCoordinates(bool sc) CADET_NOEXCEPT + { + _storeCoordinates = sc; + } + + inline bool splitComponents() const CADET_NOEXCEPT + { + return _splitComponents; + } + inline void splitComponents(bool st) CADET_NOEXCEPT + { + _splitComponents = st; + } + + inline bool splitPorts() const CADET_NOEXCEPT + { + return _splitPorts; + } + inline void splitPorts(bool st) CADET_NOEXCEPT + { + _splitPorts = st; + } + + inline bool treatSingleAsMultiPortUnitOps() const CADET_NOEXCEPT + { + return _singleAsMultiPortUnitOps; + } + inline void treatSingleAsMultiPortUnitOps(bool smp) CADET_NOEXCEPT + { + _singleAsMultiPortUnitOps = smp; + } + + inline bool keepBulkSingletonDim() const CADET_NOEXCEPT + { + return _keepBulkSingletonDim; + } + inline void keepBulkSingletonDim(bool keepSingleton) CADET_NOEXCEPT + { + _keepBulkSingletonDim = keepSingleton; + } + + inline bool keepParticleSingletonDim() const CADET_NOEXCEPT + { + return _keepParticleSingletonDim; + } + inline void keepParticleSingletonDim(bool keepSingleton) CADET_NOEXCEPT + { + _keepParticleSingletonDim = keepSingleton; + } + + inline UnitOpIdx unitOperation() const CADET_NOEXCEPT + { + return _unitOp; + } + inline void unitOperation(UnitOpIdx idx) CADET_NOEXCEPT + { + _unitOp = idx; + } + + inline unsigned int numDataPoints() const CADET_NOEXCEPT + { + return _numTimesteps; + } + inline unsigned int numComponents() const CADET_NOEXCEPT + { + return _nComp; + } + inline unsigned int numInletPorts() const CADET_NOEXCEPT + { + return _nInletPorts; + } + inline unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return _nOutletPorts; + } + + inline double const* time() const CADET_NOEXCEPT + { + return _time.data(); + } + inline double const* inlet() const CADET_NOEXCEPT + { + return _data.inlet.data(); + } + inline double const* outlet() const CADET_NOEXCEPT + { + return _data.outlet.data(); + } + inline double const* bulk() const CADET_NOEXCEPT + { + return _data.bulk.data(); + } + inline double const* particle(unsigned int parType = 0) const CADET_NOEXCEPT + { + return _data.particle[parType].data(); + } + inline double const* solid(unsigned int parType = 0) const CADET_NOEXCEPT + { + return _data.solid[parType].data(); + } + inline double const* flux() const CADET_NOEXCEPT + { + return _data.flux.data(); + } + inline double const* volume() const CADET_NOEXCEPT + { + return _data.volume.data(); + } + inline double const* inletDot() const CADET_NOEXCEPT + { + return _dataDot.inlet.data(); + } + inline double const* outletDot() const CADET_NOEXCEPT + { + return _dataDot.outlet.data(); + } + inline double const* bulkDot() const CADET_NOEXCEPT + { + return _dataDot.bulk.data(); + } + inline double const* particleDot(unsigned int parType = 0) const CADET_NOEXCEPT + { + return _dataDot.particle[parType].data(); + } + inline double const* solidDot(unsigned int parType = 0) const CADET_NOEXCEPT + { + return _dataDot.solid[parType].data(); + } + inline double const* fluxDot() const CADET_NOEXCEPT + { + return _dataDot.flux.data(); + } + inline double const* volumeDot() const CADET_NOEXCEPT + { + return _dataDot.volume.data(); + } + inline double const* sensInlet(unsigned int idx) const CADET_NOEXCEPT + { + return _sens[idx].inlet.data(); + } + inline double const* sensOutlet(unsigned int idx) const CADET_NOEXCEPT + { + return _sens[idx].outlet.data(); + } + inline double const* sensBulk(unsigned int idx) const CADET_NOEXCEPT + { + return _sens[idx].bulk.data(); + } + inline double const* sensParticle(unsigned int idx, unsigned int parType = 0) const CADET_NOEXCEPT + { + return _sens[idx].particle[parType].data(); + } + inline double const* sensSolid(unsigned int idx, unsigned int parType = 0) const CADET_NOEXCEPT + { + return _sens[idx].solid[parType].data(); + } + inline double const* sensFlux(unsigned int idx) const CADET_NOEXCEPT + { + return _sens[idx].flux.data(); + } + inline double const* sensVolume(unsigned int idx) const CADET_NOEXCEPT + { + return _sens[idx].volume.data(); + } + inline double const* sensInletDot(unsigned int idx) const CADET_NOEXCEPT + { + return _sensDot[idx].inlet.data(); + } + inline double const* sensOutletDot(unsigned int idx) const CADET_NOEXCEPT + { + return _sensDot[idx].outlet.data(); + } + inline double const* sensBulkDot(unsigned int idx) const CADET_NOEXCEPT + { + return _sensDot[idx].bulk.data(); + } + inline double const* sensParticleDot(unsigned int idx, unsigned int parType = 0) const CADET_NOEXCEPT + { + return _sensDot[idx].particle[parType].data(); + } + inline double const* sensSolidDot(unsigned int idx, unsigned int parType = 0) const CADET_NOEXCEPT + { + return _sensDot[idx].solid[parType].data(); + } + inline double const* sensFluxDot(unsigned int idx) const CADET_NOEXCEPT + { + return _sensDot[idx].flux.data(); + } + inline double const* sensVolumeDot(unsigned int idx) const CADET_NOEXCEPT + { + return _sensDot[idx].volume.data(); + } + +protected: struct Storage { std::vector outlet; @@ -562,8 +754,7 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder _curStorage->volume.reserve(nAllocTimesteps * exporter.numVolumeDofs()); } - template - void writeData(Writer_t& writer, const char* prefix, std::ostringstream& oss) + template void writeData(Writer_t& writer, const char* prefix, std::ostringstream& oss) { if (_curCfg->storeOutlet) { @@ -578,15 +769,19 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder oss.str(""); if ((_nOutletPorts == 1) && !_singleAsMultiPortUnitOps) { - oss << prefix << "_OUTLET_COMP_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << comp; + oss << prefix << "_OUTLET_COMP_" << std::setfill('0') << std::setw(3) + << std::setprecision(0) << comp; } else { - oss << prefix << "_OUTLET_PORT_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << port - << "_COMP_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << comp; + oss << prefix << "_OUTLET_PORT_" << std::setfill('0') << std::setw(3) + << std::setprecision(0) << port << "_COMP_" << std::setfill('0') << std::setw(3) + << std::setprecision(0) << comp; } - writer.template vector(oss.str(), _numTimesteps, _curStorage->outlet.data() + comp + port * _nComp, _nComp * _nOutletPorts); + writer.template vector(oss.str(), _numTimesteps, + _curStorage->outlet.data() + comp + port * _nComp, + _nComp * _nOutletPorts); } } } @@ -598,9 +793,12 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder if ((_nOutletPorts == 1) && !_singleAsMultiPortUnitOps) oss << prefix << "_OUTLET"; else - oss << prefix << "_OUTLET_PORT_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << port; + oss << prefix << "_OUTLET_PORT_" << std::setfill('0') << std::setw(3) + << std::setprecision(0) << port; - writer.template matrix(oss.str(), _numTimesteps, _nComp, _curStorage->outlet.data() + port * _nComp, _nOutletPorts * _nComp, _nComp); + writer.template matrix(oss.str(), _numTimesteps, _nComp, + _curStorage->outlet.data() + port * _nComp, + _nOutletPorts * _nComp, _nComp); } } } @@ -611,11 +809,14 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder for (unsigned int comp = 0; comp < _nComp; ++comp) { oss.str(""); - oss << prefix << "_OUTLET_COMP_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << comp; + oss << prefix << "_OUTLET_COMP_" << std::setfill('0') << std::setw(3) << std::setprecision(0) + << comp; if ((_nOutletPorts == 1) && !_singleAsMultiPortUnitOps) - writer.template vector(oss.str(), _numTimesteps, _curStorage->outlet.data() + comp, _nComp); + writer.template vector(oss.str(), _numTimesteps, _curStorage->outlet.data() + comp, + _nComp); else - writer.template matrix(oss.str(), _numTimesteps, _nOutletPorts, _curStorage->outlet.data() + comp, _nComp); + writer.template matrix(oss.str(), _numTimesteps, _nOutletPorts, + _curStorage->outlet.data() + comp, _nComp); } } else @@ -626,13 +827,15 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder { const std::vector layout = {_numTimesteps, _nComp}; debugCheckTensorLayout(layout, _curStorage->outlet.size()); - writer.template tensor(oss.str(), layout.size(), layout.data(), _curStorage->outlet.data()); + writer.template tensor(oss.str(), layout.size(), layout.data(), + _curStorage->outlet.data()); } else { const std::vector layout = {_numTimesteps, _nOutletPorts, _nComp}; debugCheckTensorLayout(layout, _curStorage->outlet.size()); - writer.template tensor(oss.str(), layout.size(), layout.data(), _curStorage->outlet.data()); + writer.template tensor(oss.str(), layout.size(), layout.data(), + _curStorage->outlet.data()); } } } @@ -651,15 +854,19 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder oss.str(""); if ((_nInletPorts == 1) && !_singleAsMultiPortUnitOps) { - oss << prefix << "_INLET_COMP_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << comp; + oss << prefix << "_INLET_COMP_" << std::setfill('0') << std::setw(3) + << std::setprecision(0) << comp; } else { - oss << prefix << "_INLET_PORT_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << port - << "_COMP_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << comp; + oss << prefix << "_INLET_PORT_" << std::setfill('0') << std::setw(3) + << std::setprecision(0) << port << "_COMP_" << std::setfill('0') << std::setw(3) + << std::setprecision(0) << comp; } - writer.template vector(oss.str(), _numTimesteps, _curStorage->inlet.data() + comp + port * _nComp, _nComp * _nInletPorts); + writer.template vector(oss.str(), _numTimesteps, + _curStorage->inlet.data() + comp + port * _nComp, + _nComp * _nInletPorts); } } } @@ -671,9 +878,12 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder if ((_nInletPorts == 1) && !_singleAsMultiPortUnitOps) oss << prefix << "_INLET"; else - oss << prefix << "_INLET_PORT_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << port; + oss << prefix << "_INLET_PORT_" << std::setfill('0') << std::setw(3) << std::setprecision(0) + << port; - writer.template matrix(oss.str(), _numTimesteps, _nComp, _curStorage->inlet.data() + port * _nComp, _nInletPorts * _nComp, _nComp); + writer.template matrix(oss.str(), _numTimesteps, _nComp, + _curStorage->inlet.data() + port * _nComp, _nInletPorts * _nComp, + _nComp); } } } @@ -684,11 +894,14 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder for (unsigned int comp = 0; comp < _nComp; ++comp) { oss.str(""); - oss << prefix << "_INLET_COMP_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << comp; + oss << prefix << "_INLET_COMP_" << std::setfill('0') << std::setw(3) << std::setprecision(0) + << comp; if ((_nInletPorts == 1) && !_singleAsMultiPortUnitOps) - writer.template vector(oss.str(), _numTimesteps, _curStorage->inlet.data() + comp, _nComp); + writer.template vector(oss.str(), _numTimesteps, _curStorage->inlet.data() + comp, + _nComp); else - writer.template matrix(oss.str(), _numTimesteps, _nInletPorts, _curStorage->inlet.data() + comp, _nComp); + writer.template matrix(oss.str(), _numTimesteps, _nInletPorts, + _curStorage->inlet.data() + comp, _nComp); } } else @@ -699,13 +912,15 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder { const std::vector layout = {_numTimesteps, _nComp}; debugCheckTensorLayout(layout, _curStorage->inlet.size()); - writer.template tensor(oss.str(), layout.size(), layout.data(), _curStorage->inlet.data()); + writer.template tensor(oss.str(), layout.size(), layout.data(), + _curStorage->inlet.data()); } else { const std::vector layout = {_numTimesteps, _nInletPorts, _nComp}; debugCheckTensorLayout(layout, _curStorage->inlet.size()); - writer.template tensor(oss.str(), layout.size(), layout.data(), _curStorage->inlet.data()); + writer.template tensor(oss.str(), layout.size(), layout.data(), + _curStorage->inlet.data()); } } } @@ -757,11 +972,13 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder oss.str(""); oss << prefix << "_PARTICLE"; - writer.template tensor(oss.str(), layout.size(), layout.data(), _curStorage->particle[0].data()); + writer.template tensor(oss.str(), layout.size(), layout.data(), + _curStorage->particle[0].data()); } else { - const bool hasParticleShells = std::any_of(_nParShells.begin(), _nParShells.end(), [](unsigned int x) { return x >= 1; }); + const bool hasParticleShells = + std::any_of(_nParShells.begin(), _nParShells.end(), [](unsigned int x) { return x >= 1; }); if (hasParticleShells) layout.push_back(0); layout.push_back(_nComp); @@ -785,8 +1002,10 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder debugCheckTensorLayout(layout, layoutSize, _curStorage->particle[parType].size()); oss.str(""); - oss << prefix << "_PARTICLE_PARTYPE_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << parType; - writer.template tensor(oss.str(), layoutSize, layout.data(), _curStorage->particle[parType].data()); + oss << prefix << "_PARTICLE_PARTYPE_" << std::setfill('0') << std::setw(3) << std::setprecision(0) + << parType; + writer.template tensor(oss.str(), layoutSize, layout.data(), + _curStorage->particle[parType].data()); } } } @@ -821,7 +1040,8 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder } else { - const bool hasParticleShells = std::any_of(_nParShells.begin(), _nParShells.end(), [](unsigned int x) { return x >= 1; }); + const bool hasParticleShells = + std::any_of(_nParShells.begin(), _nParShells.end(), [](unsigned int x) { return x >= 1; }); if (hasParticleShells) layout.push_back(0); layout.push_back(0); @@ -844,8 +1064,10 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder debugCheckTensorLayout(layout, layoutSize, _curStorage->solid[parType].size()); oss.str(""); - oss << prefix << "_SOLID_PARTYPE_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << parType; - writer.template tensor(oss.str(), layoutSize, layout.data(), _curStorage->solid[parType].data()); + oss << prefix << "_SOLID_PARTYPE_" << std::setfill('0') << std::setw(3) << std::setprecision(0) + << parType; + writer.template tensor(oss.str(), layoutSize, layout.data(), + _curStorage->solid[parType].data()); } } } @@ -901,13 +1123,12 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder std::size_t layoutElems = 1; for (std::size_t i = 0; i < layoutSize; ++i) layoutElems *= layout[i]; - + cadet_assert(numElems == layoutElems); #endif } - template - void debugCheckTensorLayout(const std::vector& layout, std::size_t numElems) + template void debugCheckTensorLayout(const std::vector& layout, std::size_t numElems) { #ifdef CADET_DEBUG debugCheckTensorLayout(layout, layout.size(), numElems); @@ -954,7 +1175,6 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder std::vector _particleCoords; }; - /** * @brief Stores pieces of the solution of the whole model system in recorders of single unit operations * @details Maintains a collection of InternalStorageUnitOpRecorder objects that store individual unit operations. @@ -964,7 +1184,6 @@ class InternalStorageUnitOpRecorder : public ISolutionRecorder class InternalStorageSystemRecorder : public ISolutionRecorder { public: - InternalStorageSystemRecorder() : _numTimesteps(0), _numSens(0), _storeTime(true) { } @@ -1027,7 +1246,8 @@ class InternalStorageSystemRecorder : public ISolutionRecorder rec->beginTimestep(t); } - virtual void beginUnitOperation(cadet::UnitOpIdx idx, const cadet::IModel& model, const cadet::ISolutionExporter& exporter) + virtual void beginUnitOperation(cadet::UnitOpIdx idx, const cadet::IModel& model, + const cadet::ISolutionExporter& exporter) { for (InternalStorageUnitOpRecorder* rec : _recorders) rec->beginUnitOperation(idx, model, exporter); @@ -1093,15 +1313,15 @@ class InternalStorageSystemRecorder : public ISolutionRecorder rec->endSensitivityDerivative(pId, sensIdx); } - template - void writeCoordinates(Writer_t& writer) + template void writeCoordinates(Writer_t& writer) { std::ostringstream oss; for (InternalStorageUnitOpRecorder* rec : _recorders) { oss.str(""); - oss << "unit_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << static_cast(rec->unitOperation()); + oss << "unit_" << std::setfill('0') << std::setw(3) << std::setprecision(0) + << static_cast(rec->unitOperation()); if (rec->storeCoordinates()) { @@ -1111,9 +1331,8 @@ class InternalStorageSystemRecorder : public ISolutionRecorder } } } - - template - void writeSolution(Writer_t& writer) + + template void writeSolution(Writer_t& writer) { std::ostringstream oss; @@ -1123,7 +1342,8 @@ class InternalStorageSystemRecorder : public ISolutionRecorder for (InternalStorageUnitOpRecorder* rec : _recorders) { oss.str(""); - oss << "unit_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << static_cast(rec->unitOperation()); + oss << "unit_" << std::setfill('0') << std::setw(3) << std::setprecision(0) + << static_cast(rec->unitOperation()); writer.pushGroup(oss.str()); rec->writeSolution(writer); @@ -1131,8 +1351,7 @@ class InternalStorageSystemRecorder : public ISolutionRecorder } } - template - void writeSensitivity(Writer_t& writer) + template void writeSensitivity(Writer_t& writer) { std::ostringstream oss; @@ -1145,7 +1364,8 @@ class InternalStorageSystemRecorder : public ISolutionRecorder for (InternalStorageUnitOpRecorder* rec : _recorders) { oss.str(""); - oss << "unit_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << static_cast(rec->unitOperation()); + oss << "unit_" << std::setfill('0') << std::setw(3) << std::setprecision(0) + << static_cast(rec->unitOperation()); writer.pushGroup(oss.str()); rec->writeSensitivity(writer, param); @@ -1156,8 +1376,14 @@ class InternalStorageSystemRecorder : public ISolutionRecorder } } - inline bool storeTime() const CADET_NOEXCEPT { return _storeTime; } - inline void storeTime(bool st) CADET_NOEXCEPT { _storeTime = st; } + inline bool storeTime() const CADET_NOEXCEPT + { + return _storeTime; + } + inline void storeTime(bool st) CADET_NOEXCEPT + { + _storeTime = st; + } inline bool anyUnitStoresCoordinates() const CADET_NOEXCEPT { @@ -1170,16 +1396,28 @@ class InternalStorageSystemRecorder : public ISolutionRecorder return false; } - inline unsigned int numDataPoints() const CADET_NOEXCEPT { return _numTimesteps; } + inline unsigned int numDataPoints() const CADET_NOEXCEPT + { + return _numTimesteps; + } inline void addRecorder(InternalStorageUnitOpRecorder* rec) { _recorders.push_back(rec); } - inline unsigned int numRecorders() const CADET_NOEXCEPT { return _recorders.size(); } - inline InternalStorageUnitOpRecorder* recorder(unsigned int idx) CADET_NOEXCEPT { return _recorders[idx]; } - inline InternalStorageUnitOpRecorder* recorder(unsigned int idx) const CADET_NOEXCEPT { return _recorders[idx]; } + inline unsigned int numRecorders() const CADET_NOEXCEPT + { + return _recorders.size(); + } + inline InternalStorageUnitOpRecorder* recorder(unsigned int idx) CADET_NOEXCEPT + { + return _recorders[idx]; + } + inline InternalStorageUnitOpRecorder* recorder(unsigned int idx) const CADET_NOEXCEPT + { + return _recorders[idx]; + } inline InternalStorageUnitOpRecorder* unitOperation(UnitOpIdx idx) CADET_NOEXCEPT { @@ -1207,10 +1445,12 @@ class InternalStorageSystemRecorder : public ISolutionRecorder _recorders.clear(); } - inline double const* time() const CADET_NOEXCEPT { return _time.data(); } + inline double const* time() const CADET_NOEXCEPT + { + return _time.data(); + } protected: - std::vector _recorders; unsigned int _numTimesteps; unsigned int _numSens; @@ -1218,7 +1458,6 @@ class InternalStorageSystemRecorder : public ISolutionRecorder bool _storeTime; }; - } // namespace cadet -#endif // LIBCADET_SOLUTIONRECORDER_IMPL_HPP_ +#endif // LIBCADET_SOLUTIONRECORDER_IMPL_HPP_ diff --git a/include/common/TclapUtils.hpp b/include/common/TclapUtils.hpp index fd8496ae2..18d3127c8 100644 --- a/include/common/TclapUtils.hpp +++ b/include/common/TclapUtils.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -18,59 +18,61 @@ #include "cadet/LibVersionInfo.hpp" -namespace TCLAP +namespace TCLAP { - /** - * @brief Modifies the standard behavior of TCLAP to output a better version notice - * @details The version notice includes the version of CADET (tag, branch, and commit hash) - * as well hints to CADET-Web and the GitHub project. - */ - class CustomOutput : public StdOutput +/** + * @brief Modifies the standard behavior of TCLAP to output a better version notice + * @details The version notice includes the version of CADET (tag, branch, and commit hash) + * as well hints to CADET-Web and the GitHub project. + */ +class CustomOutput : public StdOutput +{ +public: + CustomOutput(const std::string& progName) : _progName(progName) { - public: - - CustomOutput(const std::string& progName) : _progName(progName) { } + } - virtual void version(CmdLineInterface& c) - { - std::cout << "This is " << _progName << " version " << cadet::getLibraryVersion() << " (" << cadet::getLibraryBranchRefspec() << " branch)\n"; - std::cout << "Built from commit " << cadet::getLibraryCommitHash() << "\n"; - std::cout << "Build variant " << cadet::getLibraryBuildType() << "\n"; - std::cout << "CADET homepage: \n"; - std::cout << "Fork CADET on GitHub: \n"; - std::cout << "Report bugs to the issue tracker on GitHub or \n"; - std::cout << "See the accompanying LICENSE.txt, CONTRIBUTORS.md files" << std::endl; - } - - protected: - std::string _progName; - }; + virtual void version(CmdLineInterface& c) + { + std::cout << "This is " << _progName << " version " << cadet::getLibraryVersion() << " (" + << cadet::getLibraryBranchRefspec() << " branch)\n"; + std::cout << "Built from commit " << cadet::getLibraryCommitHash() << "\n"; + std::cout << "Build variant " << cadet::getLibraryBuildType() << "\n"; + std::cout << "CADET homepage: \n"; + std::cout << "Fork CADET on GitHub: \n"; + std::cout << "Report bugs to the issue tracker on GitHub or \n"; + std::cout << "See the accompanying LICENSE.txt, CONTRIBUTORS.md files" << std::endl; + } +protected: + std::string _progName; +}; - /** - * @brief Modifies the standard behavior of TCLAP to output a better version notice (without library version) - * @details The version notice includes hints to CADET-Web and the GitHub project. - */ - class CustomOutputWithoutVersion : public StdOutput +/** + * @brief Modifies the standard behavior of TCLAP to output a better version notice (without library version) + * @details The version notice includes hints to CADET-Web and the GitHub project. + */ +class CustomOutputWithoutVersion : public StdOutput +{ +public: + CustomOutputWithoutVersion(const std::string& progName) : _progName(progName) { - public: - - CustomOutputWithoutVersion(const std::string& progName) : _progName(progName) { } + } - virtual void version(CmdLineInterface& c) - { - std::cout << "This is " << _progName << "\n"; - std::cout << "CADET homepage: \n"; - std::cout << "Fork CADET on GitHub: \n"; - std::cout << "Report bugs to the issue tracker on GitHub or \n"; - std::cout << "See the accompanying LICENSE.txt, CONTRIBUTORS.md files" << std::endl; - } + virtual void version(CmdLineInterface& c) + { + std::cout << "This is " << _progName << "\n"; + std::cout << "CADET homepage: \n"; + std::cout << "Fork CADET on GitHub: \n"; + std::cout << "Report bugs to the issue tracker on GitHub or \n"; + std::cout << "See the accompanying LICENSE.txt, CONTRIBUTORS.md files" << std::endl; + } - protected: - std::string _progName; - }; +protected: + std::string _progName; +}; -} +} // namespace TCLAP -#endif // LIBCADET_TCLAPUTILS_HPP_ +#endif // LIBCADET_TCLAPUTILS_HPP_ diff --git a/include/common/Timer.hpp b/include/common/Timer.hpp index b279417c1..54298effa 100644 --- a/include/common/Timer.hpp +++ b/include/common/Timer.hpp @@ -24,332 +24,339 @@ namespace cadet { +/** + * @brief Base class for all timers + * @details Uses policy design pattern to inject the actual timer implementation. A valid + * timer implementation has to provide the following functions: + *
+ *              void start()
+ *              double stopCore() const
+ *          
+ * @tparam timer_t Actual timer implementation + */ +template class BaseTimer : public timer_t +{ +public: + BaseTimer() : timer_t(), _totalElapsed(0.0) + { + } + /** - * @brief Base class for all timers - * @details Uses policy design pattern to inject the actual timer implementation. A valid - * timer implementation has to provide the following functions: - *
-	 *              void start()
-	 *              double stopCore() const
-	 *          
- * @tparam timer_t Actual timer implementation + * @brief Stops the currently running timer and returns the elapsed time + * @details Accumulates the total elapsed time over all start() and stop() calls. + * @return Elapsed time since the last call to start() in seconds */ - template - class BaseTimer : public timer_t + inline double stop() { - public: - BaseTimer() : timer_t(), _totalElapsed(0.0) { } - - /** - * @brief Stops the currently running timer and returns the elapsed time - * @details Accumulates the total elapsed time over all start() and stop() calls. - * @return Elapsed time since the last call to start() in seconds - */ - inline double stop() - { - const double elapsed = timer_t::stopCore(); - _totalElapsed += elapsed; + const double elapsed = timer_t::stopCore(); + _totalElapsed += elapsed; - return elapsed; - } + return elapsed; + } - /** - * @brief Returns the total elapsed time between all start() and stop() calls in seconds - * @return Total elapsed time in seconds - */ - inline double totalElapsedTime() const - { - return _totalElapsed; - } + /** + * @brief Returns the total elapsed time between all start() and stop() calls in seconds + * @return Total elapsed time in seconds + */ + inline double totalElapsedTime() const + { + return _totalElapsed; + } - /** - * @brief Returns the total elapsed time between all start() and stop() calls in milliseconds - * @return Total elapsed time in milliseconds - */ - inline double totalElapsedTimeMs() const - { - return _totalElapsed * 1000.0; - } + /** + * @brief Returns the total elapsed time between all start() and stop() calls in milliseconds + * @return Total elapsed time in milliseconds + */ + inline double totalElapsedTimeMs() const + { + return _totalElapsed * 1000.0; + } - protected: - double _totalElapsed; - }; +protected: + double _totalElapsed; +}; } // namespace cadet - #ifdef CADET_USE_PLATFORM_TIMER - #ifdef _WIN32 - // Windows (x64 and x86) +#ifdef _WIN32 + // Windows (x64 and x86) - #define NOMINMAX - #define _WINDOWS - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #include +#define NOMINMAX +#define _WINDOWS +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include - namespace cadet - { +namespace cadet +{ + +/** + * @brief Windows timer implementation using QueryPerformanceFrequency() and QueryPerformanceCounter() + */ +class WindowsTimer +{ +public: + WindowsTimer() + { + // Get frequency of the clock + QueryPerformanceFrequency(&_frequency); + } - /** - * @brief Windows timer implementation using QueryPerformanceFrequency() and QueryPerformanceCounter() - */ - class WindowsTimer - { - public: - WindowsTimer() - { - // Get frequency of the clock - QueryPerformanceFrequency(&_frequency); - } + inline void start() + { + QueryPerformanceCounter(&_startCount); + } - inline void start() - { - QueryPerformanceCounter(&_startCount); - } +protected: + inline double stopCore() const + { + LARGE_INTEGER lastCount QueryPerformanceCounter(&lastCount); - protected: + // Convert elapsed time to seconds + return static_cast(lastCount.QuadPart - _startCount.QuadPart) / + static_cast(_frequency.QuadPart); + } - inline double stopCore() const - { - LARGE_INTEGER lastCount - QueryPerformanceCounter(&lastCount); +protected: + LARGE_INTEGER _frequency; + LARGE_INTEGER _startCount; +}; - // Convert elapsed time to seconds - return static_cast(lastCount.QuadPart - _startCount.QuadPart) / static_cast(_frequency.QuadPart); - } +typedef BaseTimer Timer; - protected: - LARGE_INTEGER _frequency; - LARGE_INTEGER _startCount; - }; +} // namespace cadet - typedef BaseTimer Timer; +#elif __unix__ || __linux__ + // Unix and Linux +// Do not forget to link to librt via -lrt flag - } // namespace cadet +#include +#include - #elif __unix__ || __linux__ - // Unix and Linux - // Do not forget to link to librt via -lrt flag +namespace cadet +{ - #include - #include +/** + * @brief Linux timer implementation using clock_gettime() in the monotonic mode + */ +class LinuxTimer +{ +public: + // Constructor + LinuxTimer() + { + } - namespace cadet - { + inline void start() + { + clock_gettime(CLOCK_MONOTONIC, &_startCount); + } - /** - * @brief Linux timer implementation using clock_gettime() in the monotonic mode - */ - class LinuxTimer - { - public: - // Constructor - LinuxTimer() { } - - inline void start() - { - clock_gettime(CLOCK_MONOTONIC, &_startCount); - } - - protected: - - inline double stopCore() const - { - timespec lastCount; - clock_gettime(CLOCK_MONOTONIC, &lastCount); - - // Convert elapsed time to seconds - if (lastCount.tv_nsec < _startCount.tv_nsec) - { - return static_cast(lastCount.tv_sec - _startCount.tv_sec) - static_cast(_startCount.tv_nsec - lastCount.tv_nsec) * 1.0e-9; - } - else - { - return static_cast(lastCount.tv_sec - _startCount.tv_sec) + static_cast(lastCount.tv_nsec - _startCount.tv_nsec) * 1.0e-9; - } - } - - protected: - timespec _startCount; - }; - - typedef BaseTimer Timer; - - } // namespace cadet - - #elif __APPLE__ - // Mac OS X - - #include - #include - - namespace cadet +protected: + inline double stopCore() const + { + timespec lastCount; + clock_gettime(CLOCK_MONOTONIC, &lastCount); + + // Convert elapsed time to seconds + if (lastCount.tv_nsec < _startCount.tv_nsec) { + return static_cast(lastCount.tv_sec - _startCount.tv_sec) - + static_cast(_startCount.tv_nsec - lastCount.tv_nsec) * 1.0e-9; + } + else + { + return static_cast(lastCount.tv_sec - _startCount.tv_sec) + + static_cast(lastCount.tv_nsec - _startCount.tv_nsec) * 1.0e-9; + } + } + +protected: + timespec _startCount; +}; - /** - * @brief Mac OS X timer implementation using mach_absolute_time() - */ - class OSXTimer - { - public: - // Constructor - OSXTimer() - { - mach_timebase_info(&_timebaseInfo); - } +typedef BaseTimer Timer; + +} // namespace cadet - inline void start() - { - _startCount = mach_absolute_time(); - } +#elif __APPLE__ + // Mac OS X - protected: +#include +#include - inline double stopCore() const - { - const uint64_t lastCount = mach_absolute_time(); +namespace cadet +{ - // Convert elapsed time to seconds - // Hopefully this does not overflow - return static_cast((lastCount - _startCount) * _timebaseInfo.numer / _timebaseInfo.denom) / 1.0e9; - } +/** + * @brief Mac OS X timer implementation using mach_absolute_time() + */ +class OSXTimer +{ +public: + // Constructor + OSXTimer() + { + mach_timebase_info(&_timebaseInfo); + } - protected: - mach_timebase_info_data_t _timebaseInfo; - uint64_t _startCount; - }; + inline void start() + { + _startCount = mach_absolute_time(); + } - typedef BaseTimer Timer; +protected: + inline double stopCore() const + { + const uint64_t lastCount = mach_absolute_time(); - } // namespace cadet + // Convert elapsed time to seconds + // Hopefully this does not overflow + return static_cast((lastCount - _startCount) * _timebaseInfo.numer / _timebaseInfo.denom) / 1.0e9; + } - #endif +protected: + mach_timebase_info_data_t _timebaseInfo; + uint64_t _startCount; +}; -#else +typedef BaseTimer Timer; - #ifdef _OPENMP +} // namespace cadet - #include +#endif - namespace cadet - { +#else - /** - * @brief OpenMP timer implementation using omp_get_wtime() - */ - class OpenMPTimer - { - public: - // Constructor - OpenMPTimer() : _startTime(0.0) { } +#ifdef _OPENMP - inline void start() - { - _startTime = omp_get_wtime(); - } +#include - inline double resolution() const - { - return omp_get_wtick(); - } +namespace cadet +{ - protected: +/** + * @brief OpenMP timer implementation using omp_get_wtime() + */ +class OpenMPTimer +{ +public: + // Constructor + OpenMPTimer() : _startTime(0.0) + { + } - inline double stopCore() const - { - return omp_get_wtime() - _startTime; - } + inline void start() + { + _startTime = omp_get_wtime(); + } - protected: - double _startTime; - }; + inline double resolution() const + { + return omp_get_wtick(); + } - typedef BaseTimer Timer; +protected: + inline double stopCore() const + { + return omp_get_wtime() - _startTime; + } - } // namespace cadet +protected: + double _startTime; +}; - #elif defined(CADET_PARALLELIZE) +typedef BaseTimer Timer; - #include +} // namespace cadet - namespace cadet - { +#elif defined(CADET_PARALLELIZE) - /** - * @brief TBB timer implementation using tbb:tick_count::now() - */ - class TBBTimer - { - public: - // Constructor - TBBTimer() : _startTime() { } +#include - inline void start() - { - _startTime = tbb::tick_count::now(); - } +namespace cadet +{ - inline double resolution() const - { - return tbb::tick_count::resolution(); - } +/** + * @brief TBB timer implementation using tbb:tick_count::now() + */ +class TBBTimer +{ +public: + // Constructor + TBBTimer() : _startTime() + { + } - protected: + inline void start() + { + _startTime = tbb::tick_count::now(); + } - inline double stopCore() const - { - return (tbb::tick_count::now() - _startTime).seconds(); - } + inline double resolution() const + { + return tbb::tick_count::resolution(); + } - protected: - tbb::tick_count _startTime; - }; +protected: + inline double stopCore() const + { + return (tbb::tick_count::now() - _startTime).seconds(); + } - typedef BaseTimer Timer; +protected: + tbb::tick_count _startTime; +}; - } // namespace cadet +typedef BaseTimer Timer; - #else +} // namespace cadet - #include +#else - namespace cadet - { +#include - /** - * @brief Standard C++ library timer implementation using std::chrono::high_resolution_clock - */ - class StdTimer - { - public: - // Constructor - StdTimer() : _startTime() { } +namespace cadet +{ - inline void start() - { - _startTime = std::chrono::high_resolution_clock::now(); - } +/** + * @brief Standard C++ library timer implementation using std::chrono::high_resolution_clock + */ +class StdTimer +{ +public: + // Constructor + StdTimer() : _startTime() + { + } - protected: + inline void start() + { + _startTime = std::chrono::high_resolution_clock::now(); + } - inline double stopCore() const - { - return std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - _startTime).count() * 1e-9; - } +protected: + inline double stopCore() const + { + return std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - + _startTime) + .count() * + 1e-9; + } - protected: - typename std::chrono::high_resolution_clock::time_point _startTime; - }; +protected: + typename std::chrono::high_resolution_clock::time_point _startTime; +}; - typedef BaseTimer Timer; +typedef BaseTimer Timer; - } // namespace cadet +} // namespace cadet - #endif +#endif #endif -#endif // CADET_TIMER_HPP_ +#endif // CADET_TIMER_HPP_ diff --git a/include/io/FileIO.hpp b/include/io/FileIO.hpp index 0b361899d..f0548223f 100644 --- a/include/io/FileIO.hpp +++ b/include/io/FileIO.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -27,8 +27,12 @@ namespace io class IReaderCapabilities { public: - IReaderCapabilities() { } - virtual ~IReaderCapabilities() CADET_NOEXCEPT { } + IReaderCapabilities() + { + } + virtual ~IReaderCapabilities() CADET_NOEXCEPT + { + } virtual double getDouble(const std::string& paramName) = 0; virtual int getInt(const std::string& paramName) = 0; @@ -49,27 +53,44 @@ class IReaderCapabilities virtual bool isDouble(const std::string& elementName) = 0; }; - class IWriterCapabilities { public: - IWriterCapabilities() { } - virtual ~IWriterCapabilities() CADET_NOEXCEPT { } - - virtual void writeTensorDouble(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const double* buffer, const std::size_t stride, const std::size_t blockSize) = 0; - virtual void writeTensorInt(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const int* buffer, const std::size_t stride, const std::size_t blockSize) = 0; - virtual void writeTensorBool(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const bool* buffer, const std::size_t stride, const std::size_t blockSize) = 0; - virtual void writeTensorString(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const std::string* buffer, const std::size_t stride, const std::size_t blockSize) = 0; - - virtual void writeMatrixDouble(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const double* buffer, const std::size_t stride, const std::size_t blockSize) = 0; - virtual void writeMatrixInt(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const int* buffer, const std::size_t stride, const std::size_t blockSize) = 0; - virtual void writeMatrixBool(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const bool* buffer, const std::size_t stride, const std::size_t blockSize) = 0; - virtual void writeMatrixString(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const std::string* buffer, const std::size_t stride, const std::size_t blockSize) = 0; - - virtual void writeVectorDouble(const std::string& dataSetName, const std::size_t length, const double* buffer, const std::size_t stride, const std::size_t blockSize) = 0; - virtual void writeVectorInt(const std::string& dataSetName, const std::size_t length, const int* buffer, const std::size_t stride, const std::size_t blockSize) = 0; - virtual void writeVectorBool(const std::string& dataSetName, const std::size_t length, const bool* buffer, const std::size_t stride, const std::size_t blockSize) = 0; - virtual void writeVectorString(const std::string& dataSetName, const std::size_t length, const std::string* buffer, const std::size_t stride, const std::size_t blockSize) = 0; + IWriterCapabilities() + { + } + virtual ~IWriterCapabilities() CADET_NOEXCEPT + { + } + + virtual void writeTensorDouble(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const double* buffer, const std::size_t stride, const std::size_t blockSize) = 0; + virtual void writeTensorInt(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const int* buffer, const std::size_t stride, const std::size_t blockSize) = 0; + virtual void writeTensorBool(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const bool* buffer, const std::size_t stride, const std::size_t blockSize) = 0; + virtual void writeTensorString(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const std::string* buffer, const std::size_t stride, + const std::size_t blockSize) = 0; + + virtual void writeMatrixDouble(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const double* buffer, const std::size_t stride, const std::size_t blockSize) = 0; + virtual void writeMatrixInt(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const int* buffer, const std::size_t stride, const std::size_t blockSize) = 0; + virtual void writeMatrixBool(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const bool* buffer, const std::size_t stride, const std::size_t blockSize) = 0; + virtual void writeMatrixString(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const std::string* buffer, const std::size_t stride, + const std::size_t blockSize) = 0; + + virtual void writeVectorDouble(const std::string& dataSetName, const std::size_t length, const double* buffer, + const std::size_t stride, const std::size_t blockSize) = 0; + virtual void writeVectorInt(const std::string& dataSetName, const std::size_t length, const int* buffer, + const std::size_t stride, const std::size_t blockSize) = 0; + virtual void writeVectorBool(const std::string& dataSetName, const std::size_t length, const bool* buffer, + const std::size_t stride, const std::size_t blockSize) = 0; + virtual void writeVectorString(const std::string& dataSetName, const std::size_t length, const std::string* buffer, + const std::size_t stride, const std::size_t blockSize) = 0; virtual void writeDouble(const std::string& dataSetName, const double buffer) = 0; virtual void writeInt(const std::string& dataSetName, const int buffer) = 0; @@ -80,12 +101,15 @@ class IWriterCapabilities virtual void deleteDataset(const std::string& dsName) = 0; }; - class INavigationCapabilities { public: - INavigationCapabilities() { } - virtual ~INavigationCapabilities() CADET_NOEXCEPT { } + INavigationCapabilities() + { + } + virtual ~INavigationCapabilities() CADET_NOEXCEPT + { + } virtual void pushGroup(const std::string& scope) = 0; virtual void popGroup() = 0; @@ -98,29 +122,42 @@ class INavigationCapabilities virtual std::vector itemNames() = 0; }; - -class IMemoryIO : public INavigationCapabilities, public IReaderCapabilities, public IWriterCapabilities { }; +class IMemoryIO : public INavigationCapabilities, public IReaderCapabilities, public IWriterCapabilities +{ +}; class IFileIO { public: - IFileIO() { } - virtual ~IFileIO() CADET_NOEXCEPT { } + IFileIO() + { + } + virtual ~IFileIO() CADET_NOEXCEPT + { + } virtual void openFile(const std::string& fileName, const char* mode) = 0; virtual void closeFile() = 0; }; -class IReader : public INavigationCapabilities, public IReaderCapabilities { }; -class IWriter : public INavigationCapabilities, public IWriterCapabilities { }; -class IFileReader : public IFileIO, public IReader { }; -class IFileWriter : public IFileIO, public IWriter { }; +class IReader : public INavigationCapabilities, public IReaderCapabilities +{ +}; +class IWriter : public INavigationCapabilities, public IWriterCapabilities +{ +}; +class IFileReader : public IFileIO, public IReader +{ +}; +class IFileWriter : public IFileIO, public IWriter +{ +}; IMemoryIO* createMemoryReaderWriter(); IFileReader* createReader(const std::string& fileExt); IFileWriter* createWriter(const std::string& fileExt); -} // namespace io +} // namespace io } // namespace cadet #endif /* LIBCADET_FILEIO_HPP_ */ diff --git a/include/io/IOException.hpp b/include/io/IOException.hpp index 0d139ec42..fa095654c 100644 --- a/include/io/IOException.hpp +++ b/include/io/IOException.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -16,7 +16,7 @@ #include #include -namespace cadet +namespace cadet { namespace io @@ -25,13 +25,13 @@ namespace io class IOException : public std::runtime_error { public: - IOException(const std::string& message) : std::runtime_error(message) { } + IOException(const std::string& message) : std::runtime_error(message) + { + } }; - } // namespace io } // namespace cadet - #endif /* LIBCADET_IOEXCEPTION_HPP_ */ diff --git a/include/io/hdf5/HDF5Base.hpp b/include/io/hdf5/HDF5Base.hpp index 4471ba6ae..af572b2bb 100644 --- a/include/io/hdf5/HDF5Base.hpp +++ b/include/io/hdf5/HDF5Base.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -41,7 +41,10 @@ class HDF5Base /// \brief Open an HDF5 file inline void openFile(const std::string& fileName, const std::string& mode = "r"); - inline void openFile(const char* fileName, const std::string& mode = "r") { openFile(std::string(fileName), mode); } + inline void openFile(const char* fileName, const std::string& mode = "r") + { + openFile(std::string(fileName), mode); + } /// \brief Close the currently opened file inline void closeFile(); @@ -51,40 +54,70 @@ class HDF5Base /// \brief Open the subgroup with the given name inline void pushGroup(const std::string& groupName); - /// \brief Close the currently open subgroup + /// \brief Close the currently open subgroup inline void popGroup(); /// \brief Checks if the given dataset or group exists in the file inline bool exists(const std::string& elementName); - inline bool exists(const char* elementName) { return exists(std::string(elementName)); } + inline bool exists(const char* elementName) + { + return exists(std::string(elementName)); + } /// \brief Checks if the given dataset is a vector (i.e., has more than one value) inline bool isVector(const std::string& elementName); - inline bool isVector(const char* elementName) { return isVector(std::string(elementName)); } + inline bool isVector(const char* elementName) + { + return isVector(std::string(elementName)); + } /// \brief Checks if the given dataset is a string inline bool isString(const std::string& elementName); - inline bool isString(const char* elementName) { return isString(std::string(elementName)); } + inline bool isString(const char* elementName) + { + return isString(std::string(elementName)); + } /// \brief Checks if the given dataset is a signed int - inline bool isInt(const std::string& elementName) { return isDataType(elementName, H5T_NATIVE_INT); } - inline bool isInt(const char* elementName) { return isInt(std::string(elementName)); } + inline bool isInt(const std::string& elementName) + { + return isDataType(elementName, H5T_NATIVE_INT); + } + inline bool isInt(const char* elementName) + { + return isInt(std::string(elementName)); + } /// \brief Checks if the given dataset is a double - inline bool isDouble(const std::string& elementName) { return isDataType(elementName, H5T_NATIVE_DOUBLE); } - inline bool isDouble(const char* elementName) { return isDouble(std::string(elementName)); } + inline bool isDouble(const std::string& elementName) + { + return isDataType(elementName, H5T_NATIVE_DOUBLE); + } + inline bool isDouble(const char* elementName) + { + return isDouble(std::string(elementName)); + } /// \brief Checks whether the given element is a group inline bool isGroup(const std::string& elementName); - inline bool isGroup(const char* elementName) { return isGroup(std::string(elementName)); } + inline bool isGroup(const char* elementName) + { + return isGroup(std::string(elementName)); + } /// \brief Returns the dimensions of the tensor identified by name inline std::vector tensorDimensions(const std::string& elementName); - inline std::vector tensorDimensions(const char* elementName) { return tensorDimensions(std::string(elementName)); } + inline std::vector tensorDimensions(const char* elementName) + { + return tensorDimensions(std::string(elementName)); + } /// \brief Returns the number of elements in the array identified by name inline std::size_t arraySize(const std::string& elementName); - inline std::size_t arraySize(const char* elementName) { return arraySize(std::string(elementName)); } + inline std::size_t arraySize(const char* elementName) + { + return arraySize(std::string(elementName)); + } /// \brief Returns the number of items in the group inline int numItems(); @@ -94,6 +127,7 @@ class HDF5Base /// \brief Returns the names of all items in the group inline std::vector itemNames(); + protected: hid_t _file; @@ -107,37 +141,39 @@ class HDF5Base bool isDataType(const std::string& elementName, hid_t refType); }; - HDF5Base::HDF5Base() { H5Eset_auto(H5E_DEFAULT, NULL, NULL); _groupNames.push_back("/"); } - -HDF5Base::~HDF5Base() CADET_NOEXCEPT { } - +HDF5Base::~HDF5Base() CADET_NOEXCEPT +{ +} void HDF5Base::openFile(const std::string& fileName, const std::string& mode) { - if (mode == "r" ) _file = H5Fopen(fileName.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT); // open in read mode - else if (mode == "rw") _file = H5Fopen(fileName.c_str(), H5F_ACC_RDWR, H5P_DEFAULT); // open in read / write mode - else if (mode == "c" ) _file = H5Fcreate(fileName.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); // create new file - else if (mode == "co") _file = H5Fcreate(fileName.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); // create / overwrite new file - else throw IOException("Wrong file open mode"); + if (mode == "r") + _file = H5Fopen(fileName.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT); // open in read mode + else if (mode == "rw") + _file = H5Fopen(fileName.c_str(), H5F_ACC_RDWR, H5P_DEFAULT); // open in read / write mode + else if (mode == "c") + _file = H5Fcreate(fileName.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); // create new file + else if (mode == "co") + _file = H5Fcreate(fileName.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); // create / overwrite new file + else + throw IOException("Wrong file open mode"); if (_file < 0) throw IOException("Failed to open or create HDF5 file \"" + fileName + "\" in mode " + mode); } - void HDF5Base::closeFile() { closeGroup(); H5Fclose(_file); } - bool HDF5Base::exists(const std::string& elementName) { openGroup(); @@ -149,7 +185,7 @@ bool HDF5Base::exists(const std::string& elementName) // Found the group H5Gclose(grp); closeGroup(); - return true; + return true; } // Try to open elementName as dataset @@ -167,7 +203,6 @@ bool HDF5Base::exists(const std::string& elementName) return false; } - bool HDF5Base::isVector(const std::string& elementName) { openGroup(); @@ -196,7 +231,6 @@ bool HDF5Base::isVector(const std::string& elementName) return isVector; } - bool HDF5Base::isString(const std::string& elementName) { // Get the dataset we want to read from @@ -206,7 +240,7 @@ bool HDF5Base::isString(const std::string& elementName) if (obj < 0) { closeGroup(); - throw IOException("Field \"" + elementName + "\" does not exist in group " + getFullGroupName()); + throw IOException("Field \"" + elementName + "\" does not exist in group " + getFullGroupName()); } const H5I_type_t oType = H5Iget_type(obj); @@ -222,20 +256,19 @@ bool HDF5Base::isString(const std::string& elementName) closeGroup(); if (dataSet < 0) - throw IOException("Field \"" + elementName + "\" does not exist in group " + getFullGroupName()); + throw IOException("Field \"" + elementName + "\" does not exist in group " + getFullGroupName()); // Determine the datatype const hid_t dataType = H5Dget_type(dataSet); const H5T_class_t typeClass = H5Tget_class(dataType); const bool result = (H5Tis_variable_str(dataType) > 0) || (typeClass == H5T_STRING); - + H5Tclose(dataType); - H5Dclose(dataSet); + H5Dclose(dataSet); return result; } - bool HDF5Base::isDataType(const std::string& elementName, hid_t refType) { // Get the dataset we want to read from @@ -245,7 +278,7 @@ bool HDF5Base::isDataType(const std::string& elementName, hid_t refType) if (obj < 0) { closeGroup(); - throw IOException("Field \"" + elementName + "\" does not exist in group " + getFullGroupName()); + throw IOException("Field \"" + elementName + "\" does not exist in group " + getFullGroupName()); } const H5I_type_t oType = H5Iget_type(obj); @@ -261,22 +294,21 @@ bool HDF5Base::isDataType(const std::string& elementName, hid_t refType) closeGroup(); if (dataSet < 0) - throw IOException("Field \"" + elementName + "\" does not exist in group " + getFullGroupName()); + throw IOException("Field \"" + elementName + "\" does not exist in group " + getFullGroupName()); // Determine the datatype const hid_t dataType = H5Dget_type(dataSet); const hid_t nativeType = H5Tget_native_type(dataType, H5T_DIR_ASCEND); - + const bool result = H5Tequal(nativeType, refType) > 0; H5Tclose(nativeType); H5Tclose(dataType); - H5Dclose(dataSet); + H5Dclose(dataSet); return result; } - bool HDF5Base::isGroup(const std::string& elementName) { // Get the dataset we want to read from @@ -286,7 +318,7 @@ bool HDF5Base::isGroup(const std::string& elementName) if (obj < 0) { closeGroup(); - throw IOException("Field \"" + elementName + "\" does not exist in group " + getFullGroupName()); + throw IOException("Field \"" + elementName + "\" does not exist in group " + getFullGroupName()); } const H5I_type_t oType = H5Iget_type(obj); @@ -296,7 +328,6 @@ bool HDF5Base::isGroup(const std::string& elementName) return oType == H5I_GROUP; } - std::vector HDF5Base::tensorDimensions(const std::string& elementName) { // Get the dataset we want to read from @@ -327,7 +358,6 @@ std::vector HDF5Base::tensorDimensions(const std::string& elementNa return dims; } - std::size_t HDF5Base::arraySize(const std::string& elementName) { // Get the dataset we want to read from @@ -347,7 +377,6 @@ std::size_t HDF5Base::arraySize(const std::string& elementName) return n; } - int HDF5Base::numItems() { H5G_info_t info; @@ -358,15 +387,15 @@ int HDF5Base::numItems() return info.nlinks; } - std::string HDF5Base::itemName(int n) { openGroup(); - - const std::size_t len = H5Lget_name_by_idx(_groupsOpened.top(), ".", H5_INDEX_NAME, H5_ITER_NATIVE, n, nullptr, 0, H5P_DEFAULT) + 1; + + const std::size_t len = + H5Lget_name_by_idx(_groupsOpened.top(), ".", H5_INDEX_NAME, H5_ITER_NATIVE, n, nullptr, 0, H5P_DEFAULT) + 1; char* const data = new char[len]; - data[len-1] = 0; - + data[len - 1] = 0; + H5Lget_name_by_idx(_groupsOpened.top(), ".", H5_INDEX_NAME, H5_ITER_NATIVE, n, data, len, H5P_DEFAULT); const std::string name(data); delete[] data; @@ -376,7 +405,6 @@ std::string HDF5Base::itemName(int n) return name; } - std::vector HDF5Base::itemNames() { H5G_info_t info; @@ -391,7 +419,8 @@ std::vector HDF5Base::itemNames() std::size_t maxBufSize = 0; for (std::size_t i = 0; i < info.nlinks; ++i) { - const std::size_t len = H5Lget_name_by_idx(_groupsOpened.top(), ".", H5_INDEX_NAME, H5_ITER_NATIVE, i, nullptr, 0, H5P_DEFAULT); + const std::size_t len = + H5Lget_name_by_idx(_groupsOpened.top(), ".", H5_INDEX_NAME, H5_ITER_NATIVE, i, nullptr, 0, H5P_DEFAULT); maxBufSize = std::max(maxBufSize, len + 1); } @@ -411,13 +440,12 @@ std::vector HDF5Base::itemNames() return names; } - void HDF5Base::setGroup(const std::string& groupName) { _groupNames.clear(); - std::size_t start = 0; - std::size_t end = 0; + std::size_t start = 0; + std::size_t end = 0; std::string delimiter("/"); // Quick return when called with empty group name @@ -428,33 +456,32 @@ void HDF5Base::setGroup(const std::string& groupName) } // Don't care for a preceding delimiter - if (groupName[0] == delimiter[0]) ++start; + if (groupName[0] == delimiter[0]) + ++start; while (end != std::string::npos) { end = groupName.find(delimiter, start); // If at end, use length = maxLength. Else use length = end - start. - _groupNames.push_back(delimiter + groupName.substr(start, (end == std::string::npos) ? std::string::npos : end - start)); + _groupNames.push_back(delimiter + + groupName.substr(start, (end == std::string::npos) ? std::string::npos : end - start)); // If at end, use start = maxSize. Else use start = end + delimiter. start = ((end > (std::string::npos - delimiter.size())) ? std::string::npos : end + delimiter.size()); } } - void HDF5Base::pushGroup(const std::string& groupName) { _groupNames.push_back("/" + groupName); } - void HDF5Base::popGroup() { _groupNames.pop_back(); } - void HDF5Base::openGroup(bool forceCreation) { std::string dynName; @@ -471,7 +498,6 @@ void HDF5Base::openGroup(bool forceCreation) } } - void HDF5Base::closeGroup() { while (_groupsOpened.size() >= 1) @@ -481,7 +507,6 @@ void HDF5Base::closeGroup() } } - std::string HDF5Base::getFullGroupName() { std::ostringstream oss; @@ -492,9 +517,8 @@ std::string HDF5Base::getFullGroupName() return oss.str(); } -} // namespace io - -} // namespace cadet +} // namespace io +} // namespace cadet #endif /* HDF5BASE_HPP_ */ diff --git a/include/io/hdf5/HDF5Reader.hpp b/include/io/hdf5/HDF5Reader.hpp index 5ed5cc9cc..555816e6b 100644 --- a/include/io/hdf5/HDF5Reader.hpp +++ b/include/io/hdf5/HDF5Reader.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -36,50 +36,45 @@ class HDF5Reader : public HDF5Base ~HDF5Reader() CADET_NOEXCEPT; /// \brief Convenience wrapper for reading vectors - template - std::vector vector(const std::string& dataSetName); + template std::vector vector(const std::string& dataSetName); /// \brief Convenience wrapper for reading scalars - template - T scalar(const std::string& dataSetName, std::size_t position = 0); + template T scalar(const std::string& dataSetName, std::size_t position = 0); private: - template - std::vector read(const std::string& dataSetName, hid_t dataType); + template std::vector read(const std::string& dataSetName, hid_t dataType); }; +HDF5Reader::HDF5Reader() +{ +} -HDF5Reader::HDF5Reader() { } - -HDF5Reader::~HDF5Reader() CADET_NOEXCEPT { } - +HDF5Reader::~HDF5Reader() CADET_NOEXCEPT +{ +} // ============================================================================================================ // Template specializations of member functions for diffenet data types // ============================================================================================================ // Double specialization of vector() -template <> -std::vector HDF5Reader::vector(const std::string& dataSetName) +template <> std::vector HDF5Reader::vector(const std::string& dataSetName) { return read(dataSetName, H5T_NATIVE_DOUBLE); } // Integer specializations of vector() -template <> -std::vector HDF5Reader::vector(const std::string& dataSetName) +template <> std::vector HDF5Reader::vector(const std::string& dataSetName) { return read(dataSetName, H5T_NATIVE_INT); } -template <> -std::vector HDF5Reader::vector(const std::string& dataSetName) +template <> std::vector HDF5Reader::vector(const std::string& dataSetName) { return read(dataSetName, H5T_NATIVE_UINT64); } // std::string specialization of vector() -template <> -std::vector HDF5Reader::vector(const std::string& dataSetName) +template <> std::vector HDF5Reader::vector(const std::string& dataSetName) { // Get the dataset we want to read from openGroup(); @@ -87,7 +82,7 @@ std::vector HDF5Reader::vector(const std::string& data closeGroup(); if (dataSet < 0) - throw IOException("Field \"" + dataSetName + "\" does not exist in group " + getFullGroupName()); + throw IOException("Field \"" + dataSetName + "\" does not exist in group " + getFullGroupName()); // Determine the datatype and allocate buffer const hid_t dataType = H5Dget_type(dataSet); @@ -106,7 +101,7 @@ std::vector HDF5Reader::vector(const std::string& data if (H5Tis_variable_str(dataType)) { - char** buffer = new char*[bufSize]; + char** buffer = new char*[bufSize]; const hid_t memType = H5Tcopy(H5T_C_S1); H5Tset_size(memType, H5T_VARIABLE); @@ -140,7 +135,7 @@ std::vector HDF5Reader::vector(const std::string& data H5Tclose(memType); } - + H5Tclose(dataType); H5Sclose(dataSpace); H5Dclose(dataSet); @@ -149,23 +144,18 @@ std::vector HDF5Reader::vector(const std::string& data } // Template that matches on every unsupported type and throws an exception -template -std::vector HDF5Reader::vector(const std::string& dataSetName) +template std::vector HDF5Reader::vector(const std::string& dataSetName) { throw IOException("You may not try to read an unsupported type"); } // ============================================================================================================ - -template -T HDF5Reader::scalar(const std::string& dataSetName, std::size_t position) +template T HDF5Reader::scalar(const std::string& dataSetName, std::size_t position) { return vector(dataSetName).at(position); } - -template -std::vector HDF5Reader::read(const std::string& dataSetName, hid_t memType) +template std::vector HDF5Reader::read(const std::string& dataSetName, hid_t memType) { // Get the dataset we want to read from openGroup(); @@ -173,7 +163,7 @@ std::vector HDF5Reader::read(const std::string& dataSetName, hid_t memType) closeGroup(); if (dataSet < 0) - throw IOException("Field \"" + dataSetName + "\" does not exist in group " + getFullGroupName()); + throw IOException("Field \"" + dataSetName + "\" does not exist in group " + getFullGroupName()); // Determine the datatype and allocate buffer const hid_t dataType = H5Dget_type(dataSet); @@ -192,7 +182,7 @@ std::vector HDF5Reader::read(const std::string& dataSetName, hid_t memType) // Read data from file and write it to buffer H5Dread(dataSet, memType, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer.data()); - + H5Tclose(dataType); H5Sclose(dataSpace); H5Dclose(dataSet); @@ -200,10 +190,8 @@ std::vector HDF5Reader::read(const std::string& dataSetName, hid_t memType) return buffer; } +} // namespace io -} // namespace io - -} // namespace cadet - +} // namespace cadet #endif /* HDF5READER_HPP_ */ diff --git a/include/io/hdf5/HDF5Writer.hpp b/include/io/hdf5/HDF5Writer.hpp index 623654a59..cc39576a6 100644 --- a/include/io/hdf5/HDF5Writer.hpp +++ b/include/io/hdf5/HDF5Writer.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -42,35 +42,41 @@ class HDF5Writer : public HDF5Base /// \brief Write data from C-array to a dataset template - void write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); + void write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, + const std::size_t stride = 1, const std::size_t blockSize = 1); /// \brief Convenience wrapper for writing tensors from C-array template - void tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); + void tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, + const std::size_t stride = 1, const std::size_t blockSize = 1); /// \brief Convenience wrapper for writing tensors from std::vector template - void tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const std::vector& buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); + void tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const std::vector& buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); /// \brief Convenience wrapper for writing matrices from C-array template - void matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const T* buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); + void matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const T* buffer, + const std::size_t stride = 1, const std::size_t blockSize = 1); /// \brief Convenience wrapper for writing matrices from std::vector template - void matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const std::vector& buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); + void matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const std::vector& buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); /// \brief Convenience wrapper for writing vectors from C-array template - void vector(const std::string& dataSetName, const std::size_t length, const T* buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); + void vector(const std::string& dataSetName, const std::size_t length, const T* buffer, const std::size_t stride = 1, + const std::size_t blockSize = 1); /// \brief Convenience wrapper for writing vectors from std::vector template - void vector(const std::string& dataSetName, const std::vector& buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); + void vector(const std::string& dataSetName, const std::vector& buffer, const std::size_t stride = 1, + const std::size_t blockSize = 1); /// \brief Convenience wrapper for writing scalars - template - void scalar(const std::string& dataSetName, const T buffer); + template void scalar(const std::string& dataSetName, const T buffer); /// \brief Removes an existing group from the file inline void unlinkGroup(const std::string& groupName); @@ -79,60 +85,67 @@ class HDF5Writer : public HDF5Base inline void unlinkDataset(const std::string& dsName); /// \brief Enable/disable compression for tensors of 2nd order and above - inline void compressFields(bool setCompression) {_writeCompressed = setCompression;} + inline void compressFields(bool setCompression) + { + _writeCompressed = setCompression; + } /// \brief Tensors of 2nd order (matrices) and above are written as extendible fields /// (maxsize = unlimited, chunked layout), when set to true. - inline void extendibleFields(bool setExtendible) {_writeExtendible = setExtendible;} + inline void extendibleFields(bool setExtendible) + { + _writeExtendible = setExtendible; + } private: - - void writeWork(const std::string& dataSetName, hid_t memType, hid_t fileType, const std::size_t rank, const std::size_t* dims, const void* buffer, const std::size_t stride, const std::size_t blockSize); - - bool _writeScalar; - bool _writeExtendible; - bool _writeCompressed; - hsize_t* _maxDims; - hsize_t* _chunks; - double _chunkFactor; + void writeWork(const std::string& dataSetName, hid_t memType, hid_t fileType, const std::size_t rank, + const std::size_t* dims, const void* buffer, const std::size_t stride, const std::size_t blockSize); + + bool _writeScalar; + bool _writeExtendible; + bool _writeCompressed; + hsize_t* _maxDims; + hsize_t* _chunks; + double _chunkFactor; }; +HDF5Writer::HDF5Writer() + : _writeScalar(false), _writeExtendible(true), _writeCompressed(false), _maxDims(NULL), _chunks(NULL), + _chunkFactor(1.5) +{ +} -HDF5Writer::HDF5Writer() : - _writeScalar(false), - _writeExtendible(true), - _writeCompressed(false), - _maxDims(NULL), - _chunks(NULL), - _chunkFactor(1.5) -{} - -HDF5Writer::~HDF5Writer() CADET_NOEXCEPT { } - +HDF5Writer::~HDF5Writer() CADET_NOEXCEPT +{ +} // ============================================================================================================ // Template specializations of member function write() for diffenet data types // ============================================================================================================ template <> -void HDF5Writer::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const double* buffer, const std::size_t stride, const std::size_t blockSize) +void HDF5Writer::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const double* buffer, const std::size_t stride, const std::size_t blockSize) { writeWork(dataSetName, H5T_NATIVE_DOUBLE, H5T_IEEE_F64LE, rank, dims, buffer, stride, blockSize); } template <> -void HDF5Writer::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const int* buffer, const std::size_t stride, const std::size_t blockSize) +void HDF5Writer::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const int* buffer, const std::size_t stride, const std::size_t blockSize) { writeWork(dataSetName, H5T_NATIVE_INT, H5T_STD_I32LE, rank, dims, buffer, stride, blockSize); } template <> -void HDF5Writer::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const uint64_t* buffer, const std::size_t stride, const std::size_t blockSize) +void HDF5Writer::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const uint64_t* buffer, const std::size_t stride, const std::size_t blockSize) { writeWork(dataSetName, H5T_NATIVE_UINT64, H5T_STD_I32LE, rank, dims, buffer, stride, blockSize); } template <> -void HDF5Writer::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const std::string* buffer, const std::size_t stride, const std::size_t blockSize) +void HDF5Writer::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const std::string* buffer, const std::size_t stride, const std::size_t blockSize) { hid_t dataType = H5Tcopy(H5T_C_S1); H5Tset_size(dataType, H5T_VARIABLE); @@ -170,18 +183,19 @@ void HDF5Writer::write(const std::string& dataSetName, const std::size_t rank, c } template -void HDF5Writer::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, const std::size_t stride, const std::size_t blockSize) +void HDF5Writer::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, + const std::size_t stride, const std::size_t blockSize) { throw IOException("You may not try to write an unsupported type"); } // ============================================================================================================ - // ============================================================================================================ // Convenience wrappers // ============================================================================================================ template -void HDF5Writer::tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const std::vector& buffer, const std::size_t stride, const std::size_t blockSize) +void HDF5Writer::tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const std::vector& buffer, const std::size_t stride, const std::size_t blockSize) { #ifdef CADET_DEBUG std::size_t bufSize = 1; @@ -193,54 +207,56 @@ void HDF5Writer::tensor(const std::string& dataSetName, const std::size_t rank, } template -void HDF5Writer::tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, const std::size_t stride, const std::size_t blockSize) +void HDF5Writer::tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const T* buffer, const std::size_t stride, const std::size_t blockSize) { write(dataSetName, rank, dims, buffer, stride, blockSize); } template -void HDF5Writer::matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const T* buffer, const std::size_t stride, const std::size_t blockSize) +void HDF5Writer::matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const T* buffer, + const std::size_t stride, const std::size_t blockSize) { const std::size_t dims[2] = {rows, cols}; write(dataSetName, 2, dims, buffer, stride, blockSize); } template -void HDF5Writer::matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const std::vector& buffer, const std::size_t stride, const std::size_t blockSize) +void HDF5Writer::matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const std::vector& buffer, const std::size_t stride, const std::size_t blockSize) { - cadet_assert(rows*cols <= buffer.size()); + cadet_assert(rows * cols <= buffer.size()); const std::size_t dims[2] = {rows, cols}; write(dataSetName, 2, dims, buffer.data(), stride, blockSize); } template -void HDF5Writer::vector(const std::string& dataSetName, const std::size_t length, const T* buffer, const std::size_t stride, const std::size_t blockSize) +void HDF5Writer::vector(const std::string& dataSetName, const std::size_t length, const T* buffer, + const std::size_t stride, const std::size_t blockSize) { write(dataSetName, 1, &length, buffer, stride, blockSize); } template -void HDF5Writer::vector(const std::string& dataSetName, const std::vector& buffer, const std::size_t stride, const std::size_t blockSize) +void HDF5Writer::vector(const std::string& dataSetName, const std::vector& buffer, const std::size_t stride, + const std::size_t blockSize) { const std::size_t length = buffer.size() / stride; write(dataSetName, 1, &length, buffer.data(), stride, blockSize); } -template -void HDF5Writer::scalar(const std::string& dataSetName, const T buffer) +template void HDF5Writer::scalar(const std::string& dataSetName, const T buffer) { _writeScalar = true; vector(dataSetName, 1, &buffer); } // ============================================================================================================ - void HDF5Writer::unlinkGroup(const std::string& groupName) { H5Ldelete(_file, groupName.c_str(), H5P_DEFAULT); } - void HDF5Writer::unlinkDataset(const std::string& dsName) { bool wasOpen = !_groupsOpened.empty(); @@ -254,8 +270,9 @@ void HDF5Writer::unlinkDataset(const std::string& dsName) closeGroup(); } - -void HDF5Writer::writeWork(const std::string& dataSetName, hid_t memType, hid_t fileType, const std::size_t rank, const std::size_t* dims, const void* buffer, const std::size_t stride, const std::size_t blockSize) +void HDF5Writer::writeWork(const std::string& dataSetName, hid_t memType, hid_t fileType, const std::size_t rank, + const std::size_t* dims, const void* buffer, const std::size_t stride, + const std::size_t blockSize) { hid_t propList = H5Pcreate(H5P_DATASET_CREATE); hid_t dataSpace; @@ -263,9 +280,10 @@ void HDF5Writer::writeWork(const std::string& dataSetName, hid_t memType, hid_t { if (_writeExtendible || _writeCompressed) // we need chunking { - _chunks = new hsize_t[rank]; + _chunks = new hsize_t[rank]; for (std::size_t i = 0; i < rank; ++i) - _chunks[i] = (_writeExtendible) ? static_cast(dims[i] * _chunkFactor) : dims[i]; // leave some space in all dims, if extendible + _chunks[i] = (_writeExtendible) ? static_cast(dims[i] * _chunkFactor) + : dims[i]; // leave some space in all dims, if extendible H5Pset_chunk(propList, rank, _chunks); delete[] _chunks; @@ -305,11 +323,12 @@ void HDF5Writer::writeWork(const std::string& dataSetName, hid_t memType, hid_t // Create dataset openGroup(true); - const hid_t dataSet = H5Dcreate2(_groupsOpened.top(), dataSetName.c_str(), fileType, dataSpace, H5P_DEFAULT, propList, H5P_DEFAULT); + const hid_t dataSet = + H5Dcreate2(_groupsOpened.top(), dataSetName.c_str(), fileType, dataSpace, H5P_DEFAULT, propList, H5P_DEFAULT); closeGroup(); if (dataSet < 0) - throw IOException("Cannot create field \"" + dataSetName + "\" in group " + getFullGroupName()); + throw IOException("Cannot create field \"" + dataSetName + "\" in group " + getFullGroupName()); // Write data if (stride <= 1) @@ -337,7 +356,7 @@ void HDF5Writer::writeWork(const std::string& dataSetName, hid_t memType, hid_t H5Pclose(propList); } -} // namespace io -} // namespace cadet +} // namespace io +} // namespace cadet #endif /* HDF5WRITER_HPP_ */ diff --git a/include/io/xml/XMLBase.hpp b/include/io/xml/XMLBase.hpp index b686ee74f..640505d05 100644 --- a/include/io/xml/XMLBase.hpp +++ b/include/io/xml/XMLBase.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -23,7 +23,7 @@ #include #include "io/IOException.hpp" -namespace cadet +namespace cadet { namespace io @@ -42,7 +42,10 @@ class XMLBase /// \brief Open an XML file inline void openFile(const std::string& fileName, const std::string& mode = "r"); - inline void openFile(const char* fileName, const std::string& mode = "r") { openFile(std::string(fileName), mode); } + inline void openFile(const char* fileName, const std::string& mode = "r") + { + openFile(std::string(fileName), mode); + } /// \brief Close the currently opened file inline void closeFile(); @@ -52,40 +55,73 @@ class XMLBase /// \brief Open the subgroup with the given name inline void pushGroup(const std::string& groupName); - /// \brief Close the currently open subgroup + /// \brief Close the currently open subgroup inline void popGroup(); /// \brief Checks if the given dataset or group exists in the file - inline bool exists(const std::string& elementName) { return exists(elementName.c_str()); } + inline bool exists(const std::string& elementName) + { + return exists(elementName.c_str()); + } inline bool exists(const char* elementName); /// \brief Checks if the given dataset is a vector (i.e., has more than one value) - inline bool isVector(const std::string& elementName) { return isVector(elementName.c_str()); } + inline bool isVector(const std::string& elementName) + { + return isVector(elementName.c_str()); + } inline bool isVector(const char* elementName); /// \brief Checks if the given dataset is a string - inline bool isString(const std::string& elementName) { return isDataType(elementName, _typeChar); } - inline bool isString(const char* elementName) { return isString(std::string(elementName)); } + inline bool isString(const std::string& elementName) + { + return isDataType(elementName, _typeChar); + } + inline bool isString(const char* elementName) + { + return isString(std::string(elementName)); + } /// \brief Checks if the given dataset is a signed int - inline bool isInt(const std::string& elementName) { return isDataType(elementName, _typeInt); } - inline bool isInt(const char* elementName) { return isInt(std::string(elementName)); } + inline bool isInt(const std::string& elementName) + { + return isDataType(elementName, _typeInt); + } + inline bool isInt(const char* elementName) + { + return isInt(std::string(elementName)); + } /// \brief Checks if the given dataset is a double - inline bool isDouble(const std::string& elementName) { return isDataType(elementName, _typeDouble); } - inline bool isDouble(const char* elementName) { return isDouble(std::string(elementName)); } + inline bool isDouble(const std::string& elementName) + { + return isDataType(elementName, _typeDouble); + } + inline bool isDouble(const char* elementName) + { + return isDouble(std::string(elementName)); + } /// \brief Checks whether the given element is a group inline bool isGroup(const std::string& elementName); - inline bool isGroup(const char* elementName) { return isGroup(std::string(elementName)); } + inline bool isGroup(const char* elementName) + { + return isGroup(std::string(elementName)); + } /// \brief Returns the dimensions of the tensor identified by name inline std::vector tensorDimensions(const std::string& elementName); - inline std::vector tensorDimensions(const char* elementName) { return tensorDimensions(std::string(elementName)); } + inline std::vector tensorDimensions(const char* elementName) + { + return tensorDimensions(std::string(elementName)); + } /// \brief Returns the number of elements in the array identified by name inline std::size_t arraySize(const std::string& elementName); - inline std::size_t arraySize(const char* elementName) { return arraySize(std::string(elementName)); } + inline std::size_t arraySize(const char* elementName) + { + return arraySize(std::string(elementName)); + } /// \brief Returns the number of items in the group inline int numItems(); @@ -95,101 +131,96 @@ class XMLBase /// \brief Returns the names of all items in the group inline std::vector itemNames(); -protected: - - xml_document _doc; //!< XML document - xml_node _root; //!< XML root node - bool _enable_write; //!< Specifies write permission for currently opened XML file - std::string _fileName; //!< Name of the currently opened XML file - - static const std::string _textSeparator; //!< Character sequence for separation of text entries - static const std::string _dimsSeparator; //!< Character sequence for separation of dimensions - - static const std::string _nodeRoot; //!< Name used for the root node - static const std::string _nodeGrp; //!< Name used for creation of 'group' nodes - static const std::string _nodeDset; //!< Name used for creation of 'dataset' nodes - static const std::string _attrName; //!< Name used for creation of 'name' attributes - static const std::string _attrRank; //!< Name used for creation of 'rank' attributes - static const std::string _attrDims; //!< Name used for creation of 'dims' attributes - static const std::string _attrType; //!< Name used for creation of 'type' attributes - static const std::string _attrValue; //!< Name used for creation of 'value' attributes - - static const std::string _typeChar; //!< Name used for 'type' attributes of type 'char' - static const std::string _typeInt; //!< Name used for 'type' attributes of type 'int' - static const std::string _typeUint64; //!< Name used for 'type' attributes of type 'uint64_t' - static const std::string _typeDouble; //!< Name used for 'type' attributes of type 'double' - static const std::string _typeBool; //!< Name used for 'type' attributes of type 'bool' - - xpath_node _groupOpened; //!< Holds the group that is currently opened - std::stack _groupExists; //!< Stack holding the last successfully opened group - std::vector _groupNames; //!< Vector of group names for the currently selected group +protected: + xml_document _doc; //!< XML document + xml_node _root; //!< XML root node + bool _enable_write; //!< Specifies write permission for currently opened XML file + std::string _fileName; //!< Name of the currently opened XML file + + static const std::string _textSeparator; //!< Character sequence for separation of text entries + static const std::string _dimsSeparator; //!< Character sequence for separation of dimensions + + static const std::string _nodeRoot; //!< Name used for the root node + static const std::string _nodeGrp; //!< Name used for creation of 'group' nodes + static const std::string _nodeDset; //!< Name used for creation of 'dataset' nodes + + static const std::string _attrName; //!< Name used for creation of 'name' attributes + static const std::string _attrRank; //!< Name used for creation of 'rank' attributes + static const std::string _attrDims; //!< Name used for creation of 'dims' attributes + static const std::string _attrType; //!< Name used for creation of 'type' attributes + static const std::string _attrValue; //!< Name used for creation of 'value' attributes + + static const std::string _typeChar; //!< Name used for 'type' attributes of type 'char' + static const std::string _typeInt; //!< Name used for 'type' attributes of type 'int' + static const std::string _typeUint64; //!< Name used for 'type' attributes of type 'uint64_t' + static const std::string _typeDouble; //!< Name used for 'type' attributes of type 'double' + static const std::string _typeBool; //!< Name used for 'type' attributes of type 'bool' + + xpath_node _groupOpened; //!< Holds the group that is currently opened + std::stack _groupExists; //!< Stack holding the last successfully opened group + std::vector _groupNames; //!< Vector of group names for the currently selected group void openGroup(bool forceCreation = false); void closeGroup(); void findOrCreateRootNode(); - template - bool isDataType(const std::string& elementName, const std::string& typeName); + template bool isDataType(const std::string& elementName, const std::string& typeName); // Some helper methods std::vector& split(const std::string& s, const char* delim, std::vector& elems); std::vector split(const std::string& s, const char* delim); }; - // Setting constant values const std::string XMLBase::_textSeparator = ", "; const std::string XMLBase::_dimsSeparator = "x"; -const std::string XMLBase::_nodeRoot = "cadet"; -const std::string XMLBase::_nodeGrp = "group"; -const std::string XMLBase::_nodeDset = "dataset"; +const std::string XMLBase::_nodeRoot = "cadet"; +const std::string XMLBase::_nodeGrp = "group"; +const std::string XMLBase::_nodeDset = "dataset"; -const std::string XMLBase::_attrName = "name"; -const std::string XMLBase::_attrRank = "rank"; -const std::string XMLBase::_attrDims = "dims"; -const std::string XMLBase::_attrType = "type"; +const std::string XMLBase::_attrName = "name"; +const std::string XMLBase::_attrRank = "rank"; +const std::string XMLBase::_attrDims = "dims"; +const std::string XMLBase::_attrType = "type"; const std::string XMLBase::_attrValue = "value"; -const std::string XMLBase::_typeChar = "char"; -const std::string XMLBase::_typeInt = "int"; +const std::string XMLBase::_typeChar = "char"; +const std::string XMLBase::_typeInt = "int"; const std::string XMLBase::_typeUint64 = "uint64_t"; const std::string XMLBase::_typeDouble = "double"; -const std::string XMLBase::_typeBool = "bool"; - +const std::string XMLBase::_typeBool = "bool"; -XMLBase::XMLBase() : - _enable_write(false) -{} +XMLBase::XMLBase() : _enable_write(false) +{ +} XMLBase::~XMLBase() CADET_NOEXCEPT { closeFile(); } - - void XMLBase::openFile(const std::string& fileName, const std::string& mode) { xml_parse_result flag; - if (mode == "r" ) // open in read mode + if (mode == "r") // open in read mode { - if (!_doc.load_file(fileName.c_str())) + if (!_doc.load_file(fileName.c_str())) throw IOException("XML file does not exist!"); _enable_write = false; } else if (mode == "rw") // open in read / write mode { - if (!_doc.load_file(fileName.c_str())) + if (!_doc.load_file(fileName.c_str())) throw IOException("XML file does not exist!"); _enable_write = true; } - else if (mode == "c" ) // create new file + else if (mode == "c") // create new file { - if (_doc.load_file(fileName.c_str())) + if (_doc.load_file(fileName.c_str())) throw IOException("XML file already exists"); _enable_write = true; } @@ -202,14 +233,12 @@ void XMLBase::openFile(const std::string& fileName, const std::string& mode) _fileName = fileName; } - void XMLBase::closeFile() { - if (_enable_write) + if (_enable_write) _doc.save_file(_fileName.c_str()); } - bool XMLBase::exists(const char* elementName) { openGroup(); @@ -218,7 +247,6 @@ bool XMLBase::exists(const char* elementName) return exists; } - bool XMLBase::isVector(const char* elementName) { openGroup(); @@ -233,7 +261,7 @@ bool XMLBase::isVector(const char* elementName) } // Read text and attributes - std::size_t rank = dataset.attribute(_attrRank.c_str()).as_int(); + std::size_t rank = dataset.attribute(_attrRank.c_str()).as_int(); std::string dims_str = dataset.attribute(_attrDims.c_str()).value(); // Get dims and compute buffer size @@ -248,14 +276,13 @@ bool XMLBase::isVector(const char* elementName) return items > 1; } - -template -bool XMLBase::isDataType(const std::string& elementName, const std::string& typeName) +template bool XMLBase::isDataType(const std::string& elementName, const std::string& typeName) { openGroup(); // Open dataset and throw if it does not exist - xml_node dataset = _groupOpened.node().find_child_by_attribute(_nodeDset.c_str(), _attrName.c_str(), elementName.c_str()); + xml_node dataset = + _groupOpened.node().find_child_by_attribute(_nodeDset.c_str(), _attrName.c_str(), elementName.c_str()); if (!dataset) { std::ostringstream oss; @@ -284,21 +311,21 @@ bool XMLBase::isDataType(const std::string& elementName, const std::string& type return result; } - bool XMLBase::isGroup(const std::string& elementName) { openGroup(); - xml_node dataset = _groupOpened.node().find_child_by_attribute(_nodeGrp.c_str(), _attrName.c_str(), elementName.c_str()); + xml_node dataset = + _groupOpened.node().find_child_by_attribute(_nodeGrp.c_str(), _attrName.c_str(), elementName.c_str()); closeGroup(); return !!dataset; } - std::vector XMLBase::tensorDimensions(const std::string& elementName) { // Open dataset and throw if it does not exist - xml_node dataset = _groupOpened.node().find_child_by_attribute(_nodeDset.c_str(), _attrName.c_str(), elementName.c_str()); + xml_node dataset = + _groupOpened.node().find_child_by_attribute(_nodeDset.c_str(), _attrName.c_str(), elementName.c_str()); if (!dataset) { std::ostringstream oss; @@ -320,11 +347,11 @@ std::vector XMLBase::tensorDimensions(const std::string& elementNam return dims; } - std::size_t XMLBase::arraySize(const std::string& elementName) { // Open dataset and throw if it does not exist - xml_node dataset = _groupOpened.node().find_child_by_attribute(_nodeDset.c_str(), _attrName.c_str(), elementName.c_str()); + xml_node dataset = + _groupOpened.node().find_child_by_attribute(_nodeDset.c_str(), _attrName.c_str(), elementName.c_str()); if (!dataset) { std::ostringstream oss; @@ -349,7 +376,6 @@ std::size_t XMLBase::arraySize(const std::string& elementName) return n; } - int XMLBase::numItems() { openGroup(); @@ -362,11 +388,10 @@ int XMLBase::numItems() return n; } - std::string XMLBase::itemName(int n) { openGroup(); - + std::string name = ""; int i = 0; for (xml_node::iterator it = _groupOpened.node().begin(); it != _groupOpened.node().end(); ++it) @@ -384,11 +409,10 @@ std::string XMLBase::itemName(int n) return name; } - std::vector XMLBase::itemNames() { openGroup(); - + std::vector names; for (xml_node::iterator it = _groupOpened.node().begin(); it != _groupOpened.node().end(); ++it) { @@ -400,13 +424,12 @@ std::vector XMLBase::itemNames() return names; } - void XMLBase::setGroup(const std::string& groupName) { _groupNames.clear(); - std::size_t start = 0; - std::size_t end = 0; + std::size_t start = 0; + std::size_t end = 0; std::string delimiter("/"); // Quick return when called with empty group name @@ -414,7 +437,8 @@ void XMLBase::setGroup(const std::string& groupName) return; // Don't care for a preceding delimiter - if (groupName[0] == delimiter[0]) ++start; + if (groupName[0] == delimiter[0]) + ++start; while (end != std::string::npos) { @@ -428,23 +452,21 @@ void XMLBase::setGroup(const std::string& groupName) } } - void XMLBase::pushGroup(const std::string& groupName) { _groupNames.push_back(groupName); } - void XMLBase::popGroup() { _groupNames.pop_back(); } - void XMLBase::openGroup(bool forceCreation) { // Clear existent group cache - while (_groupExists.size() > 0) _groupExists.pop(); + while (_groupExists.size() > 0) + _groupExists.pop(); // Generate xpath query string std::ostringstream query(std::ostringstream::ate); @@ -452,7 +474,10 @@ void XMLBase::openGroup(bool forceCreation) { query << "/" << _nodeGrp << "[@" << _attrName << "='" << *it << "']"; // Store query string of parent groups for potential later usage - if (_root.select_single_node(("/" + _nodeRoot + query.str()).c_str())) { _groupExists.push(query.str()); } + if (_root.select_single_node(("/" + _nodeRoot + query.str()).c_str())) + { + _groupExists.push(query.str()); + } } if (_groupNames.empty()) @@ -473,7 +498,8 @@ void XMLBase::openGroup(bool forceCreation) xml_node newNode; std::size_t ngrpexist = _groupExists.size(); // Check for any parent group to be existent - for (std::vector::const_iterator it = _groupNames.begin() + ngrpexist; it < _groupNames.end(); ++it) + for (std::vector::const_iterator it = _groupNames.begin() + ngrpexist; it < _groupNames.end(); + ++it) { query.str(""); // Select the least group @@ -484,7 +510,8 @@ void XMLBase::openGroup(bool forceCreation) newNode = _groupOpened.node().append_child(_nodeGrp.c_str()); query << _groupExists.top(); } - else newNode = _root.append_child(_nodeGrp.c_str()); + else + newNode = _root.append_child(_nodeGrp.c_str()); // Set attribute name newNode.append_attribute(_attrName.c_str()) = it->c_str(); @@ -497,19 +524,17 @@ void XMLBase::openGroup(bool forceCreation) _groupOpened = _root.select_single_node(("/" + _nodeRoot + query.str()).c_str()); } else - throw IOException("Group was not opened/created! Either not existent, creation not forced or file not opened in write mode"); + throw IOException("Group was not opened/created! Either not existent, creation not forced or file not " + "opened in write mode"); } } - - void XMLBase::closeGroup() { - while (_groupExists.size() > 0) _groupExists.pop(); + while (_groupExists.size() > 0) + _groupExists.pop(); } - - void XMLBase::findOrCreateRootNode() { xpath_node rootNode = _doc.select_single_node(("/" + _nodeRoot).c_str()); @@ -525,13 +550,12 @@ void XMLBase::findOrCreateRootNode() } } - std::vector& XMLBase::split(const std::string& s, const char* delim, std::vector& elems) { std::stringstream ss(s); std::string item; - while(std::getline(ss, item, *delim)) + while (std::getline(ss, item, *delim)) elems.push_back(item); return elems; @@ -543,10 +567,8 @@ std::vector XMLBase::split(const std::string& s, const char* delim) return split(s, delim, elems); } - } // namespace io } // namespace cadet - #endif /* XMLBASE_HPP_ */ diff --git a/include/io/xml/XMLReader.hpp b/include/io/xml/XMLReader.hpp index 7fecae60c..8328b7944 100644 --- a/include/io/xml/XMLReader.hpp +++ b/include/io/xml/XMLReader.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -16,7 +16,7 @@ #include "cadet/cadetCompilerInfo.hpp" #include "XMLBase.hpp" -namespace cadet +namespace cadet { namespace io @@ -34,81 +34,68 @@ class XMLReader : public XMLBase ~XMLReader() CADET_NOEXCEPT; /// \brief Convenience wrapper for reading vectors - template - std::vector vector(const std::string& dataSetName); + template std::vector vector(const std::string& dataSetName); /// \brief Convenience wrapper for reading scalars - template - T scalar(const std::string& dataSetName, std::size_t position = 0); + template T scalar(const std::string& dataSetName, std::size_t position = 0); private: - - template - std::vector read(const std::string& dataSetName); - + template std::vector read(const std::string& dataSetName); }; +XMLReader::XMLReader() +{ +} -XMLReader::XMLReader() { } - -XMLReader::~XMLReader() CADET_NOEXCEPT { } - - +XMLReader::~XMLReader() CADET_NOEXCEPT +{ +} // ============================================================================================================ // Template specializations of member functions for diffenet data types // ============================================================================================================ // Double specialization of vector() -template <> -std::vector XMLReader::vector(const std::string& dataSetName) +template <> std::vector XMLReader::vector(const std::string& dataSetName) { return read(dataSetName); } // Integer specializations of vector() -template <> -std::vector XMLReader::vector(const std::string& dataSetName) +template <> std::vector XMLReader::vector(const std::string& dataSetName) { return read(dataSetName); } -template <> -std::vector XMLReader::vector(const std::string& dataSetName) +template <> std::vector XMLReader::vector(const std::string& dataSetName) { return read(dataSetName); } // std::string specialization of vector() -template <> -std::vector XMLReader::vector(const std::string& dataSetName) +template <> std::vector XMLReader::vector(const std::string& dataSetName) { return read(dataSetName); } // Template that matches on every unsupported type and throws an exception -template -std::vector XMLReader::vector(const std::string& dataSetName) +template std::vector XMLReader::vector(const std::string& dataSetName) { throw IOException("You may not try to read an unsupported type"); } // ============================================================================================================ - - -template -T XMLReader::scalar(const std::string& dataSetName, std::size_t position) +template T XMLReader::scalar(const std::string& dataSetName, std::size_t position) { return vector(dataSetName).at(position); } - -template -std::vector XMLReader::read(const std::string& dataSetName) +template std::vector XMLReader::read(const std::string& dataSetName) { openGroup(); // Open dataset and throw if it does not exist - xml_node dataset = _groupOpened.node().find_child_by_attribute(_nodeDset.c_str(), _attrName.c_str(), dataSetName.c_str()); + xml_node dataset = + _groupOpened.node().find_child_by_attribute(_nodeDset.c_str(), _attrName.c_str(), dataSetName.c_str()); if (!dataset) { std::ostringstream oss; @@ -118,7 +105,7 @@ std::vector XMLReader::read(const std::string& dataSetName) // Read text and attributes std::string data_str = dataset.text().get(); - std::size_t rank = dataset.attribute(_attrRank.c_str()).as_int(); + std::size_t rank = dataset.attribute(_attrRank.c_str()).as_int(); std::string dims_str = dataset.attribute(_attrDims.c_str()).value(); // Get dims and compute buffer size @@ -131,14 +118,15 @@ std::vector XMLReader::read(const std::string& dataSetName) ss >> dims[i]; bufSize *= dims[i]; } - delete [] dims; + delete[] dims; // Split data and convert to right type std::vector data_vec = split(data_str, _textSeparator.c_str()); if (bufSize != data_vec.size()) { std::ostringstream oss; - oss << "XML file is inconsistent: Possibly wrong no. of entrys in dataset '" << dataset.attribute(_attrName.c_str()).value() << "'"; + oss << "XML file is inconsistent: Possibly wrong no. of entrys in dataset '" + << dataset.attribute(_attrName.c_str()).value() << "'"; throw IOException(oss.str()); } @@ -157,5 +145,4 @@ std::vector XMLReader::read(const std::string& dataSetName) } // namespace cadet - #endif /* XMLREADER_HPP_ */ diff --git a/include/io/xml/XMLWriter.hpp b/include/io/xml/XMLWriter.hpp index fd24daa40..e33870331 100644 --- a/include/io/xml/XMLWriter.hpp +++ b/include/io/xml/XMLWriter.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -30,7 +30,6 @@ using namespace pugi; class XMLWriter : public XMLBase { public: - /// \brief Constructor XMLWriter(); @@ -43,102 +42,122 @@ class XMLWriter : public XMLBase /// \brief Write data from C-array to a dataset template - void write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, const std::size_t stride, const std::size_t blockSize); + void write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, + const std::size_t stride, const std::size_t blockSize); /// \brief Convenience wrapper for writing tensors from C-array template - void tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); + void tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, + const std::size_t stride = 1, const std::size_t blockSize = 1); /// \brief Convenience wrapper for writing tensors from std::vector template - void tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const std::vector& buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); + void tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const std::vector& buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); /// \brief Convenience wrapper for writing matrices from C-array template - void matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const T* buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); + void matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const T* buffer, + const std::size_t stride = 1, const std::size_t blockSize = 1); /// \brief Convenience wrapper for writing matrices from std::vector template - void matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const std::vector& buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); + void matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const std::vector& buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); /// \brief Convenience wrapper for writing vectors from C-array template - void vector(const std::string& dataSetName, const std::size_t length, const T* buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); + void vector(const std::string& dataSetName, const std::size_t length, const T* buffer, const std::size_t stride = 1, + const std::size_t blockSize = 1); /// \brief Convenience wrapper for writing vectors from std::vector template - void vector(const std::string& dataSetName, const std::vector& buffer, const std::size_t stride = 1, const std::size_t blockSize = 1); + void vector(const std::string& dataSetName, const std::vector& buffer, const std::size_t stride = 1, + const std::size_t blockSize = 1); /// \brief Convenience wrapper for writing scalars - template - void scalar(const std::string& dataSetName, const T buffer); + template void scalar(const std::string& dataSetName, const T buffer); /// \brief This functionality is not supported by XML - this is a stub. /// Removes an existing group from the file. - inline void unlinkGroup(const std::string& groupName) {} + inline void unlinkGroup(const std::string& groupName) + { + } /// \brief This functionality is not supported by XML - this is a stub. /// Removes an existing dataset from the current group. - inline void unlinkDataset(const std::string& dsName) {} + inline void unlinkDataset(const std::string& dsName) + { + } /// \brief This functionality is not supported by XML - this is a stub. /// Set the level of compression used for tensors of 2nd order and above - inline void compressFields(bool setCompression) {} + inline void compressFields(bool setCompression) + { + } /// \brief This functionality is not supported by XML - this is a stub. /// Tensors of 2nd order (vectors) and above are written as extendible fields /// (maxsize = unlimited, chunked layout), when set to true. - inline void extendibleFields(bool setExtendible) {} + inline void extendibleFields(bool setExtendible) + { + } private: - - std::string _typeName; //!< Name of the type to be written + std::string _typeName; //!< Name of the type to be written template - void writeWork(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, const std::size_t stride, const std::size_t blockSize); + void writeWork(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, + const std::size_t stride, const std::size_t blockSize); }; +XMLWriter::XMLWriter() +{ +} -XMLWriter::XMLWriter() { } - -XMLWriter::~XMLWriter() CADET_NOEXCEPT { } - +XMLWriter::~XMLWriter() CADET_NOEXCEPT +{ +} // ============================================================================================================ // Template specializations of member function write() for diffenet data types // ============================================================================================================ template <> -void XMLWriter::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const double* buffer, const std::size_t stride, const std::size_t blockSize) +void XMLWriter::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const double* buffer, const std::size_t stride, const std::size_t blockSize) { _typeName = _typeDouble; writeWork(dataSetName, rank, dims, buffer, stride, blockSize); } template <> -void XMLWriter::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const int* buffer, const std::size_t stride, const std::size_t blockSize) +void XMLWriter::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const int* buffer, const std::size_t stride, const std::size_t blockSize) { _typeName = _typeUint64; writeWork(dataSetName, rank, dims, buffer, stride, blockSize); } template <> -void XMLWriter::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const uint64_t* buffer, const std::size_t stride, const std::size_t blockSize) +void XMLWriter::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const uint64_t* buffer, const std::size_t stride, const std::size_t blockSize) { _typeName = _typeInt; writeWork(dataSetName, rank, dims, buffer, stride, blockSize); } -//template <> -//void XMLWriter::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const bool* buffer) +// template <> +// void XMLWriter::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const +// bool* buffer) //{ -// _typeName = _typeBool; -// writeWork(dataSetName, rank, dims, buffer); -//} - +// _typeName = _typeBool; +// writeWork(dataSetName, rank, dims, buffer); +// } template <> -void XMLWriter::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const std::string* buffer, const std::size_t stride, const std::size_t blockSize) +void XMLWriter::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const std::string* buffer, const std::size_t stride, const std::size_t blockSize) { // Compute bufSize and create charBuf std::size_t bufSize = 1; @@ -162,7 +181,6 @@ void XMLWriter::write(const std::string& dataSetName, const std::si delete[] charBuf; } - // Template that matches on every unsupported type and throws an exception template void XMLWriter::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer) @@ -171,20 +189,19 @@ void XMLWriter::write(const std::string& dataSetName, const std::size_t rank, co } template -void XMLWriter::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, const std::size_t stride, const std::size_t blockSize) +void XMLWriter::write(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, + const std::size_t stride, const std::size_t blockSize) { throw IOException("You may not try to write an unsupported type"); } // ============================================================================================================ - - - // ============================================================================================================ // Convenience wrappers // ============================================================================================================ template -void XMLWriter::tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const std::vector& buffer, const std::size_t stride, const std::size_t blockSize) +void XMLWriter::tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const std::vector& buffer, const std::size_t stride, const std::size_t blockSize) { #ifdef CADET_DEBUG std::size_t bufSize = 1; @@ -196,57 +213,58 @@ void XMLWriter::tensor(const std::string& dataSetName, const std::size_t rank, c } template -void XMLWriter::tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, const std::size_t stride, const std::size_t blockSize) +void XMLWriter::tensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, + const std::size_t stride, const std::size_t blockSize) { write(dataSetName, rank, dims, buffer, stride, blockSize); } template -void XMLWriter::matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const T* buffer, const std::size_t stride, const std::size_t blockSize) +void XMLWriter::matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const T* buffer, + const std::size_t stride, const std::size_t blockSize) { std::size_t dims[2] = {rows, cols}; write(dataSetName, 2, dims, buffer, stride, blockSize); } template -void XMLWriter::matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const std::vector& buffer, const std::size_t stride, const std::size_t blockSize) +void XMLWriter::matrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const std::vector& buffer, const std::size_t stride, const std::size_t blockSize) { - cadet_assert(rows*cols <= buffer.size()); + cadet_assert(rows * cols <= buffer.size()); std::size_t dims[2] = {rows, cols}; write(dataSetName, 2, dims, buffer.data(), stride, blockSize); } template -void XMLWriter::vector(const std::string& dataSetName, const std::size_t length, const T* buffer, const std::size_t stride, const std::size_t blockSize) +void XMLWriter::vector(const std::string& dataSetName, const std::size_t length, const T* buffer, + const std::size_t stride, const std::size_t blockSize) { write(dataSetName, 1, &length, buffer, stride, blockSize); } template -void XMLWriter::vector(const std::string& dataSetName, const std::vector& buffer, const std::size_t stride, const std::size_t blockSize) +void XMLWriter::vector(const std::string& dataSetName, const std::vector& buffer, const std::size_t stride, + const std::size_t blockSize) { std::size_t length = buffer.size() / stride; write(dataSetName, 1, &length, buffer.data(), stride, blockSize); } -template -void XMLWriter::scalar(const std::string& dataSetName, const T buffer) +template void XMLWriter::scalar(const std::string& dataSetName, const T buffer) { vector(dataSetName, 1, &buffer); } // ============================================================================================================ - - - // ============================================================================================================ // Private members // ============================================================================================================ - // This method can only work on template parameters of type: const char_t*, int, unsigned int, double and bool! template -void XMLWriter::writeWork(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const T* buffer, const std::size_t stride, const std::size_t blockSize) +void XMLWriter::writeWork(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const T* buffer, const std::size_t stride, const std::size_t blockSize) { openGroup(true); @@ -263,27 +281,28 @@ void XMLWriter::writeWork(const std::string& dataSetName, const std::size_t rank } else { - for (std::size_t i = 0; i < rank-1; ++i) + for (std::size_t i = 0; i < rank - 1; ++i) { dims_str << dims[i] << _dimsSeparator; bufSize *= dims[i]; } - dims_str << dims[rank-1]; - bufSize *= dims[rank-1]; + dims_str << dims[rank - 1]; + bufSize *= dims[rank - 1]; } // Create the text-string std::ostringstream text_str; text_str << std::setprecision(16); - for (std::size_t i = 0; i < (bufSize-1) / blockSize; ++i) + for (std::size_t i = 0; i < (bufSize - 1) / blockSize; ++i) for (std::size_t j = 0; j < blockSize; ++j) text_str << buffer[i * stride + j] << _textSeparator; for (std::size_t j = 0; j < blockSize - 1; ++j) - text_str << buffer[(bufSize-1) * stride + j] << _textSeparator; - text_str << buffer[(bufSize-1) * stride + blockSize - 1]; + text_str << buffer[(bufSize - 1) * stride + j] << _textSeparator; + text_str << buffer[(bufSize - 1) * stride + blockSize - 1]; - xml_node dataset = _groupOpened.node().find_child_by_attribute(_nodeDset.c_str(), _attrName.c_str(), dataSetName.c_str()); + xml_node dataset = + _groupOpened.node().find_child_by_attribute(_nodeDset.c_str(), _attrName.c_str(), dataSetName.c_str()); if (dataset) { dataset.attribute(_attrType.c_str()) = _typeName.c_str(); @@ -305,7 +324,6 @@ void XMLWriter::writeWork(const std::string& dataSetName, const std::size_t rank closeGroup(); } - } // namespace io } // namespace cadet diff --git a/src/build-tools/CMakeLists.txt b/src/build-tools/CMakeLists.txt index 991736fc5..05e94b5de 100644 --- a/src/build-tools/CMakeLists.txt +++ b/src/build-tools/CMakeLists.txt @@ -1,9 +1,9 @@ # ============================================================================= # CADET -# +# # Copyright © The CADET Authors # Please see the CONTRIBUTORS.md file. -# +# # All rights reserved. This program and the accompanying materials # are made available under the terms of the GNU Public License v3.0 (or, at # your option, any later version) which accompanies this distribution, and @@ -21,4 +21,3 @@ set_target_properties(templateCodeGen PROPERTIES CXX_EXTENSIONS OFF) # Info message message(STATUS "Added build tools") - diff --git a/src/build-tools/templateCodeGen.cpp b/src/build-tools/templateCodeGen.cpp index e1cebf7c1..afb88479f 100644 --- a/src/build-tools/templateCodeGen.cpp +++ b/src/build-tools/templateCodeGen.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -24,7 +24,7 @@ * i.e., {slash}* and *{slash}. Each block is assumed to contain a JSON object that serves as data * for an inja template given in a separate file. The blocks are replaced by their template instantiations * and the result is written to file. - * + * * The template is taken from a file after the marker {slash}* *{slash}. If the marker is not * found, the full file is taken as template. */ @@ -37,9 +37,9 @@ std::string readFile(const std::string& fileName) { std::ifstream in(fileName); - std::ostringstream sstr; - sstr << in.rdbuf(); - return sstr.str(); + std::ostringstream sstr; + sstr << in.rdbuf(); + return sstr.str(); } /** @@ -111,9 +111,11 @@ int main(int argc, char** argv) std::ostringstream output; output << R"header(/*********************************************************** * DO NOT EDIT. This file was generated by templateCodeGen from - * )header" << dataFileName << R"header( - * using template - * )header" << templateFileName << R"header( + * )header" + << dataFileName << R"header( + * using template + * )header" + << templateFileName << R"header( * Please edit either the data or the applied template. **********************************************************/ )header" << "\n"; diff --git a/src/cadet-cli/CMakeLists.txt b/src/cadet-cli/CMakeLists.txt index ad194315d..1a0c66a9e 100644 --- a/src/cadet-cli/CMakeLists.txt +++ b/src/cadet-cli/CMakeLists.txt @@ -1,9 +1,9 @@ # ============================================================================= # CADET -# +# # Copyright © The CADET Authors # Please see the CONTRIBUTORS.md file. -# +# # All rights reserved. This program and the accompanying materials # are made available under the terms of the GNU Public License v3.0 (or, at # your option, any later version) which accompanies this distribution, and @@ -42,7 +42,7 @@ else() ) endif() endif() - + # Add include directories for access to exported LIBCADET header files. target_include_directories(cadet-cli PRIVATE ${CMAKE_SOURCE_DIR}/ThirdParty/pugixml ${CMAKE_SOURCE_DIR}/ThirdParty/json ${CMAKE_SOURCE_DIR}/ThirdParty/tclap/include ${CMAKE_BINARY_DIR}) diff --git a/src/cadet-cli/Logging.hpp b/src/cadet-cli/Logging.hpp index 470af8f51..c1742d4ab 100644 --- a/src/cadet-cli/Logging.hpp +++ b/src/cadet-cli/Logging.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Logging configuration for cadet-cli. */ @@ -25,41 +25,42 @@ namespace cadet namespace log { - /** - * @brief Implements a standard formatting policy - */ - class CadetCliFormattingPolicy : public FormattingPolicyBase +/** + * @brief Implements a standard formatting policy + */ +class CadetCliFormattingPolicy : public FormattingPolicyBase +{ +public: + template + static inline void format(receiver_t& recv, const char* fileName, const char* funcName, unsigned int line, + LogLevel lvl, const paramList_t& p) { - public: - template - static inline void format(receiver_t& recv, const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, const paramList_t& p) - { - writeObj(recv, lvl, '['); - writeObj(recv, lvl, to_string(lvl)); - writeObj(recv, lvl, ": "); -// writeObj(recv, lvl, fileName); -// writeObj(recv, lvl, "::"); - writeObj(recv, lvl, funcName); - writeObj(recv, lvl, "::"); - writeObj(recv, lvl, line); - writeObj(recv, lvl, "] "); - writeParams(recv, lvl, p); - } - }; + writeObj(recv, lvl, '['); + writeObj(recv, lvl, to_string(lvl)); + writeObj(recv, lvl, ": "); + // writeObj(recv, lvl, fileName); + // writeObj(recv, lvl, "::"); + writeObj(recv, lvl, funcName); + writeObj(recv, lvl, "::"); + writeObj(recv, lvl, line); + writeObj(recv, lvl, "] "); + writeParams(recv, lvl, p); + } +}; - typedef NonFilteringLogger GlobalLogger; +typedef NonFilteringLogger GlobalLogger; #ifndef CADET_LOGGING_DISABLE - typedef Logger, LogLevel::CADET_LOGLEVEL_MIN> DoubleFilterLogger; +typedef Logger, LogLevel::CADET_LOGLEVEL_MIN> DoubleFilterLogger; - #ifdef __clang__ - // Silence -Wundefined-var-template warning by indicating an - // explicit instantiation of this template in another translation unit - template<> LogLevel RuntimeFilteringLogger::_minLvl; - extern template class RuntimeFilteringLogger; - #endif +#ifdef __clang__ +// Silence -Wundefined-var-template warning by indicating an +// explicit instantiation of this template in another translation unit +template <> LogLevel RuntimeFilteringLogger::_minLvl; +extern template class RuntimeFilteringLogger; +#endif #else - typedef Logger DiscardingLogger; +typedef Logger DiscardingLogger; #endif } // namespace log @@ -67,24 +68,28 @@ namespace log #ifndef CADET_LOGGING_DISABLE - /** - * @brief Base for logging macros - * @details Note that because of the usage pattern - *
LOG(Info) << "My log line " << arg1;
- * no semicolon is appended. - */ - #define LOG(lvl) cadet::log::DoubleFilterLogger::statement(__FILE__, __func__, __LINE__) = cadet::log::DoubleFilterLogger::template createMessage() +/** + * @brief Base for logging macros + * @details Note that because of the usage pattern + *
LOG(Info) << "My log line " << arg1;
+ * no semicolon is appended. + */ +#define LOG(lvl) \ + cadet::log::DoubleFilterLogger::statement(__FILE__, __func__, __LINE__) = \ + cadet::log::DoubleFilterLogger::template createMessage() #else - /** - * @brief Base for logging macros - * @details Note that because of the usage pattern - *
LOG(Info) << "My log line " << arg1;
- * no semicolon is appended. - */ - #define LOG(lvl) cadet::log::DiscardingLogger::statement(__FILE__, __func__, __LINE__) = cadet::log::DiscardingLogger::template createMessage() +/** + * @brief Base for logging macros + * @details Note that because of the usage pattern + *
LOG(Info) << "My log line " << arg1;
+ * no semicolon is appended. + */ +#define LOG(lvl) \ + cadet::log::DiscardingLogger::statement(__FILE__, __func__, __LINE__) = \ + cadet::log::DiscardingLogger::template createMessage() #endif -#endif // CADETCLI_LOGGING_IMPL_HPP_ +#endif // CADETCLI_LOGGING_IMPL_HPP_ diff --git a/src/cadet-cli/ProgressBar.cpp b/src/cadet-cli/ProgressBar.cpp index 2af725f8f..ec325fb69 100644 --- a/src/cadet-cli/ProgressBar.cpp +++ b/src/cadet-cli/ProgressBar.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -41,76 +41,76 @@ bool isStdOutAttachedToTerminal(); bool isStdErrAttachedToTerminal(); #ifdef _WIN32 - // Windows (x64 and x86) +// Windows (x64 and x86) - #define NOMINMAX - #define _WINDOWS - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif +#define NOMINMAX +#define _WINDOWS +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif - #include - #include - #include +#include +#include +#include - ConsoleSize getConsoleSize(StandardStream ss) - { - CONSOLE_SCREEN_BUFFER_INFO csbi; - ConsoleSize cs; +ConsoleSize getConsoleSize(StandardStream ss) +{ + CONSOLE_SCREEN_BUFFER_INFO csbi; + ConsoleSize cs; - if (ss == StandardStream::Out) - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); - else if (ss == StandardStream::Err) - GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi); + if (ss == StandardStream::Out) + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + else if (ss == StandardStream::Err) + GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi); - cs.columns = csbi.srWindow.Right - csbi.srWindow.Left + 1; - cs.rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; - return cs; - } + cs.columns = csbi.srWindow.Right - csbi.srWindow.Left + 1; + cs.rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; + return cs; +} - bool isStdOutAttachedToTerminal() - { - DWORD st; - const int fn = _fileno(stdout); - HANDLE hd = reinterpret_cast(_get_osfhandle(fn)); - return _isatty(fn) && (hd != INVALID_HANDLE_VALUE) && GetConsoleMode(hd, &st); - } +bool isStdOutAttachedToTerminal() +{ + DWORD st; + const int fn = _fileno(stdout); + HANDLE hd = reinterpret_cast(_get_osfhandle(fn)); + return _isatty(fn) && (hd != INVALID_HANDLE_VALUE) && GetConsoleMode(hd, &st); +} - bool isStdErrAttachedToTerminal() - { - DWORD st; - const int fn = _fileno(stderr); - HANDLE hd = reinterpret_cast(_get_osfhandle(fn)); - return _isatty(fn) && (hd != INVALID_HANDLE_VALUE) && GetConsoleMode(hd, &st); - } +bool isStdErrAttachedToTerminal() +{ + DWORD st; + const int fn = _fileno(stderr); + HANDLE hd = reinterpret_cast(_get_osfhandle(fn)); + return _isatty(fn) && (hd != INVALID_HANDLE_VALUE) && GetConsoleMode(hd, &st); +} #elif __unix__ || __linux__ || __APPLE__ - // Linux, BSD, Mac OSX +// Linux, BSD, Mac OSX - #include - #include - #include +#include +#include +#include - ConsoleSize getConsoleSize(StandardStream ss) - { - winsize w; - if (ss == StandardStream::Out) - ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); - else if (ss == StandardStream::Err) - ioctl(STDERR_FILENO, TIOCGWINSZ, &w); +ConsoleSize getConsoleSize(StandardStream ss) +{ + winsize w; + if (ss == StandardStream::Out) + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + else if (ss == StandardStream::Err) + ioctl(STDERR_FILENO, TIOCGWINSZ, &w); - return ConsoleSize{ w.ws_row, w.ws_col}; - } + return ConsoleSize{w.ws_row, w.ws_col}; +} - bool isStdOutAttachedToTerminal() - { - return isatty(fileno(stdout)); - } +bool isStdOutAttachedToTerminal() +{ + return isatty(fileno(stdout)); +} - bool isStdErrAttachedToTerminal() - { - return isatty(fileno(stderr)); - } +bool isStdErrAttachedToTerminal() +{ + return isatty(fileno(stderr)); +} #endif @@ -123,177 +123,178 @@ bool isStdErrAttachedToTerminal(); namespace cadet { - ProgressBar::ProgressBar() : _os(nullptr), _progress(0.0), _minUpdate(0.005), _minTime(0.1), _buffer(0), _barRatio(0.6) - { - useStdOut(); - } +ProgressBar::ProgressBar() : _os(nullptr), _progress(0.0), _minUpdate(0.005), _minTime(0.1), _buffer(0), _barRatio(0.6) +{ + useStdOut(); +} - ProgressBar::~ProgressBar() CADET_NOEXCEPT - { - } +ProgressBar::~ProgressBar() CADET_NOEXCEPT +{ +} - ProgressBar& ProgressBar::minProgressUpdate(double minUpdate) - { - _minUpdate = minUpdate; - return *this; - } +ProgressBar& ProgressBar::minProgressUpdate(double minUpdate) +{ + _minUpdate = minUpdate; + return *this; +} - ProgressBar& ProgressBar::minTimeUpdate(double minTime) - { - _minTime = minTime; - return *this; - } +ProgressBar& ProgressBar::minTimeUpdate(double minTime) +{ + _minTime = minTime; + return *this; +} - ProgressBar& ProgressBar::useStdOut() +ProgressBar& ProgressBar::useStdOut() +{ + if (isStdOutAttachedToTerminal()) { - if (isStdOutAttachedToTerminal()) - { - _os = &std::cout; - const ConsoleSize cs = getConsoleSize(StandardStream::Out); - setup(cs.columns); - } - else - _os = nullptr; - - return *this; + _os = &std::cout; + const ConsoleSize cs = getConsoleSize(StandardStream::Out); + setup(cs.columns); } + else + _os = nullptr; - ProgressBar& ProgressBar::useStdErr() - { - if (isStdErrAttachedToTerminal()) - { - _os = &std::cerr; - const ConsoleSize cs = getConsoleSize(StandardStream::Err); - setup(cs.columns); - } - else - _os = nullptr; - - return *this; - } + return *this; +} - ProgressBar& ProgressBar::disable() +ProgressBar& ProgressBar::useStdErr() +{ + if (isStdErrAttachedToTerminal()) { - _os = nullptr; - return *this; + _os = &std::cerr; + const ConsoleSize cs = getConsoleSize(StandardStream::Err); + setup(cs.columns); } + else + _os = nullptr; - ProgressBar& ProgressBar::barWidthRatio(double width) - { - _barRatio = width; - return *this; - } + return *this; +} - void ProgressBar::setup(int columns) - { - // Apply default terminal size - if (columns == 0) - columns = 80; +ProgressBar& ProgressBar::disable() +{ + _os = nullptr; + return *this; +} - _termWidth = columns; +ProgressBar& ProgressBar::barWidthRatio(double width) +{ + _barRatio = width; + return *this; +} - // Add one element for terminal '\0' char, and one for carriage return '\r' - _buffer.resize(columns + 2); +void ProgressBar::setup(int columns) +{ + // Apply default terminal size + if (columns == 0) + columns = 80; - // Add prefix to buffer - std::snprintf(_buffer.data(), _buffer.size(), "\r ["); + _termWidth = columns; - // Add terminal '\0' char - _buffer.back() = '\0'; - } + // Add one element for terminal '\0' char, and one for carriage return '\r' + _buffer.resize(columns + 2); - void ProgressBar::begin() - { - _startTime = std::chrono::steady_clock::now(); - } + // Add prefix to buffer + std::snprintf(_buffer.data(), _buffer.size(), "\r ["); - void ProgressBar::print() const - { - print(std::chrono::steady_clock::now(), true); - } + // Add terminal '\0' char + _buffer.back() = '\0'; +} - void ProgressBar::print(typename std::chrono::steady_clock::time_point curTime, bool arrowTip) const - { - if (cadet_unlikely(!_os)) - return; +void ProgressBar::begin() +{ + _startTime = std::chrono::steady_clock::now(); +} - cadet_assert((_progress >= 0.0) && (_progress <= 1.0)); +void ProgressBar::print() const +{ + print(std::chrono::steady_clock::now(), true); +} - // Extrapolate remaining time - const double elapsedSec = std::chrono::duration_cast(curTime - _startTime).count(); - const double remainingSec = (std::max)(0.0, elapsedSec * (1.0 / (std::max)(_progress, 1e-10) - 1.0)); +void ProgressBar::print(typename std::chrono::steady_clock::time_point curTime, bool arrowTip) const +{ + if (cadet_unlikely(!_os)) + return; - // Compute size for the bar, reserve 4 chars for braces and spaces - const int barSize = _barRatio * (_termWidth - 4); + cadet_assert((_progress >= 0.0) && (_progress <= 1.0)); - // Skip carriage return and prefix - int pos = 3; + // Extrapolate remaining time + const double elapsedSec = std::chrono::duration_cast(curTime - _startTime).count(); + const double remainingSec = (std::max)(0.0, elapsedSec * (1.0 / (std::max)(_progress, 1e-10) - 1.0)); - // Add bar - const int barProgWidth = (std::max)(0.0, _progress * barSize - 1); - char* const ptrBar = _buffer.data() + pos; - for (int i = 0; i < barProgWidth; ++i) - ptrBar[i] = '='; + // Compute size for the bar, reserve 4 chars for braces and spaces + const int barSize = _barRatio * (_termWidth - 4); - // Add the tip - if (arrowTip) - ptrBar[barProgWidth] = '>'; - else - ptrBar[barProgWidth] = '='; + // Skip carriage return and prefix + int pos = 3; - // Fill remaining bar with space - for (int i = barProgWidth + 1; i < barSize; ++i) - ptrBar[i] = ' '; + // Add bar + const int barProgWidth = (std::max)(0.0, _progress * barSize - 1); + char* const ptrBar = _buffer.data() + pos; + for (int i = 0; i < barProgWidth; ++i) + ptrBar[i] = '='; - pos += barSize; - const int textSize = std::snprintf(_buffer.data() + pos, _buffer.size() - pos, "] %4.1f%% (%5.1f>%5.1f s) %s", _progress * 100.0, elapsedSec, remainingSec, _message.c_str()); + // Add the tip + if (arrowTip) + ptrBar[barProgWidth] = '>'; + else + ptrBar[barProgWidth] = '='; - if (textSize + pos < static_cast(_buffer.size()) - 1) - { - // Fill remaining buffer - char* const ptrBuffer = _buffer.data(); - for (int i = textSize + pos; i < static_cast(_buffer.size()) - 1; ++i) - ptrBuffer[i] = ' '; - } + // Fill remaining bar with space + for (int i = barProgWidth + 1; i < barSize; ++i) + ptrBar[i] = ' '; - (*_os) << _buffer.data() << std::flush; - } + pos += barSize; + const int textSize = std::snprintf(_buffer.data() + pos, _buffer.size() - pos, "] %4.1f%% (%5.1f>%5.1f s) %s", + _progress * 100.0, elapsedSec, remainingSec, _message.c_str()); - void ProgressBar::update(char const* message) + if (textSize + pos < static_cast(_buffer.size()) - 1) { - update(_progress, message); + // Fill remaining buffer + char* const ptrBuffer = _buffer.data(); + for (int i = textSize + pos; i < static_cast(_buffer.size()) - 1; ++i) + ptrBuffer[i] = ' '; } - void ProgressBar::update(double progress, char const* message) - { - const double deltaProgress = progress - _progress; - const typename std::chrono::steady_clock::time_point curTime = std::chrono::steady_clock::now(); - const double deltaTime = std::chrono::duration_cast(curTime - _lastUpdateTime).count(); - - // Update if enough progress has been made or enough time has elapsed - if ((deltaTime >= _minTime) || (deltaProgress >= _minUpdate)) - { - _message = message; - _progress = progress; - - print(curTime, true); - _lastUpdateTime = curTime; - } - } + (*_os) << _buffer.data() << std::flush; +} - void ProgressBar::finish() - { - _progress = 1.0; - print(std::chrono::steady_clock::now(), false); +void ProgressBar::update(char const* message) +{ + update(_progress, message); +} - if (_os) - (*_os) << std::endl; - } +void ProgressBar::update(double progress, char const* message) +{ + const double deltaProgress = progress - _progress; + const typename std::chrono::steady_clock::time_point curTime = std::chrono::steady_clock::now(); + const double deltaTime = std::chrono::duration_cast(curTime - _lastUpdateTime).count(); - void ProgressBar::finish(char const* message) + // Update if enough progress has been made or enough time has elapsed + if ((deltaTime >= _minTime) || (deltaProgress >= _minUpdate)) { _message = message; - finish(); + _progress = progress; + + print(curTime, true); + _lastUpdateTime = curTime; } +} + +void ProgressBar::finish() +{ + _progress = 1.0; + print(std::chrono::steady_clock::now(), false); + + if (_os) + (*_os) << std::endl; +} + +void ProgressBar::finish(char const* message) +{ + _message = message; + finish(); +} } // namespace cadet diff --git a/src/cadet-cli/ProgressBar.hpp b/src/cadet-cli/ProgressBar.hpp index 6e27cb896..41f823edf 100644 --- a/src/cadet-cli/ProgressBar.hpp +++ b/src/cadet-cli/ProgressBar.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Progress bar for cadet-cli. */ @@ -114,14 +114,14 @@ class ProgressBar protected: std::ostream* _os; //!< Stream to print to - double _progress; //!< Current progress in percent (between 0.0 and 1.0) + double _progress; //!< Current progress in percent (between 0.0 and 1.0) double _minUpdate; //!< Minimum amount of progress update to trigger printing - double _minTime; //!< Minimum amount of elapsed time between printing - typename std::chrono::steady_clock::time_point _startTime; //!< Start time of process + double _minTime; //!< Minimum amount of elapsed time between printing + typename std::chrono::steady_clock::time_point _startTime; //!< Start time of process typename std::chrono::steady_clock::time_point _lastUpdateTime; //!< Time of last update - int _termWidth; //!< Number of columns of terminal - mutable std::vector _buffer; //!< Buffer for printing the progress bar - std::string _message; //!< Message to display behind the bar + int _termWidth; //!< Number of columns of terminal + mutable std::vector _buffer; //!< Buffer for printing the progress bar + std::string _message; //!< Message to display behind the bar double _barRatio; //!< Percentage of terminal width used for the bar (between 0.0 and 1.0) void setup(int columns); @@ -130,4 +130,4 @@ class ProgressBar } // namespace cadet -#endif // CADETCLI_PROGRESSBAR_HPP_ +#endif // CADETCLI_PROGRESSBAR_HPP_ diff --git a/src/cadet-cli/SignalHandler.cpp b/src/cadet-cli/SignalHandler.cpp index 149f60e36..0d5c1b569 100644 --- a/src/cadet-cli/SignalHandler.cpp +++ b/src/cadet-cli/SignalHandler.cpp @@ -1,104 +1,103 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and // is available at http://www.gnu.org/licenses/gpl.html // ============================================================================= - #ifdef _WIN32 - // Windows (x64 and x86) - - #define NOMINMAX - #define _WINDOWS - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - - #include - #include - - namespace - { - volatile bool stopExecution = false; - - BOOL WINAPI signalHandler(DWORD fdwCtrlType) - { - switch (fdwCtrlType) - { - case CTRL_C_EVENT: - stopExecution = true; - return TRUE; - - case CTRL_CLOSE_EVENT: - stopExecution = true; - return TRUE; - } - - // Pass other signals to the next handler - return FALSE; - } - } // namespace - - namespace cadet - { - bool installSignalHandler() - { - if (SetConsoleCtrlHandler(signalHandler, TRUE)) - return true; - - return false; - } +// Windows (x64 and x86) - bool stopExecutionRequested() - { - return stopExecution; - } - - } // namespace cadet +#define NOMINMAX +#define _WINDOWS +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif -#elif __unix__ || __linux__ || __APPLE__ - // Linux, BSD, Mac OSX +#include +#include - #include - #include +namespace +{ +volatile bool stopExecution = false; - namespace +BOOL WINAPI signalHandler(DWORD fdwCtrlType) +{ + switch (fdwCtrlType) { - volatile sig_atomic_t stopExecution = 0; + case CTRL_C_EVENT: + stopExecution = true; + return TRUE; - void signalHandler(int signal) - { - if (signal == SIGINT) - stopExecution = 1; - } - } // namespace + case CTRL_CLOSE_EVENT: + stopExecution = true; + return TRUE; + } - namespace cadet - { - bool installSignalHandler() - { - struct sigaction sigbreak; - sigbreak.sa_handler = &signalHandler; - sigemptyset(&sigbreak.sa_mask); - sigbreak.sa_flags = 0; + // Pass other signals to the next handler + return FALSE; +} +} // namespace + +namespace cadet +{ +bool installSignalHandler() +{ + if (SetConsoleCtrlHandler(signalHandler, TRUE)) + return true; - if (sigaction(SIGINT, &sigbreak, NULL) != 0) - return false; + return false; +} - return true; - } +bool stopExecutionRequested() +{ + return stopExecution; +} - bool stopExecutionRequested() - { - return stopExecution == 1; - } +} // namespace cadet - } // namespace cadet +#elif __unix__ || __linux__ || __APPLE__ +// Linux, BSD, Mac OSX + +#include +#include + +namespace +{ +volatile sig_atomic_t stopExecution = 0; + +void signalHandler(int signal) +{ + if (signal == SIGINT) + stopExecution = 1; +} +} // namespace + +namespace cadet +{ +bool installSignalHandler() +{ + struct sigaction sigbreak; + sigbreak.sa_handler = &signalHandler; + sigemptyset(&sigbreak.sa_mask); + sigbreak.sa_flags = 0; + + if (sigaction(SIGINT, &sigbreak, NULL) != 0) + return false; + + return true; +} + +bool stopExecutionRequested() +{ + return stopExecution == 1; +} + +} // namespace cadet #endif diff --git a/src/cadet-cli/SignalHandler.hpp b/src/cadet-cli/SignalHandler.hpp index 895766028..46fe69c5d 100644 --- a/src/cadet-cli/SignalHandler.hpp +++ b/src/cadet-cli/SignalHandler.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Signal handling for cadet-cli. */ @@ -20,19 +20,19 @@ namespace cadet { - /** - * @brief Installs the signal handler in the OS - * @return @c true if the signal handler was installed successfully, otherwise @c false - */ - bool installSignalHandler(); +/** + * @brief Installs the signal handler in the OS + * @return @c true if the signal handler was installed successfully, otherwise @c false + */ +bool installSignalHandler(); - /** - * @brief Checks whether the user has requested the program to stop - * @details The user can request stopping the execution by pressing CTRL+C or sending SIGINT. - * @return @c true if the user request stopping the program, otherwise @c false - */ - bool stopExecutionRequested(); +/** + * @brief Checks whether the user has requested the program to stop + * @details The user can request stopping the execution by pressing CTRL+C or sending SIGINT. + * @return @c true if the user request stopping the program, otherwise @c false + */ +bool stopExecutionRequested(); } // namespace cadet -#endif // CADETCLI_SIGNALHANDLER_HPP_ +#endif // CADETCLI_SIGNALHANDLER_HPP_ diff --git a/src/cadet-cli/cadet-cli.cpp b/src/cadet-cli/cadet-cli.cpp index 8011a9981..b534987ef 100644 --- a/src/cadet-cli/cadet-cli.cpp +++ b/src/cadet-cli/cadet-cli.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -29,7 +29,7 @@ #include "common/Driver.hpp" #ifdef CADET_BENCHMARK_MODE - #include "common/Timer.hpp" +#include "common/Timer.hpp" #endif #include @@ -38,91 +38,99 @@ #include #ifndef CADET_LOGGING_DISABLE - template <> - cadet::LogLevel cadet::log::RuntimeFilteringLogger::_minLvl = cadet::LogLevel::Trace; +template <> +cadet::LogLevel cadet::log::RuntimeFilteringLogger::_minLvl = cadet::LogLevel::Trace; - #ifdef __clang__ - // Silence -Wundefined-var-template warning - template class cadet::log::RuntimeFilteringLogger; - #endif +#ifdef __clang__ +// Silence -Wundefined-var-template warning +template class cadet::log::RuntimeFilteringLogger; +#endif #endif namespace { - inline void setLocalLogLevel(cadet::LogLevel newLL) - { +inline void setLocalLogLevel(cadet::LogLevel newLL) +{ #ifndef CADET_LOGGING_DISABLE - cadet::log::RuntimeFilteringLogger::level(newLL); + cadet::log::RuntimeFilteringLogger::level(newLL); #endif - } } +} // namespace class LogReceiver : public cadet::ILogReceiver { public: - LogReceiver() { } + LogReceiver() + { + } - virtual void message(const char* file, const char* func, const unsigned int line, cadet::LogLevel lvl, const char* lvlStr, const char* message) + virtual void message(const char* file, const char* func, const unsigned int line, cadet::LogLevel lvl, + const char* lvlStr, const char* message) { std::cout << '[' << lvlStr << ": " << func << "::" << line << "] " << message << std::flush; } }; #ifdef CADET_BENCHMARK_MODE - /** - * @brief Scope class that starts a timer on construction and stops it on destruction - */ - class BenchScope - { - public: - BenchScope() : _timer() { _timer.start(); } - ~BenchScope() CADET_NOEXCEPT - { - const double t = _timer.stop(); - std::cout << "Total elapsed time: " << t << " sec" << std::endl; - } - private: - cadet::Timer _timer; - }; +/** + * @brief Scope class that starts a timer on construction and stops it on destruction + */ +class BenchScope +{ +public: + BenchScope() : _timer() + { + _timer.start(); + } + ~BenchScope() CADET_NOEXCEPT + { + const double t = _timer.stop(); + std::cout << "Total elapsed time: " << t << " sec" << std::endl; + } + +private: + cadet::Timer _timer; +}; #endif // Command line parsing support for cadet::LogLevel type -namespace TCLAP +namespace TCLAP { - template<> - struct ArgTraits - { - typedef StringLike ValueCategory; - }; +template <> struct ArgTraits +{ + typedef StringLike ValueCategory; +}; - template<> - void SetString(cadet::LogLevel& v, const std::string& s) +template <> void SetString(cadet::LogLevel& v, const std::string& s) +{ + if (std::isdigit(s[0])) { - if (std::isdigit(s[0])) - { - const unsigned int lvl = std::stoul(s); - if (lvl > static_cast::type>(cadet::LogLevel::Trace)) - throw TCLAP::ArgParseException("Couldn't convert '" + s + "' to a valid log level"); + const unsigned int lvl = std::stoul(s); + if (lvl > static_cast::type>(cadet::LogLevel::Trace)) + throw TCLAP::ArgParseException("Couldn't convert '" + s + "' to a valid log level"); - v = static_cast(lvl); - } - else - { - const cadet::LogLevel lvl = cadet::to_loglevel(s); - if (cadet::to_string(lvl) != s) - throw TCLAP::ArgParseException("Couldn't convert '" + s + "' to a valid log level"); + v = static_cast(lvl); + } + else + { + const cadet::LogLevel lvl = cadet::to_loglevel(s); + if (cadet::to_string(lvl) != s) + throw TCLAP::ArgParseException("Couldn't convert '" + s + "' to a valid log level"); - v = lvl; - } + v = lvl; } +} } // namespace TCLAP - class ProgressBarNotifier : public cadet::INotificationCallback { public: - ProgressBarNotifier() : _progBar(), _secStrBuffer(13) { } - virtual ~ProgressBarNotifier() CADET_NOEXCEPT { } + ProgressBarNotifier() : _progBar(), _secStrBuffer(13) + { + } + virtual ~ProgressBarNotifier() CADET_NOEXCEPT + { + } virtual void timeIntegrationStart() { @@ -134,16 +142,20 @@ class ProgressBarNotifier : public cadet::INotificationCallback _progBar.finish("Done"); } - virtual void timeIntegrationError(char const* message, unsigned int section, double time, double progress) { } + virtual void timeIntegrationError(char const* message, unsigned int section, double time, double progress) + { + } - virtual bool timeIntegrationSection(unsigned int section, double time, double const* state, double const* stateDot, double progress) + virtual bool timeIntegrationSection(unsigned int section, double time, double const* state, double const* stateDot, + double progress) { snprintf(_secStrBuffer.data(), _secStrBuffer.size(), "Init Sec %3u", section); _progBar.update(progress, _secStrBuffer.data()); return !cadet::stopExecutionRequested(); } - virtual bool timeIntegrationStep(unsigned int section, double time, double const* state, double const* stateDot, double progress) + virtual bool timeIntegrationStep(unsigned int section, double time, double const* state, double const* stateDot, + double progress) { snprintf(_secStrBuffer.data(), _secStrBuffer.size(), "Section %3u", section); _progBar.update(progress, _secStrBuffer.data()); @@ -155,34 +167,45 @@ class ProgressBarNotifier : public cadet::INotificationCallback std::vector _secStrBuffer; }; - class SignalHandlingNotifier : public cadet::INotificationCallback { public: - SignalHandlingNotifier() { } - virtual ~SignalHandlingNotifier() CADET_NOEXCEPT { } + SignalHandlingNotifier() + { + } + virtual ~SignalHandlingNotifier() CADET_NOEXCEPT + { + } - virtual void timeIntegrationStart() { } - virtual void timeIntegrationEnd() { } - virtual void timeIntegrationError(char const* message, unsigned int section, double time, double progress) { } + virtual void timeIntegrationStart() + { + } + virtual void timeIntegrationEnd() + { + } + virtual void timeIntegrationError(char const* message, unsigned int section, double time, double progress) + { + } - virtual bool timeIntegrationSection(unsigned int section, double time, double const* state, double const* stateDot, double progress) + virtual bool timeIntegrationSection(unsigned int section, double time, double const* state, double const* stateDot, + double progress) { return !cadet::stopExecutionRequested(); } - virtual bool timeIntegrationStep(unsigned int section, double time, double const* state, double const* stateDot, double progress) + virtual bool timeIntegrationStep(unsigned int section, double time, double const* state, double const* stateDot, + double progress) { return !cadet::stopExecutionRequested(); } }; - -template -class FileReaderDriverConfigurator +template class FileReaderDriverConfigurator { public: - FileReaderDriverConfigurator() { } + FileReaderDriverConfigurator() + { + } void configure(cadet::Driver& drv, const std::string& inFileName) { @@ -196,11 +219,12 @@ class FileReaderDriverConfigurator } }; - class JsonDriverConfigurator { public: - JsonDriverConfigurator() { } + JsonDriverConfigurator() + { + } void configure(cadet::Driver& drv, const std::string& inFileName) { @@ -214,13 +238,12 @@ class JsonDriverConfigurator } }; - template int run(const std::string& inFileName, const std::string& outFileName, bool showProgressBar) { int returnCode = 0; cadet::Driver drv; - + { DriverConfigurator_t dc; dc.configure(drv, inFileName); @@ -249,7 +272,6 @@ int run(const std::string& inFileName, const std::string& outFileName, bool show drv.simulator()->setNotificationCallback(shn.get()); #endif - try { drv.run(); @@ -278,10 +300,10 @@ int run(const std::string& inFileName, const std::string& outFileName, bool show const std::vector sysTiming = drv.model()->benchmarkTimings(); char const* const* sysDesc = drv.model()->benchmarkDescriptions(); - for (std::size_t i = 0; i < sysTiming.size()-1; ++i) + for (std::size_t i = 0; i < sysTiming.size() - 1; ++i) std::cout << "\t\t\"" << sysDesc[i] << "\": " << sysTiming[i] << ",\n"; - std::cout << "\t\t\"" << sysDesc[sysTiming.size()-1] << "\": " << sysTiming[sysTiming.size()-1] << "\n\t}"; + std::cout << "\t\t\"" << sysDesc[sysTiming.size() - 1] << "\": " << sysTiming[sysTiming.size() - 1] << "\n\t}"; // Then, timings for all unit operations for (unsigned int j = 0; j < drv.model()->numModels(); ++j) @@ -295,10 +317,10 @@ int run(const std::string& inFileName, const std::string& outFileName, bool show const std::vector grmTiming = m->benchmarkTimings(); char const* const* grmDesc = m->benchmarkDescriptions(); - for (std::size_t i = 0; i < grmTiming.size()-1; ++i) + for (std::size_t i = 0; i < grmTiming.size() - 1; ++i) std::cout << "\t\t\"" << grmDesc[i] << "\": " << grmTiming[i] << ",\n"; - std::cout << "\t\t\"" << grmDesc[grmTiming.size()-1] << "\": " << grmTiming[grmTiming.size()-1] << "\n\t}"; + std::cout << "\t\t\"" << grmDesc[grmTiming.size() - 1] << "\": " << grmTiming[grmTiming.size() - 1] << "\n\t}"; } std::cout << "\n}" << std::endl; #endif @@ -306,9 +328,8 @@ int run(const std::string& inFileName, const std::string& outFileName, bool show return returnCode; } - int main(int argc, char** argv) -{ +{ #ifdef CADET_BENCHMARK_MODE // Benchmark the whole program from start to finish BenchScope bsTotalTime; @@ -330,13 +351,18 @@ int main(int argc, char** argv) cmd.setOutput(&customOut); cmd >> (new TCLAP::SwitchArg("", "progress", "Show a progress bar"))->storeIn(&showProgressBar); - cmd >> (new TCLAP::ValueArg("L", "loglevel", "Set the log level", false, cadet::LogLevel::Trace, "LogLevel"))->storeIn(&logLevel); - cmd >> (new TCLAP::UnlabeledValueArg("input", "Input file", true, "", "File"))->storeIn(&inFileName); - cmd >> (new TCLAP::UnlabeledValueArg("output", "Output file (defaults to input file)", false, "", "File"))->storeIn(&outFileName); + cmd >> (new TCLAP::ValueArg("L", "loglevel", "Set the log level", false, + cadet::LogLevel::Trace, "LogLevel")) + ->storeIn(&logLevel); + cmd >> + (new TCLAP::UnlabeledValueArg("input", "Input file", true, "", "File"))->storeIn(&inFileName); + cmd >> (new TCLAP::UnlabeledValueArg("output", "Output file (defaults to input file)", false, "", + "File")) + ->storeIn(&outFileName); cmd.parse(argc, argv); } - catch (const TCLAP::ArgException &e) + catch (const TCLAP::ArgException& e) { std::cerr << "ERROR: " << e.error() << " for argument " << e.argId() << std::endl; return 1; @@ -369,8 +395,8 @@ int main(int argc, char** argv) return 2; } - const std::string fileExtIn = inFileName.substr(dotPosIn+1); - const std::string fileExtOut = outFileName.substr(dotPosOut+1); + const std::string fileExtIn = inFileName.substr(dotPosIn + 1); + const std::string fileExtOut = outFileName.substr(dotPosOut + 1); int returnCode = 0; try @@ -379,11 +405,13 @@ int main(int argc, char** argv) { if (cadet::util::caseInsensitiveEquals(fileExtOut, "h5")) { - returnCode = run, cadet::io::HDF5Writer>(inFileName, outFileName, showProgressBar); + returnCode = run, cadet::io::HDF5Writer>( + inFileName, outFileName, showProgressBar); } else if (cadet::util::caseInsensitiveEquals(fileExtOut, "xml")) { - returnCode = run, cadet::io::XMLWriter>(inFileName, outFileName, showProgressBar); + returnCode = run, cadet::io::XMLWriter>( + inFileName, outFileName, showProgressBar); } else { @@ -395,11 +423,13 @@ int main(int argc, char** argv) { if (cadet::util::caseInsensitiveEquals(fileExtOut, "xml")) { - returnCode = run, cadet::io::XMLWriter>(inFileName, outFileName, showProgressBar); + returnCode = run, cadet::io::XMLWriter>( + inFileName, outFileName, showProgressBar); } else if (cadet::util::caseInsensitiveEquals(fileExtOut, "h5")) { - returnCode = run, cadet::io::HDF5Writer>(inFileName, outFileName, showProgressBar); + returnCode = run, cadet::io::HDF5Writer>( + inFileName, outFileName, showProgressBar); } else { @@ -411,11 +441,13 @@ int main(int argc, char** argv) { if (cadet::util::caseInsensitiveEquals(fileExtOut, "xml")) { - returnCode = run(inFileName, outFileName, showProgressBar); + returnCode = + run(inFileName, outFileName, showProgressBar); } else if (cadet::util::caseInsensitiveEquals(fileExtOut, "h5")) { - returnCode = run(inFileName, outFileName, showProgressBar); + returnCode = + run(inFileName, outFileName, showProgressBar); } else { diff --git a/src/io/FileIO.cpp b/src/io/FileIO.cpp index 72503cc94..719a10f4f 100644 --- a/src/io/FileIO.cpp +++ b/src/io/FileIO.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -27,7 +27,6 @@ using json = nlohmann::json; - namespace { @@ -36,12 +35,15 @@ namespace * @details Wraps a generic file reading class providing the IFileReader interface. * @tparam reader_t Generic file reader class */ -template -class FileReader : public cadet::io::IFileReader +template class FileReader : public cadet::io::IFileReader { public: - FileReader() { } - virtual ~FileReader() CADET_NOEXCEPT { } + FileReader() + { + } + virtual ~FileReader() CADET_NOEXCEPT + { + } virtual double getDouble(const std::string& paramName) { @@ -122,30 +124,35 @@ class FileReader : public cadet::io::IFileReader reader_t _io; }; - /** * @brief Implements writing capabilities from a generic file writer class * @details Wraps a generic file writing class providing the IFileWriter interface. * @tparam writer_t Generic file writer class */ -template -class FileWriter : public cadet::io::IFileWriter +template class FileWriter : public cadet::io::IFileWriter { public: - FileWriter() { } - virtual ~FileWriter() CADET_NOEXCEPT { } - - virtual void writeTensorDouble(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const double* buffer, const std::size_t stride, const std::size_t blockSize) + FileWriter() + { + } + virtual ~FileWriter() CADET_NOEXCEPT + { + } + + virtual void writeTensorDouble(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const double* buffer, const std::size_t stride, const std::size_t blockSize) { _io.template tensor(dataSetName, rank, dims, buffer, stride, blockSize); } - virtual void writeTensorInt(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const int* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeTensorInt(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const int* buffer, const std::size_t stride, const std::size_t blockSize) { _io.template tensor(dataSetName, rank, dims, buffer, stride, blockSize); } - virtual void writeTensorBool(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const bool* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeTensorBool(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const bool* buffer, const std::size_t stride, const std::size_t blockSize) { // Compute buffer size std::size_t bufSize = 1; @@ -164,22 +171,26 @@ class FileWriter : public cadet::io::IFileWriter _io.template tensor(dataSetName, rank, dims, bd.data(), 1, 1); } - virtual void writeTensorString(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const std::string* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeTensorString(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const std::string* buffer, const std::size_t stride, const std::size_t blockSize) { _io.template tensor(dataSetName, rank, dims, buffer, stride, blockSize); } - virtual void writeMatrixDouble(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const double* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeMatrixDouble(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const double* buffer, const std::size_t stride, const std::size_t blockSize) { _io.template matrix(dataSetName, rows, cols, buffer, stride, blockSize); } - virtual void writeMatrixInt(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const int* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeMatrixInt(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const int* buffer, const std::size_t stride, const std::size_t blockSize) { _io.template matrix(dataSetName, rows, cols, buffer, stride, blockSize); } - virtual void writeMatrixBool(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const bool* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeMatrixBool(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const bool* buffer, const std::size_t stride, const std::size_t blockSize) { // Compute buffer size const std::size_t bufSize = rows * cols; @@ -194,22 +205,26 @@ class FileWriter : public cadet::io::IFileWriter _io.template matrix(dataSetName, rows, cols, bd.data(), 1, 1); } - virtual void writeMatrixString(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const std::string* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeMatrixString(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const std::string* buffer, const std::size_t stride, const std::size_t blockSize) { _io.template matrix(dataSetName, rows, cols, buffer, stride, blockSize); } - virtual void writeVectorDouble(const std::string& dataSetName, const std::size_t length, const double* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeVectorDouble(const std::string& dataSetName, const std::size_t length, const double* buffer, + const std::size_t stride, const std::size_t blockSize) { _io.template vector(dataSetName, length, buffer, stride, blockSize); } - virtual void writeVectorInt(const std::string& dataSetName, const std::size_t length, const int* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeVectorInt(const std::string& dataSetName, const std::size_t length, const int* buffer, + const std::size_t stride, const std::size_t blockSize) { _io.template vector(dataSetName, length, buffer, stride, blockSize); } - virtual void writeVectorBool(const std::string& dataSetName, const std::size_t length, const bool* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeVectorBool(const std::string& dataSetName, const std::size_t length, const bool* buffer, + const std::size_t stride, const std::size_t blockSize) { // Convert buffer to int std::vector bd(length); @@ -221,7 +236,8 @@ class FileWriter : public cadet::io::IFileWriter _io.template vector(dataSetName, length, bd.data(), 1, 1); } - virtual void writeVectorString(const std::string& dataSetName, const std::size_t length, const std::string* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeVectorString(const std::string& dataSetName, const std::size_t length, const std::string* buffer, + const std::size_t stride, const std::size_t blockSize) { _io.template vector(dataSetName, length, buffer, stride, blockSize); } @@ -260,16 +276,16 @@ class FileWriter : public cadet::io::IFileWriter writer_t _io; }; - /** * @brief Implements file IO and navigation functions on top of FileReader or FileWriter * @tparam base_t FileReader or FileWriter class */ -template -class BaseIOWrapper : public base_t +template class BaseIOWrapper : public base_t { public: - BaseIOWrapper() { } + BaseIOWrapper() + { + } virtual void openFile(const std::string& fileName, const char* mode) { @@ -322,16 +338,16 @@ class BaseIOWrapper : public base_t } }; - /** * @brief Implements JSON IO functions on top of some other class * @tparam base_t Base class that is augmented with JSON IO functions */ -template -class JSONBaseIOWrapper : public base_t +template class JSONBaseIOWrapper : public base_t { public: - JSONBaseIOWrapper() { } + JSONBaseIOWrapper() + { + } virtual void pushGroup(const std::string& scope) { @@ -367,7 +383,7 @@ class JSONBaseIOWrapper : public base_t } // Don't care for a preceding delimiter - if (scope[0] == delimiter[0]) + if (scope[0] == delimiter[0]) ++start; while (end != std::string::npos) @@ -379,7 +395,7 @@ class JSONBaseIOWrapper : public base_t // If at end, use start = maxSize. Else use start = end + delimiter. start = ((end > (std::string::npos - delimiter.size())) ? std::string::npos : end + delimiter.size()); - } + } } virtual bool exists(const std::string& paramName) @@ -550,7 +566,7 @@ class JSONBaseIOWrapper : public base_t std::vector names; names.reserve(base_t::_opened.top()->size()); for (json::iterator it = base_t::_opened.top()->begin(); it != base_t::_opened.top()->end(); ++it) - names.push_back(it.key()); + names.push_back(it.key()); return names; } @@ -574,15 +590,15 @@ class JSONBaseIOWrapper : public base_t if (p.is_array()) { if (p.is_number_float()) - return std::vector({ p.template get>().size() }); + return std::vector({p.template get>().size()}); else if (p.is_number_integer()) - return std::vector({ p.template get>().size() }); + return std::vector({p.template get>().size()}); else if (p.is_string()) - return std::vector({ p.template get().size() }); + return std::vector({p.template get().size()}); else if (p.is_boolean()) - return std::vector({ p.template get>().size() }); + return std::vector({p.template get>().size()}); - return std::vector({ 0 }); + return std::vector({0}); } else return std::vector(); @@ -596,62 +612,74 @@ class JSONBaseIOWrapper : public base_t return base_t::_opened.top()->at(elementName).size(); } - virtual void writeTensorDouble(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const double* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeTensorDouble(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const double* buffer, const std::size_t stride, const std::size_t blockSize) { writeTensor(dataSetName, rank, dims, buffer, stride, blockSize); } - virtual void writeTensorInt(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const int* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeTensorInt(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const int* buffer, const std::size_t stride, const std::size_t blockSize) { writeTensor(dataSetName, rank, dims, buffer, stride, blockSize); } - virtual void writeTensorBool(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const bool* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeTensorBool(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const bool* buffer, const std::size_t stride, const std::size_t blockSize) { writeTensor(dataSetName, rank, dims, buffer, stride, blockSize); } - virtual void writeTensorString(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const std::string* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeTensorString(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const std::string* buffer, const std::size_t stride, const std::size_t blockSize) { writeTensor(dataSetName, rank, dims, buffer, stride, blockSize); } - virtual void writeMatrixDouble(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const double* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeMatrixDouble(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const double* buffer, const std::size_t stride, const std::size_t blockSize) { writeMatrix(dataSetName, rows, cols, buffer, stride, blockSize); } - virtual void writeMatrixInt(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const int* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeMatrixInt(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const int* buffer, const std::size_t stride, const std::size_t blockSize) { writeMatrix(dataSetName, rows, cols, buffer, stride, blockSize); } - virtual void writeMatrixBool(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const bool* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeMatrixBool(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const bool* buffer, const std::size_t stride, const std::size_t blockSize) { writeMatrix(dataSetName, rows, cols, buffer, stride, blockSize); } - virtual void writeMatrixString(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const std::string* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeMatrixString(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const std::string* buffer, const std::size_t stride, const std::size_t blockSize) { writeMatrix(dataSetName, rows, cols, buffer, stride, blockSize); } - virtual void writeVectorDouble(const std::string& dataSetName, const std::size_t length, const double* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeVectorDouble(const std::string& dataSetName, const std::size_t length, const double* buffer, + const std::size_t stride, const std::size_t blockSize) { writeVector(dataSetName, length, buffer, stride, blockSize); } - virtual void writeVectorInt(const std::string& dataSetName, const std::size_t length, const int* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeVectorInt(const std::string& dataSetName, const std::size_t length, const int* buffer, + const std::size_t stride, const std::size_t blockSize) { writeVector(dataSetName, length, buffer, stride, blockSize); } - virtual void writeVectorBool(const std::string& dataSetName, const std::size_t length, const bool* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeVectorBool(const std::string& dataSetName, const std::size_t length, const bool* buffer, + const std::size_t stride, const std::size_t blockSize) { writeVector(dataSetName, length, buffer, stride, blockSize); } - virtual void writeVectorString(const std::string& dataSetName, const std::size_t length, const std::string* buffer, const std::size_t stride, const std::size_t blockSize) + virtual void writeVectorString(const std::string& dataSetName, const std::size_t length, const std::string* buffer, + const std::size_t stride, const std::size_t blockSize) { writeVector(dataSetName, length, buffer, stride, blockSize); } @@ -686,13 +714,19 @@ class JSONBaseIOWrapper : public base_t base_t::_opened.top()->erase(dsName); } - inline nlohmann::json* data() { return base_t::_root; } - inline nlohmann::json const* data() const { return base_t::_root; } + inline nlohmann::json* data() + { + return base_t::_root; + } + inline nlohmann::json const* data() const + { + return base_t::_root; + } protected: - template - void writeVector(const std::string& dataSetName, const std::size_t length, const source_t* buffer, const std::size_t stride, const std::size_t blockSize) + void writeVector(const std::string& dataSetName, const std::size_t length, const source_t* buffer, + const std::size_t stride, const std::size_t blockSize) { std::vector d(length); for (std::size_t i = 0; i < length / blockSize; ++i) @@ -705,7 +739,8 @@ class JSONBaseIOWrapper : public base_t } template - void writeMatrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, const source_t* buffer, const std::size_t stride, const std::size_t blockSize) + void writeMatrix(const std::string& dataSetName, const std::size_t rows, const std::size_t cols, + const source_t* buffer, const std::size_t stride, const std::size_t blockSize) { std::vector d(rows * cols); for (std::size_t i = 0; i < d.size() / blockSize; ++i) @@ -720,7 +755,8 @@ class JSONBaseIOWrapper : public base_t } template - void writeTensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, const source_t* buffer, const std::size_t stride, const std::size_t blockSize) + void writeTensor(const std::string& dataSetName, const std::size_t rank, const std::size_t* dims, + const source_t* buffer, const std::size_t stride, const std::size_t blockSize) { // Handle scalars if (rank == 0) @@ -765,7 +801,6 @@ class JSONBaseIOWrapper : public base_t } }; - /** * @brief Root class for implementing IMemoryIO using JSON * @details Serves as root class for JSONBaseIOWrapper. @@ -773,7 +808,6 @@ class JSONBaseIOWrapper : public base_t class JSONMemoryReaderWriterImpl : public cadet::io::IMemoryIO { public: - JSONMemoryReaderWriterImpl() : _root(new json()) { _opened.push(_root); @@ -789,17 +823,14 @@ class JSONMemoryReaderWriterImpl : public cadet::io::IMemoryIO std::stack _opened; }; - /** * @brief Root class for JSON FileReader and FileWriter implementations * @details Serves as root class for JSONBaseIOWrapper. * @tparam iface_t Interface to implement, either IFileReader or IFileWriter */ -template -class JSONFileIOProxy : public iface_t +template class JSONFileIOProxy : public iface_t { public: - JSONFileIOProxy() : _writeBack(false), _root(new json()) { _opened.push(_root); @@ -852,7 +883,6 @@ class JSONFileIOProxy : public iface_t std::stack _opened; }; - } // namespace namespace cadet @@ -908,4 +938,3 @@ IFileWriter* createWriter(const std::string& fileExt) } // namespace io } // namespace cadet - diff --git a/src/io/JsonParameterProvider.cpp b/src/io/JsonParameterProvider.cpp index 6b9144bbc..a5fadb281 100644 --- a/src/io/JsonParameterProvider.cpp +++ b/src/io/JsonParameterProvider.cpp @@ -21,8 +21,7 @@ #include "common/JsonParameterProvider.hpp" // Uncomment next line to enable logging in JsonParameterProvider -//#define CADET_JSON_LOGGING_ENABLE - +// #define CADET_JSON_LOGGING_ENABLE #ifdef CADET_JSON_LOGGING_ENABLE @@ -34,59 +33,63 @@ namespace cadet namespace log { - /** - * @brief Dispatches a log message to a receiver - * @param [in] file Filename in which the log message was raised - * @param [in] func Name of the function (implementation defined @c __func__ variable) - * @param [in] line Number of the line in which the log message was raised - * @param [in] lvl LogLevel representing the severity of the message - * @param [in] message Message string - */ - void emitLog(const char* file, const char* func, const unsigned int line, LogLevel lvl, const char* message); - - /** - * @brief Implements a standard formatting policy - */ - class LibCadetFormattingPolicy : public FormattingPolicyBase +/** + * @brief Dispatches a log message to a receiver + * @param [in] file Filename in which the log message was raised + * @param [in] func Name of the function (implementation defined @c __func__ variable) + * @param [in] line Number of the line in which the log message was raised + * @param [in] lvl LogLevel representing the severity of the message + * @param [in] message Message string + */ +void emitLog(const char* file, const char* func, const unsigned int line, LogLevel lvl, const char* message); + +/** + * @brief Implements a standard formatting policy + */ +class LibCadetFormattingPolicy : public FormattingPolicyBase +{ +public: + template + static inline void format(receiver_t& recv, const char* fileName, const char* funcName, unsigned int line, + LogLevel lvl, const paramList_t& p) { - public: - template - static inline void format(receiver_t& recv, const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, const paramList_t& p) - { - writeParams(recv, lvl, p); - } - }; + writeParams(recv, lvl, p); + } +}; - /** - * @brief Sends all messages to std::cout - */ - class EmitterWritePolicy : public NonBufferedWritePolicyBase +/** + * @brief Sends all messages to std::cout + */ +class EmitterWritePolicy : public NonBufferedWritePolicyBase +{ +public: + static inline void writeLine(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, + const std::string& msg) { - public: - static inline void writeLine(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, const std::string& msg) - { - emitLog(fileName, funcName, line, lvl, msg.c_str()); - } - }; + emitLog(fileName, funcName, line, lvl, msg.c_str()); + } +}; - typedef NonFilteringLogger GlobalLogger; +typedef NonFilteringLogger GlobalLogger; #ifndef CADET_LOGGING_DISABLE - typedef Logger, LogLevel::CADET_LOGLEVEL_MIN> DoubleFilterLogger; +typedef Logger, LogLevel::CADET_LOGLEVEL_MIN> DoubleFilterLogger; #else - typedef Logger DiscardingLogger; +typedef Logger DiscardingLogger; #endif } // namespace log } // namespace cadet - /** - * @brief Base for logging macros - * @details Note that because of the usage pattern - *
LOG(Info) << "My log line " << arg1;
- * no semicolon is appended. - */ - #define LOG(lvl) cadet::log::DoubleFilterLogger::statement(__FILE__, __func__, __LINE__) = cadet::log::DoubleFilterLogger::template createMessage() +/** + * @brief Base for logging macros + * @details Note that because of the usage pattern + *
LOG(Info) << "My log line " << arg1;
+ * no semicolon is appended. + */ +#define LOG(lvl) \ + cadet::log::DoubleFilterLogger::statement(__FILE__, __func__, __LINE__) = \ + cadet::log::DoubleFilterLogger::template createMessage() #endif @@ -143,7 +146,9 @@ JsonParameterProvider::JsonParameterProvider(const JsonParameterProvider& cpy) #endif } -JsonParameterProvider::JsonParameterProvider(JsonParameterProvider&& cpy) CADET_NOEXCEPT : _root(cpy._root), _opened(std::move(cpy._opened)) +JsonParameterProvider::JsonParameterProvider(JsonParameterProvider&& cpy) CADET_NOEXCEPT + : _root(cpy._root), + _opened(std::move(cpy._opened)) { cpy._root = nullptr; cpy._opened = std::stack(); @@ -399,7 +404,8 @@ std::vector JsonParameterProvider::getStringArray(const std::string bool JsonParameterProvider::exists(const std::string& paramName) { #ifdef CADET_JSON_LOGGING_ENABLE - LOG(Debug) << "EXISTS " << paramName << " = " << ((_opened.top()->find(paramName) != _opened.top()->end()) ? "yes" : "no"); + LOG(Debug) << "EXISTS " << paramName << " = " + << ((_opened.top()->find(paramName) != _opened.top()->end()) ? "yes" : "no"); #endif return _opened.top()->find(paramName) != _opened.top()->end(); } @@ -523,8 +529,8 @@ void JsonParameterProvider::copy(const std::string& src, const std::string& dest void JsonParameterProvider::toFile(const std::string& fileName) const { - std::ofstream ofs(fileName, std::ios::out | std::ios::trunc); - ofs << _root->dump(4); + std::ofstream ofs(fileName, std::ios::out | std::ios::trunc); + ofs << _root->dump(4); } JsonParameterProvider JsonParameterProvider::fromFile(const std::string& fileName) diff --git a/src/libcadet/AdUtils.cpp b/src/libcadet/AdUtils.cpp index e8919a66d..abb157576 100644 --- a/src/libcadet/AdUtils.cpp +++ b/src/libcadet/AdUtils.cpp @@ -16,7 +16,7 @@ #include "AdUtils.hpp" #ifdef ENABLE_DG - #include +#include #endif #include #include @@ -27,8 +27,8 @@ namespace cadet namespace ad { -void prepareAdVectorSeedsForBandMatrix(active* const adVec, int adDirOffset, int rows, - int lowerBandwidth, int upperBandwidth, int diagDir) +void prepareAdVectorSeedsForBandMatrix(active* const adVec, int adDirOffset, int rows, int lowerBandwidth, + int upperBandwidth, int diagDir) { // Start with diagonal Jacobian element int dir = diagDir; @@ -96,8 +96,8 @@ void extractDenseJacobianFromAd(active const* const adVec, int adDirOffset, lina } } -void extractDenseJacobianFromBandedAd(active const* const adVec, int row, int adDirOffset, int diagDir, - int lowerBandwidth, int upperBandwidth, linalg::detail::DenseMatrixBase& mat) +void extractDenseJacobianFromBandedAd(active const* const adVec, int row, int adDirOffset, int diagDir, + int lowerBandwidth, int upperBandwidth, linalg::detail::DenseMatrixBase& mat) { const int stride = lowerBandwidth + 1 + upperBandwidth; for (int eq = 0; eq < mat.rows(); ++eq) @@ -124,7 +124,8 @@ void extractDenseJacobianFromBandedAd(active const* const adVec, int row, int ad } } -double compareBandedJacobianWithAd(active const* const adVec, int adDirOffset, int diagDir, const linalg::BandMatrix& mat) +double compareBandedJacobianWithAd(active const* const adVec, int adDirOffset, int diagDir, + const linalg::BandMatrix& mat) { const int lowerBandwidth = mat.lowerBandwidth(); const int upperBandwidth = mat.upperBandwidth(); @@ -162,7 +163,8 @@ double compareBandedJacobianWithAd(active const* const adVec, int adDirOffset, i return maxDiff; } -double compareDenseJacobianWithAd(active const* const adVec, int adDirOffset, const linalg::detail::DenseMatrixBase& mat) +double compareDenseJacobianWithAd(active const* const adVec, int adDirOffset, + const linalg::detail::DenseMatrixBase& mat) { double maxDiff = 0.0; for (int eq = 0; eq < mat.rows(); ++eq) @@ -185,8 +187,9 @@ double compareDenseJacobianWithAd(active const* const adVec, int adDirOffset, co return maxDiff; } -double compareDenseJacobianWithBandedAd(active const* const adVec, int row, int adDirOffset, int diagDir, - int lowerBandwidth, int upperBandwidth, const linalg::detail::DenseMatrixBase& mat) +double compareDenseJacobianWithBandedAd(active const* const adVec, int row, int adDirOffset, int diagDir, + int lowerBandwidth, int upperBandwidth, + const linalg::detail::DenseMatrixBase& mat) { double maxDiff = 0.0; const int stride = lowerBandwidth + 1 + upperBandwidth; @@ -208,7 +211,7 @@ double compareDenseJacobianWithBandedAd(active const* const adVec, int row, int if (std::isnan(mat.native(eq, diagCol)) || std::isnan(baseVal)) return std::numeric_limits::quiet_NaN(); const double diff = std::abs(mat.native(eq, diagCol) - baseVal); - + baseVal = std::abs(baseVal); if (baseVal > 0.0) maxDiff = std::max(maxDiff, diff / baseVal); @@ -226,7 +229,8 @@ double compareDenseJacobianWithBandedAd(active const* const adVec, int row, int return maxDiff; } -void adMatrixVectorMultiply(const linalg::SparseMatrix& mat, double const* x, double* y, double alpha, double beta, int adDir) +void adMatrixVectorMultiply(const linalg::SparseMatrix& mat, double const* x, double* y, double alpha, + double beta, int adDir) { const std::vector& rows = mat.rows(); const std::vector& cols = mat.cols(); @@ -241,110 +245,116 @@ void adMatrixVectorMultiply(const linalg::SparseMatrix& mat, double cons #ifdef ENABLE_DG - void extractBandedEigenJacobianFromAd(active const* const adVec, int adDirOffset, int diagDir, const int lowerBandwidth, const int upperBandwidth, Eigen::SparseMatrix& mat) +void extractBandedEigenJacobianFromAd(active const* const adVec, int adDirOffset, int diagDir, const int lowerBandwidth, + const int upperBandwidth, Eigen::SparseMatrix& mat) +{ + const int stride = lowerBandwidth + 1 + upperBandwidth; + for (int eq = 0; eq < mat.rows(); ++eq) { - const int stride = lowerBandwidth + 1 + upperBandwidth; - for (int eq = 0; eq < mat.rows(); ++eq) + // Start with lowest subdiagonal and stay in the range of the columns: + // diagDir might be too big for the matrix and, hence, dir ranges between + // diagDir - lowerBandwidth <= dir <= diagDir + upperBandwidth + int dir = diagDir - lowerBandwidth + eq % stride; + + // Loop over diagonals + for (int diag = 0; diag < stride; ++diag) { - // Start with lowest subdiagonal and stay in the range of the columns: - // diagDir might be too big for the matrix and, hence, dir ranges between - // diagDir - lowerBandwidth <= dir <= diagDir + upperBandwidth - int dir = diagDir - lowerBandwidth + eq % stride; + if (eq - lowerBandwidth + diag >= 0 && // left block boundary + eq - lowerBandwidth + diag < mat.rows() && // right block boundary + adVec[eq].getADValue(adDirOffset + dir) != 0.0 // keep pattern + ) + mat.coeffRef(eq, eq - lowerBandwidth + diag) = adVec[eq].getADValue(adDirOffset + dir); - // Loop over diagonals - for (int diag = 0; diag < stride; ++diag) - { - if (eq - lowerBandwidth + diag >= 0 && // left block boundary - eq - lowerBandwidth + diag < mat.rows() && // right block boundary - adVec[eq].getADValue(adDirOffset + dir) != 0.0 // keep pattern - ) - mat.coeffRef(eq, eq - lowerBandwidth + diag) = adVec[eq].getADValue(adDirOffset + dir); - - // Wrap around at end of row and jump to lowest subdiagonal - if (dir == diagDir + upperBandwidth) - dir = diagDir - lowerBandwidth; - else - ++dir; - } + // Wrap around at end of row and jump to lowest subdiagonal + if (dir == diagDir + upperBandwidth) + dir = diagDir - lowerBandwidth; + else + ++dir; } } +} - void extractBandedBlockEigenJacobianFromAd(active const* const adVec, int adDirOffset, int diagDir, const int lowerBandwidth, const int upperBandwidth, - const int blockOffset, const int nRows, Eigen::SparseMatrix& mat, const int matrixOffset) +void extractBandedBlockEigenJacobianFromAd(active const* const adVec, int adDirOffset, int diagDir, + const int lowerBandwidth, const int upperBandwidth, const int blockOffset, + const int nRows, Eigen::SparseMatrix& mat, + const int matrixOffset) +{ + const int stride = lowerBandwidth + 1 + upperBandwidth; + for (int eq = blockOffset; eq < blockOffset + nRows; ++eq) { - const int stride = lowerBandwidth + 1 + upperBandwidth; - for (int eq = blockOffset; eq < blockOffset + nRows; ++eq) + // Start with lowest subdiagonal and stay in the range of the columns: + // diagDir might be too big for the matrix and, hence, dir ranges between + // diagDir - lowerBandwidth <= dir <= diagDir + upperBandwidth + int dir = diagDir - lowerBandwidth + eq % stride; + + // Loop over diagonals + for (int diag = 0; diag < stride; ++diag) { - // Start with lowest subdiagonal and stay in the range of the columns: - // diagDir might be too big for the matrix and, hence, dir ranges between - // diagDir - lowerBandwidth <= dir <= diagDir + upperBandwidth - int dir = diagDir - lowerBandwidth + eq % stride; + if (eq - lowerBandwidth + diag >= blockOffset && // left block boundary + eq - lowerBandwidth + diag < blockOffset + nRows && // right block boundary + adVec[eq].getADValue(adDirOffset + dir) != 0.0 // keep pattern + ) + mat.coeffRef(matrixOffset + eq, matrixOffset + eq - lowerBandwidth + diag) = + adVec[eq].getADValue(adDirOffset + dir); - // Loop over diagonals - for (int diag = 0; diag < stride; ++diag) - { - if (eq - lowerBandwidth + diag >= blockOffset && // left block boundary - eq - lowerBandwidth + diag < blockOffset + nRows && // right block boundary - adVec[eq].getADValue(adDirOffset + dir) != 0.0 // keep pattern - ) - mat.coeffRef(matrixOffset + eq, matrixOffset + eq - lowerBandwidth + diag) = adVec[eq].getADValue(adDirOffset + dir); - - // Wrap around at end of row and jump to lowest subdiagonal - if (dir == diagDir + upperBandwidth) - dir = diagDir - lowerBandwidth; - else - ++dir; - } + // Wrap around at end of row and jump to lowest subdiagonal + if (dir == diagDir + upperBandwidth) + dir = diagDir - lowerBandwidth; + else + ++dir; } } +} + +double compareBandedEigenJacobianWithAd(active const* const adVec, const int adDirOffset, const int diagDir, + const int lowerBandwidth, const int upperBandwidth, const int blockOffset, + const int nRows, const Eigen::SparseMatrix& mat, + const int matrixOffset) +{ + double maxDiff = 0.0; - double compareBandedEigenJacobianWithAd(active const* const adVec, const int adDirOffset, const int diagDir, const int lowerBandwidth, const int upperBandwidth, - const int blockOffset, const int nRows, const Eigen::SparseMatrix& mat, const int matrixOffset) + const int stride = lowerBandwidth + 1 + upperBandwidth; + for (int eq = blockOffset; eq < blockOffset + nRows; ++eq) { - double maxDiff = 0.0; + // Start with lowest subdiagonal and stay in the range of the columns: + // diagDir might be too big for the matrix and, hence, dir ranges between + // diagDir - lowerBandwidth <= dir <= diagDir + upperBandwidth + int dir = diagDir - lowerBandwidth + eq % stride; - const int stride = lowerBandwidth + 1 + upperBandwidth; - for (int eq = blockOffset; eq < blockOffset + nRows; ++eq) + // Loop over diagonals + for (int diag = 0; diag < stride; ++diag) { - // Start with lowest subdiagonal and stay in the range of the columns: - // diagDir might be too big for the matrix and, hence, dir ranges between - // diagDir - lowerBandwidth <= dir <= diagDir + upperBandwidth - int dir = diagDir - lowerBandwidth + eq % stride; - - // Loop over diagonals - for (int diag = 0; diag < stride; ++diag) + if (eq - lowerBandwidth + diag >= blockOffset && // left block boundary + eq - lowerBandwidth + diag < blockOffset + nRows && // right block boundary + adVec[eq].getADValue(adDirOffset + dir) != 0.0 // keep pattern + ) { - if (eq - lowerBandwidth + diag >= blockOffset && // left block boundary - eq - lowerBandwidth + diag < blockOffset + nRows && // right block boundary - adVec[eq].getADValue(adDirOffset + dir) != 0.0 // keep pattern - ) - { - double baseVal = adVec[eq].getADValue(adDirOffset + dir); - double matVal = mat.coeff(matrixOffset + eq, matrixOffset + eq - lowerBandwidth + diag); - - if (std::isnan(matVal) || std::isnan(baseVal)) - return std::numeric_limits::quiet_NaN(); - const double diff = std::abs(matVal - baseVal); - - baseVal = std::abs(baseVal); - if (baseVal > 0.0) - maxDiff = std::max(maxDiff, diff / baseVal); - else - maxDiff = std::max(maxDiff, diff); - } - - // Wrap around at end of row and jump to lowest subdiagonal - if (dir == diagDir + upperBandwidth) - dir = diagDir - lowerBandwidth; + double baseVal = adVec[eq].getADValue(adDirOffset + dir); + double matVal = mat.coeff(matrixOffset + eq, matrixOffset + eq - lowerBandwidth + diag); + + if (std::isnan(matVal) || std::isnan(baseVal)) + return std::numeric_limits::quiet_NaN(); + const double diff = std::abs(matVal - baseVal); + + baseVal = std::abs(baseVal); + if (baseVal > 0.0) + maxDiff = std::max(maxDiff, diff / baseVal); else - ++dir; + maxDiff = std::max(maxDiff, diff); } + + // Wrap around at end of row and jump to lowest subdiagonal + if (dir == diagDir + upperBandwidth) + dir = diagDir - lowerBandwidth; + else + ++dir; } - return maxDiff; } + return maxDiff; +} #endif -} // namespace ad +} // namespace ad -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/AdUtils.hpp b/src/libcadet/AdUtils.hpp index cfd80d311..d23200893 100644 --- a/src/libcadet/AdUtils.hpp +++ b/src/libcadet/AdUtils.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides utilities for AD vectors and matrices */ @@ -22,7 +22,7 @@ #include "CompileTimeConfig.hpp" #ifdef ENABLE_DG - #include +#include #endif namespace cadet @@ -30,16 +30,16 @@ namespace cadet namespace linalg { - class BandMatrix; +class BandMatrix; - namespace detail - { - class DenseMatrixBase; - } - - template class SparseMatrix; +namespace detail +{ +class DenseMatrixBase; } +template class SparseMatrix; +} // namespace linalg + namespace ad { @@ -55,8 +55,8 @@ namespace ad * @param [in] upperBandwidth Upper bandwidth (number of upper superdiagonals) of the banded Jacobian * @param [in] diagDir Diagonal direction index */ -void prepareAdVectorSeedsForBandMatrix(active* const adVec, int adDirOffset, int rows, - int lowerBandwidth, int upperBandwidth, int diagDir); +void prepareAdVectorSeedsForBandMatrix(active* const adVec, int adDirOffset, int rows, int lowerBandwidth, + int upperBandwidth, int diagDir); /** * @brief Extracts a band matrix from band compressed AD seed vectors @@ -70,54 +70,62 @@ void prepareAdVectorSeedsForBandMatrix(active* const adVec, int adDirOffset, int void extractBandedJacobianFromAd(active const* const adVec, int adDirOffset, int diagDir, linalg::BandMatrix& mat); #ifdef ENABLE_DG - /** - * @brief Extracts a band (sub)matrix (Eigen lib) from band compressed AD seed vectors - * @details Uses the results of an AD computation with seed vectors set by prepareAdVectorSeedsForBandMatrix() to - assemble the Jacobian block which is a band matrix. The block must be on the main diagonal. - * @param [in] adVec Vector of AD datatypes with band compressed seed vectors - * @param [in] adDirOffset Offset in the AD directions (can be used to move past parameter sensitivity directions) - * @param [in] diagDir Diagonal direction index - * @param [in] lowerBandwidth lower band width - * @param [in] upperBandwidth upper band width - * @param [in] blockOffset offset to diagonal block - * @param [in] nCols number of columns of the extracted block - * @param [out] mat Eigen matrix to be populated with the Jacobian block - */ - void extractBandedBlockEigenJacobianFromAd(active const* const adVec, int adDirOffset, int diagDir, const int lowerBandwidth, const int upperBandwidth, - const int blockOffset, const int nCols, Eigen::SparseMatrix& mat, const int matrixOffset = 0); +/** + * @brief Extracts a band (sub)matrix (Eigen lib) from band compressed AD seed vectors + * @details Uses the results of an AD computation with seed vectors set by prepareAdVectorSeedsForBandMatrix() to + assemble the Jacobian block which is a band matrix. The block must be on the main diagonal. +* @param [in] adVec Vector of AD datatypes with band compressed seed vectors +* @param [in] adDirOffset Offset in the AD directions (can be used to move past parameter sensitivity directions) +* @param [in] diagDir Diagonal direction index +* @param [in] lowerBandwidth lower band width +* @param [in] upperBandwidth upper band width +* @param [in] blockOffset offset to diagonal block +* @param [in] nCols number of columns of the extracted block +* @param [out] mat Eigen matrix to be populated with the Jacobian block +*/ +void extractBandedBlockEigenJacobianFromAd(active const* const adVec, int adDirOffset, int diagDir, + const int lowerBandwidth, const int upperBandwidth, const int blockOffset, + const int nCols, Eigen::SparseMatrix& mat, + const int matrixOffset = 0); - /** - * @brief Extracts a band matrix (Eigen lib) from band compressed AD seed vectors - * @details Uses the results of an AD computation with seed vectors set by prepareAdVectorSeedsForBandMatrix() to - assemble the Jacobian block which is a band matrix. The block must be on the main diagonal. - * @param [in] adVec Vector of AD datatypes with band compressed seed vectors - * @param [in] adDirOffset Offset in the AD directions (can be used to move past parameter sensitivity directions) - * @param [in] diagDir Diagonal direction index - * @param [in] lowerBandwidth lower band width - * @param [in] upperBandwidth upper band width - * @param [out] mat Eigen matrix to be populated with the Jacobian block - */ - void extractBandedEigenJacobianFromAd(active const* const adVec, int adDirOffset, int diagDir, const int lowerBandwidth, const int upperBandwidth, Eigen::SparseMatrix& mat); +/** + * @brief Extracts a band matrix (Eigen lib) from band compressed AD seed vectors + * @details Uses the results of an AD computation with seed vectors set by prepareAdVectorSeedsForBandMatrix() to + assemble the Jacobian block which is a band matrix. The block must be on the main diagonal. + * @param [in] adVec Vector of AD datatypes with band compressed seed vectors + * @param [in] adDirOffset Offset in the AD directions (can be used to move past parameter sensitivity directions) + * @param [in] diagDir Diagonal direction index + * @param [in] lowerBandwidth lower band width + * @param [in] upperBandwidth upper band width + * @param [out] mat Eigen matrix to be populated with the Jacobian block + */ +void extractBandedEigenJacobianFromAd(active const* const adVec, int adDirOffset, int diagDir, const int lowerBandwidth, + const int upperBandwidth, Eigen::SparseMatrix& mat); - /** - * @brief Compares a (block-) banded Jacobian in Eigen library row-major format with an AD version derived by band compressed AD seed vectors - * @details Uses the results of an AD computation with seed vectors set by prepareAdVectorSeedsForBandMatrix() to - compare the results with a given banded Jacobian. The AD Jacobian is treated as base and the analytic - Jacobian is compared against it. The relative difference - @f[ \Delta_{ij} = \begin{cases} \left\lvert \frac{ J_{\text{ana},ij} - J_{\text{ad},ij} }{ J_{\text{ad},ij} }\right\rvert, & J_{\text{ad},ij} \neq 0 \\ - \left\lvert J_{\text{ana},ij} - J_{\text{ad},ij} \right\rvert, & J_{\text{ad},ij} = 0 \end{cases} @f] - is computed for each matrix entry. The maximum of all @f$ \Delta_{ij} @f$ is returned. - * @param [in] adVec Vector of AD datatypes with band compressed seed vectors - * @param [in] adDirOffset Offset in the AD directions (can be used to move past parameter sensitivity directions) - * @param [in] diagDir Diagonal direction index - * @param [in] lowerBandwidth of the block - * @param [in] upperBandwidth of the block - * @param [in] blockOffset of the currently considered block - * @param [in] nRows of the matrix or number of equations to be compared - * @param [in] mat BandMatrix populated with the analytic Jacobian - * @return The maximum absolute relative difference between the matrix elements - */ - double compareBandedEigenJacobianWithAd(active const* const adVec, const int adDirOffset, const int diagDir, const int lowerBandwidth, const int upperBandwidth, const int blockOffset, const int nRows, const Eigen::SparseMatrix& mat, const int matrixOffset); +/** + * @brief Compares a (block-) banded Jacobian in Eigen library row-major format with an AD version derived by band + compressed AD seed vectors + * @details Uses the results of an AD computation with seed vectors set by prepareAdVectorSeedsForBandMatrix() to + compare the results with a given banded Jacobian. The AD Jacobian is treated as base and the analytic + Jacobian is compared against it. The relative difference + @f[ \Delta_{ij} = \begin{cases} \left\lvert \frac{ J_{\text{ana},ij} - J_{\text{ad},ij} }{ J_{\text{ad},ij} + }\right\rvert, & J_{\text{ad},ij} \neq 0 \\ \left\lvert J_{\text{ana},ij} - J_{\text{ad},ij} \right\rvert, & + J_{\text{ad},ij} = 0 \end{cases} @f] is computed for each matrix entry. The maximum of all @f$ \Delta_{ij} @f$ is + returned. + * @param [in] adVec Vector of AD datatypes with band compressed seed vectors + * @param [in] adDirOffset Offset in the AD directions (can be used to move past parameter sensitivity directions) + * @param [in] diagDir Diagonal direction index + * @param [in] lowerBandwidth of the block + * @param [in] upperBandwidth of the block + * @param [in] blockOffset of the currently considered block + * @param [in] nRows of the matrix or number of equations to be compared + * @param [in] mat BandMatrix populated with the analytic Jacobian + * @return The maximum absolute relative difference between the matrix elements + */ +double compareBandedEigenJacobianWithAd(active const* const adVec, const int adDirOffset, const int diagDir, + const int lowerBandwidth, const int upperBandwidth, const int blockOffset, + const int nRows, const Eigen::SparseMatrix& mat, + const int matrixOffset); #endif @@ -145,7 +153,8 @@ void extractDenseJacobianFromAd(active const* const adVec, int adDirOffset, lina assemble a subset of the banded Jacobian into a dense matrix. The subset is taken from the top left element of the band matrix (i.e., the first element on the main diagonal). - * @param [in] adVec Vector of AD datatypes with band compressed seed vectors pointing to the first row of the band matrix + * @param [in] adVec Vector of AD datatypes with band compressed seed vectors pointing to the first row of the band + matrix * @param [in] row Index of the first row to be extracted * @param [in] adDirOffset Offset in the AD directions (can be used to move past parameter sensitivity directions) * @param [in] diagDir Diagonal direction index @@ -154,50 +163,55 @@ void extractDenseJacobianFromAd(active const* const adVec, int adDirOffset, lina * @param [out] mat Dense matrix to be populated with the Jacobian submatrix */ void extractDenseJacobianFromBandedAd(active const* const adVec, int row, int adDirOffset, int diagDir, - int lowerBandwidth, int upperBandwidth, linalg::detail::DenseMatrixBase& mat); + int lowerBandwidth, int upperBandwidth, linalg::detail::DenseMatrixBase& mat); /** * @brief Compares a banded Jacobian with an AD version derived by band compressed AD seed vectors * @details Uses the results of an AD computation with seed vectors set by prepareAdVectorSeedsForBandMatrix() to compare the results with a given banded Jacobian. The AD Jacobian is treated as base and the analytic Jacobian is compared against it. The relative difference - @f[ \Delta_{ij} = \begin{cases} \left\lvert \frac{ J_{\text{ana},ij} - J_{\text{ad},ij} }{ J_{\text{ad},ij} }\right\rvert, & J_{\text{ad},ij} \neq 0 \\ - \left\lvert J_{\text{ana},ij} - J_{\text{ad},ij} \right\rvert, & J_{\text{ad},ij} = 0 \end{cases} @f] - is computed for each matrix entry. The maximum of all @f$ \Delta_{ij} @f$ is returned. + @f[ \Delta_{ij} = \begin{cases} \left\lvert \frac{ J_{\text{ana},ij} - J_{\text{ad},ij} }{ J_{\text{ad},ij} + }\right\rvert, & J_{\text{ad},ij} \neq 0 \\ \left\lvert J_{\text{ana},ij} - J_{\text{ad},ij} \right\rvert, & + J_{\text{ad},ij} = 0 \end{cases} @f] is computed for each matrix entry. The maximum of all @f$ \Delta_{ij} @f$ is + returned. * @param [in] adVec Vector of AD datatypes with band compressed seed vectors * @param [in] adDirOffset Offset in the AD directions (can be used to move past parameter sensitivity directions) * @param [in] diagDir Diagonal direction index * @param [in] mat BandMatrix populated with the analytic Jacobian * @return The maximum absolute relative difference between the matrix elements */ -double compareBandedJacobianWithAd(active const* const adVec, int adDirOffset, int diagDir, const linalg::BandMatrix& mat); +double compareBandedJacobianWithAd(active const* const adVec, int adDirOffset, int diagDir, + const linalg::BandMatrix& mat); /** * @brief Compares a dense Jacobian with an AD version derived by AD seed vectors * @details Uses the results of an AD computation with seed vectors set by prepareAdVectorSeedsForDenseMatrix() to compare the results with a given dense Jacobian. The AD Jacobian is treated as base and the analytic Jacobian is compared against it. The relative difference - @f[ \Delta_{ij} = \begin{cases} \left\lvert \frac{ J_{\text{ana},ij} - J_{\text{ad},ij} }{ J_{\text{ad},ij} }\right\rvert, & J_{\text{ad},ij} \neq 0 \\ - \left\lvert J_{\text{ana},ij} - J_{\text{ad},ij} \right\rvert, & J_{\text{ad},ij} = 0 \end{cases} @f] - is computed for each matrix entry. The maximum of all @f$ \Delta_{ij} @f$ is returned. + @f[ \Delta_{ij} = \begin{cases} \left\lvert \frac{ J_{\text{ana},ij} - J_{\text{ad},ij} }{ J_{\text{ad},ij} + }\right\rvert, & J_{\text{ad},ij} \neq 0 \\ \left\lvert J_{\text{ana},ij} - J_{\text{ad},ij} \right\rvert, & + J_{\text{ad},ij} = 0 \end{cases} @f] is computed for each matrix entry. The maximum of all @f$ \Delta_{ij} @f$ is + returned. * @param [in] adVec Vector of AD datatypes with seed vectors * @param [in] adDirOffset Offset in the AD directions (can be used to move past parameter sensitivity directions) * @param [in] mat Dense matrix populated with the analytic Jacobian * @return The maximum absolute relative difference between the matrix elements */ -double compareDenseJacobianWithAd(active const* const adVec, int adDirOffset, const linalg::detail::DenseMatrixBase& mat); +double compareDenseJacobianWithAd(active const* const adVec, int adDirOffset, + const linalg::detail::DenseMatrixBase& mat); /** * @brief Compares a dense submatrix with a band compressed AD version * @details Uses the results of an AD computation with seed vectors set by prepareAdVectorSeedsForBandMatrix() to compare the results with a given dense submatrix of the Jacobian. The AD Jacobian is treated as base and the analytic Jacobian is compared against it. The relative difference - @f[ \Delta_{ij} = \begin{cases} \left\lvert \frac{ J_{\text{ana},ij} - J_{\text{ad},ij} }{ J_{\text{ad},ij} }\right\rvert, & J_{\text{ad},ij} \neq 0 \\ - \left\lvert J_{\text{ana},ij} - J_{\text{ad},ij} \right\rvert, & J_{\text{ad},ij} = 0 \end{cases} @f] - is computed for each matrix entry. The maximum of all @f$ \Delta_{ij} @f$ is returned. - The submatrix is taken from the top left element of the band matrix (i.e., the first element on the - main diagonal). - * @param [in] adVec Vector of AD datatypes with band compressed seed vectors pointing to the first row of the band matrix + @f[ \Delta_{ij} = \begin{cases} \left\lvert \frac{ J_{\text{ana},ij} - J_{\text{ad},ij} }{ J_{\text{ad},ij} + }\right\rvert, & J_{\text{ad},ij} \neq 0 \\ \left\lvert J_{\text{ana},ij} - J_{\text{ad},ij} \right\rvert, & + J_{\text{ad},ij} = 0 \end{cases} @f] is computed for each matrix entry. The maximum of all @f$ \Delta_{ij} @f$ is + returned. The submatrix is taken from the top left element of the band matrix (i.e., the first element on the main + diagonal). + * @param [in] adVec Vector of AD datatypes with band compressed seed vectors pointing to the first row of the band + matrix * @param [in] row Index of the first row to be extracted * @param [in] adDirOffset Offset in the AD directions (can be used to move past parameter sensitivity directions) * @param [in] diagDir Diagonal direction index @@ -207,7 +221,8 @@ double compareDenseJacobianWithAd(active const* const adVec, int adDirOffset, co * @return The maximum absolute relative difference between the matrix elements */ double compareDenseJacobianWithBandedAd(active const* const adVec, int row, int adDirOffset, int diagDir, - int lowerBandwidth, int upperBandwidth, const linalg::detail::DenseMatrixBase& mat); + int lowerBandwidth, int upperBandwidth, + const linalg::detail::DenseMatrixBase& mat); /** * @brief Performs the operation @f$ y = \alpha A x + \beta y @f$ using the derivative matrix @@ -222,7 +237,8 @@ double compareDenseJacobianWithBandedAd(active const* const adVec, int row, int * @param [in] beta Factor @f$ \beta @f$ in front of @f$ y @f$ * @param [in] adDir AD direction to use (selects the derivative matrix) */ -void adMatrixVectorMultiply(const linalg::SparseMatrix& mat, double const* x, double* y, double alpha, double beta, int adDir); +void adMatrixVectorMultiply(const linalg::SparseMatrix& mat, double const* x, double* y, double alpha, + double beta, int adDir); /** * @brief Copies the results (0th derivative) of an AD vector to a double vector @@ -237,7 +253,6 @@ inline void copyFromAd(active const* const adVec, double* const dest, int size) dest[i] = static_cast(adVec[i]); } - /** * @brief Copies an AD direction of an AD vector to a double vector * @param [in] adVec Source vector of AD datatypes @@ -307,4 +322,4 @@ inline void resetAd(active* const adVec, int size) } // namespace cadet -#endif // LIBCADET_ADUTILS_HPP_ +#endif // LIBCADET_ADUTILS_HPP_ diff --git a/src/libcadet/AutoDiff.cpp b/src/libcadet/AutoDiff.cpp index 1f5ebb543..2cf59c802 100644 --- a/src/libcadet/AutoDiff.cpp +++ b/src/libcadet/AutoDiff.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -15,19 +15,18 @@ #include #include "AutoDiff.hpp" - #if defined(ACTIVE_SFAD) || defined(ACTIVE_SETFAD) - ACTIVE_INIT +ACTIVE_INIT #endif namespace cadet { - namespace ad - { +namespace ad +{ #if defined(ACTIVE_SFAD) || defined(ACTIVE_SETFAD) #endif - } // namespace ad -} // namespace cadet +} // namespace ad +} // namespace cadet diff --git a/src/libcadet/AutoDiff.hpp b/src/libcadet/AutoDiff.hpp index 8a5e16a11..72c8d1e43 100644 --- a/src/libcadet/AutoDiff.hpp +++ b/src/libcadet/AutoDiff.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -22,186 +22,224 @@ #include "common/CompilerSpecific.hpp" #ifdef ENABLE_DG - #include +#include #endif #include #if defined(ACTIVE_SFAD) || defined(ACTIVE_SETFAD) - #define SFAD_DEFAULT_DIR 80 +#define SFAD_DEFAULT_DIR 80 - #if defined(ACTIVE_SFAD) - #include "sfad.hpp" - #else - #include "setfad.hpp" - #endif +#if defined(ACTIVE_SFAD) +#include "sfad.hpp" +#else +#include "setfad.hpp" +#endif - #define ACTIVE_INIT SFAD_GLOBAL_GRAD_SIZE +#define ACTIVE_INIT SFAD_GLOBAL_GRAD_SIZE - namespace cadet - { - - #if defined(ACTIVE_SFAD) - typedef sfad::Fwd active; - #else - typedef sfad::FwdET active; - #endif - - namespace ad - { - /** - * @brief Returns the maximum number of allowed AD directions (seed vectors) - * @return Maximum number of allowed AD directions - */ - inline std::size_t getMaxDirections() CADET_NOEXCEPT { return SFAD_DEFAULT_DIR; } - - /** - * @brief Returns the current number of AD directions (seed vectors) - * @return Current number of AD directions - */ - inline std::size_t getDirections() CADET_NOEXCEPT { return sfad::getGradientSize(); } - - /** - * @brief Sets the current number of AD directions (seed vectors) - * @details The number of AD directions must not exceed the value returned by getMaxDirections(). - * - * @param [in] numDir Number of required AD directions - */ - inline void setDirections(std::size_t n) - { - cadet_assert(n <= SFAD_DEFAULT_DIR); - sfad::setGradientSize(n); - } - } - } +namespace cadet +{ +#if defined(ACTIVE_SFAD) +typedef sfad::Fwd active; #else +typedef sfad::FwdET active; +#endif - #error No active data type defined! +namespace ad +{ +/** + * @brief Returns the maximum number of allowed AD directions (seed vectors) + * @return Maximum number of allowed AD directions + */ +inline std::size_t getMaxDirections() CADET_NOEXCEPT +{ + return SFAD_DEFAULT_DIR; +} -#endif // #if defined ACTIVE_ +/** + * @brief Returns the current number of AD directions (seed vectors) + * @return Current number of AD directions + */ +inline std::size_t getDirections() CADET_NOEXCEPT +{ + return sfad::getGradientSize(); +} +/** + * @brief Sets the current number of AD directions (seed vectors) + * @details The number of AD directions must not exceed the value returned by getMaxDirections(). + * + * @param [in] numDir Number of required AD directions + */ +inline void setDirections(std::size_t n) +{ + cadet_assert(n <= SFAD_DEFAULT_DIR); + sfad::setGradientSize(n); +} +} // namespace ad +} // namespace cadet + +#else + +#error No active data type defined! + +#endif // #if defined ACTIVE_ namespace cadet { - /** - * @brief Selects the @c active type between @c double and @c active - * @tparam A Type A - * @tparam B Type B - */ - template - struct DoubleActivePromoterImpl { }; - - template <> - struct DoubleActivePromoterImpl { typedef cadet::active type; }; - - template <> - struct DoubleActivePromoterImpl { typedef cadet::active type; }; - - template <> - struct DoubleActivePromoterImpl { typedef cadet::active type; }; - - template <> - struct DoubleActivePromoterImpl { typedef double type; }; - - /** - * @brief Selects the @c active type between @c double and @c active - * @tparam A Type A - * @tparam B Type B - */ - template - struct DoubleActivePromoter { typedef typename DoubleActivePromoterImpl, std::decay_t>::type type; }; - - template - using ActivePromoter = DoubleActivePromoter; - - /** - * @brief Selects the @c double type between @c double and @c active - * @tparam A Type A - * @tparam B Type B - */ - template - struct DoubleActiveDemoterImpl { }; - - template <> - struct DoubleActiveDemoterImpl { typedef cadet::active type; }; - - template <> - struct DoubleActiveDemoterImpl { typedef double type; }; - - template <> - struct DoubleActiveDemoterImpl { typedef double type; }; - - template <> - struct DoubleActiveDemoterImpl { typedef double type; }; - - /** - * @brief Selects the @c double type between @c double and @c active - * @tparam A Type A - * @tparam B Type B - */ - template - struct DoubleActiveDemoter { typedef typename DoubleActiveDemoterImpl, std::decay_t>::type type; }; - - template - using DoubleDemoter = DoubleActiveDemoter; - - /** - * @brief Selects value type @c double or reference type @c active& - * @tparam A Base type (i.e., @c double or @c active) - */ - template - struct ActiveRefOrDouble { }; - - template <> - struct ActiveRefOrDouble { typedef cadet::active& type; }; - - template <> - struct ActiveRefOrDouble { typedef const cadet::active& type; }; - - template <> - struct ActiveRefOrDouble { typedef double type; }; - - template <> - struct ActiveRefOrDouble { typedef const double type; }; -} +/** + * @brief Selects the @c active type between @c double and @c active + * @tparam A Type A + * @tparam B Type B + */ +template struct DoubleActivePromoterImpl +{ +}; + +template <> struct DoubleActivePromoterImpl +{ + typedef cadet::active type; +}; + +template <> struct DoubleActivePromoterImpl +{ + typedef cadet::active type; +}; + +template <> struct DoubleActivePromoterImpl +{ + typedef cadet::active type; +}; + +template <> struct DoubleActivePromoterImpl +{ + typedef double type; +}; + +/** + * @brief Selects the @c active type between @c double and @c active + * @tparam A Type A + * @tparam B Type B + */ +template struct DoubleActivePromoter +{ + typedef typename DoubleActivePromoterImpl, std::decay_t>::type type; +}; + +template using ActivePromoter = DoubleActivePromoter; + +/** + * @brief Selects the @c double type between @c double and @c active + * @tparam A Type A + * @tparam B Type B + */ +template struct DoubleActiveDemoterImpl +{ +}; + +template <> struct DoubleActiveDemoterImpl +{ + typedef cadet::active type; +}; + +template <> struct DoubleActiveDemoterImpl +{ + typedef double type; +}; + +template <> struct DoubleActiveDemoterImpl +{ + typedef double type; +}; + +template <> struct DoubleActiveDemoterImpl +{ + typedef double type; +}; + +/** + * @brief Selects the @c double type between @c double and @c active + * @tparam A Type A + * @tparam B Type B + */ +template struct DoubleActiveDemoter +{ + typedef typename DoubleActiveDemoterImpl, std::decay_t>::type type; +}; + +template using DoubleDemoter = DoubleActiveDemoter; + +/** + * @brief Selects value type @c double or reference type @c active& + * @tparam A Base type (i.e., @c double or @c active) + */ +template struct ActiveRefOrDouble +{ +}; + +template <> struct ActiveRefOrDouble +{ + typedef cadet::active& type; +}; + +template <> struct ActiveRefOrDouble +{ + typedef const cadet::active& type; +}; + +template <> struct ActiveRefOrDouble +{ + typedef double type; +}; + +template <> struct ActiveRefOrDouble +{ + typedef const double type; +}; +} // namespace cadet #ifdef ENABLE_DG - namespace Eigen { - - template<> struct NumTraits - : NumTraits // permits to get the epsilon, dummy_precision, lowest, highest functions - { - typedef cadet::active Real; - typedef cadet::active NonInteger; - typedef cadet::active Nested; - - enum { - IsComplex = 0, - IsInteger = 0, - IsSigned = 1, - RequireInitialization = 1, - ReadCost = 1, - AddCost = 3, - MulCost = 3 - }; - }; - - // specify return types concerning active double scalar operations for eigen - template<> - struct ScalarBinaryOpTraits> { - typedef cadet::active ReturnType; - }; - template<> - struct ScalarBinaryOpTraits> { - typedef cadet::active ReturnType; - }; - template<> - struct ScalarBinaryOpTraits> { - typedef cadet::active ReturnType; - }; - } +namespace Eigen +{ + +template <> +struct NumTraits + : NumTraits // permits to get the epsilon, dummy_precision, lowest, highest functions +{ + typedef cadet::active Real; + typedef cadet::active NonInteger; + typedef cadet::active Nested; + + enum + { + IsComplex = 0, + IsInteger = 0, + IsSigned = 1, + RequireInitialization = 1, + ReadCost = 1, + AddCost = 3, + MulCost = 3 + }; +}; + +// specify return types concerning active double scalar operations for eigen +template <> +struct ScalarBinaryOpTraits> +{ + typedef cadet::active ReturnType; +}; +template <> struct ScalarBinaryOpTraits> +{ + typedef cadet::active ReturnType; +}; +template <> struct ScalarBinaryOpTraits> +{ + typedef cadet::active ReturnType; +}; +} // namespace Eigen #endif -#endif // LIBCADET_AUTODIFF_HPP_ +#endif // LIBCADET_AUTODIFF_HPP_ diff --git a/src/libcadet/Benchmark.hpp b/src/libcadet/Benchmark.hpp index c74bb519b..2bd42b343 100644 --- a/src/libcadet/Benchmark.hpp +++ b/src/libcadet/Benchmark.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides benchmark functionality. */ @@ -20,35 +20,40 @@ #ifdef CADET_BENCHMARK_MODE - #include "common/Timer.hpp" +#include "common/Timer.hpp" - #define BENCH_TIMER(name) mutable ::cadet::Timer name; - #define BENCH_START(name) name.start() - #define BENCH_STOP(name) name.stop() +#define BENCH_TIMER(name) mutable ::cadet::Timer name; +#define BENCH_START(name) name.start() +#define BENCH_STOP(name) name.stop() - /** - * @brief Starts and stops a given timer on construction and desctruction, respectively - */ - class BenchmarkScope +/** + * @brief Starts and stops a given timer on construction and desctruction, respectively + */ +class BenchmarkScope +{ +public: + BenchmarkScope(::cadet::Timer& timer) : _timer(timer) { - public: - - BenchmarkScope(::cadet::Timer& timer) : _timer(timer) { _timer.start(); } - ~BenchmarkScope() { _timer.stop(); } + _timer.start(); + } + ~BenchmarkScope() + { + _timer.stop(); + } - private: - ::cadet::Timer& _timer; - }; +private: + ::cadet::Timer& _timer; +}; - #define BENCH_SCOPE(name) BenchmarkScope scope##name(name) +#define BENCH_SCOPE(name) BenchmarkScope scope##name(name) #else - #define BENCH_TIMER(name) - #define BENCH_START(name) - #define BENCH_STOP(name) - #define BENCH_SCOPE(name) +#define BENCH_TIMER(name) +#define BENCH_START(name) +#define BENCH_STOP(name) +#define BENCH_SCOPE(name) #endif -#endif // LIBCADET_BENCHMARK_HPP_ +#endif // LIBCADET_BENCHMARK_HPP_ diff --git a/src/libcadet/BindingModelFactory.cpp b/src/libcadet/BindingModelFactory.cpp index d984e3d00..c0feb9512 100644 --- a/src/libcadet/BindingModelFactory.cpp +++ b/src/libcadet/BindingModelFactory.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -17,105 +17,115 @@ namespace cadet { - namespace model - { - namespace binding - { - void registerDummyModel(std::unordered_map>& bindings); - void registerLinearModel(std::unordered_map>& bindings); - void registerLangmuirModel(std::unordered_map>& bindings); - void registerAntiLangmuirModel(std::unordered_map>& bindings); - void registerBiLangmuirModel(std::unordered_map>& bindings); - void registerKumarLangmuirModel(std::unordered_map>& bindings); - void registerMobilePhaseModulatorLangmuirModel(std::unordered_map>& bindings); - void registerExtendedMobilePhaseModulatorLangmuirModel(std::unordered_map>& bindings); - void registerStericMassActionModel(std::unordered_map>& bindings); - void registerNoJacobianStericMassActionModel(std::unordered_map>& bindings); - void registerBiStericMassActionModel(std::unordered_map>& bindings); - void registerMultiStateStericMassActionModel(std::unordered_map>& bindings); -// void registerSimplifiedMultiStateStericMassActionModel(std::unordered_map>& bindings); - void registerSelfAssociationModel(std::unordered_map>& bindings); - void registerSaskaModel(std::unordered_map>& bindings); - void registerMultiComponentSpreadingModel(std::unordered_map>& bindings); - void registerGeneralizedIonExchangeModel(std::unordered_map>& bindings); - void registerColloidalModel(std::unordered_map>& bindings); - void registerFreundlichLDFModel(std::unordered_map>& bindings); - void registerLangmuirLDFModel(std::unordered_map>& bindings); - void registerLangmuirLDFCModel(std::unordered_map>& bindings); - void registerBiLangmuirLDFModel(std::unordered_map>& bindings); - void registerHICWaterOnHydrophobicSurfacesModel(std::unordered_map>& bindings); - void registerHICConstantWaterActivityModel(std::unordered_map>& bindings); - } - } +namespace model +{ +namespace binding +{ +void registerDummyModel(std::unordered_map>& bindings); +void registerLinearModel(std::unordered_map>& bindings); +void registerLangmuirModel(std::unordered_map>& bindings); +void registerAntiLangmuirModel(std::unordered_map>& bindings); +void registerBiLangmuirModel(std::unordered_map>& bindings); +void registerKumarLangmuirModel(std::unordered_map>& bindings); +void registerMobilePhaseModulatorLangmuirModel( + std::unordered_map>& bindings); +void registerExtendedMobilePhaseModulatorLangmuirModel( + std::unordered_map>& bindings); +void registerStericMassActionModel(std::unordered_map>& bindings); +void registerNoJacobianStericMassActionModel( + std::unordered_map>& bindings); +void registerBiStericMassActionModel(std::unordered_map>& bindings); +void registerMultiStateStericMassActionModel( + std::unordered_map>& bindings); +// void registerSimplifiedMultiStateStericMassActionModel(std::unordered_map>& bindings); +void registerSelfAssociationModel(std::unordered_map>& bindings); +void registerSaskaModel(std::unordered_map>& bindings); +void registerMultiComponentSpreadingModel( + std::unordered_map>& bindings); +void registerGeneralizedIonExchangeModel( + std::unordered_map>& bindings); +void registerColloidalModel(std::unordered_map>& bindings); +void registerFreundlichLDFModel(std::unordered_map>& bindings); +void registerLangmuirLDFModel(std::unordered_map>& bindings); +void registerLangmuirLDFCModel(std::unordered_map>& bindings); +void registerBiLangmuirLDFModel(std::unordered_map>& bindings); +void registerHICWaterOnHydrophobicSurfacesModel( + std::unordered_map>& bindings); +void registerHICConstantWaterActivityModel( + std::unordered_map>& bindings); +} // namespace binding +} // namespace model - BindingModelFactory::BindingModelFactory() - { - // Register all BindingModels here - model::binding::registerDummyModel(_bindingModels); - model::binding::registerLinearModel(_bindingModels); - model::binding::registerLangmuirModel(_bindingModels); - model::binding::registerAntiLangmuirModel(_bindingModels); - model::binding::registerBiLangmuirModel(_bindingModels); - model::binding::registerKumarLangmuirModel(_bindingModels); - model::binding::registerMobilePhaseModulatorLangmuirModel(_bindingModels); - model::binding::registerExtendedMobilePhaseModulatorLangmuirModel(_bindingModels); - model::binding::registerStericMassActionModel(_bindingModels); - model::binding::registerNoJacobianStericMassActionModel(_bindingModels); - model::binding::registerBiStericMassActionModel(_bindingModels); - model::binding::registerMultiStateStericMassActionModel(_bindingModels); -// model::binding::registerSimplifiedMultiStateStericMassActionModel(_bindingModels); - model::binding::registerSelfAssociationModel(_bindingModels); - model::binding::registerSaskaModel(_bindingModels); - model::binding::registerMultiComponentSpreadingModel(_bindingModels); - model::binding::registerGeneralizedIonExchangeModel(_bindingModels); - model::binding::registerColloidalModel(_bindingModels); - model::binding::registerFreundlichLDFModel(_bindingModels); - model::binding::registerLangmuirLDFModel(_bindingModels); - model::binding::registerLangmuirLDFCModel(_bindingModels); - model::binding::registerBiLangmuirLDFModel(_bindingModels); - model::binding::registerHICWaterOnHydrophobicSurfacesModel(_bindingModels); - model::binding::registerHICConstantWaterActivityModel(_bindingModels); - registerModel(); - } +BindingModelFactory::BindingModelFactory() +{ + // Register all BindingModels here + model::binding::registerDummyModel(_bindingModels); + model::binding::registerLinearModel(_bindingModels); + model::binding::registerLangmuirModel(_bindingModels); + model::binding::registerAntiLangmuirModel(_bindingModels); + model::binding::registerBiLangmuirModel(_bindingModels); + model::binding::registerKumarLangmuirModel(_bindingModels); + model::binding::registerMobilePhaseModulatorLangmuirModel(_bindingModels); + model::binding::registerExtendedMobilePhaseModulatorLangmuirModel(_bindingModels); + model::binding::registerStericMassActionModel(_bindingModels); + model::binding::registerNoJacobianStericMassActionModel(_bindingModels); + model::binding::registerBiStericMassActionModel(_bindingModels); + model::binding::registerMultiStateStericMassActionModel(_bindingModels); + // model::binding::registerSimplifiedMultiStateStericMassActionModel(_bindingModels); + model::binding::registerSelfAssociationModel(_bindingModels); + model::binding::registerSaskaModel(_bindingModels); + model::binding::registerMultiComponentSpreadingModel(_bindingModels); + model::binding::registerGeneralizedIonExchangeModel(_bindingModels); + model::binding::registerColloidalModel(_bindingModels); + model::binding::registerFreundlichLDFModel(_bindingModels); + model::binding::registerLangmuirLDFModel(_bindingModels); + model::binding::registerLangmuirLDFCModel(_bindingModels); + model::binding::registerBiLangmuirLDFModel(_bindingModels); + model::binding::registerHICWaterOnHydrophobicSurfacesModel(_bindingModels); + model::binding::registerHICConstantWaterActivityModel(_bindingModels); + registerModel(); +} - BindingModelFactory::~BindingModelFactory() { } +BindingModelFactory::~BindingModelFactory() +{ +} - template - void BindingModelFactory::registerModel(const std::string& name) - { - _bindingModels[name] = []() { return new BindingModel_t(); }; - } +template void BindingModelFactory::registerModel(const std::string& name) +{ + _bindingModels[name] = []() { return new BindingModel_t(); }; +} - template - void BindingModelFactory::registerModel() - { - registerModel(BindingModel_t::identifier()); - } +template void BindingModelFactory::registerModel() +{ + registerModel(BindingModel_t::identifier()); +} - model::IBindingModel* BindingModelFactory::create(const std::string& name) const +model::IBindingModel* BindingModelFactory::create(const std::string& name) const +{ + const auto it = _bindingModels.find(name); + if (it == _bindingModels.end()) { - const auto it = _bindingModels.find(name); - if (it == _bindingModels.end()) - { - // BindingModel was not found - return nullptr; - } - - // Call factory function (thanks to type erasure of std::function we can store - // all factory functions in one container) - return it->second(); + // BindingModel was not found + return nullptr; } - void BindingModelFactory::registerModel(const std::string& name, std::function factory) - { - if (_bindingModels.find(name) == _bindingModels.end()) - _bindingModels[name] = factory; - else - throw InvalidParameterException("IBindingModel implementation with the name " + name + " is already registered and cannot be overwritten"); - } + // Call factory function (thanks to type erasure of std::function we can store + // all factory functions in one container) + return it->second(); +} - bool BindingModelFactory::exists(const std::string& name) const - { - return _bindingModels.find(name) != _bindingModels.end(); - } +void BindingModelFactory::registerModel(const std::string& name, std::function factory) +{ + if (_bindingModels.find(name) == _bindingModels.end()) + _bindingModels[name] = factory; + else + throw InvalidParameterException("IBindingModel implementation with the name " + name + + " is already registered and cannot be overwritten"); +} + +bool BindingModelFactory::exists(const std::string& name) const +{ + return _bindingModels.find(name) != _bindingModels.end(); +} } // namespace cadet diff --git a/src/libcadet/BindingModelFactory.hpp b/src/libcadet/BindingModelFactory.hpp index 9d763a29e..69e21920f 100644 --- a/src/libcadet/BindingModelFactory.hpp +++ b/src/libcadet/BindingModelFactory.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the BindingModelFactory */ @@ -25,66 +25,65 @@ namespace cadet { - namespace model - { - class IBindingModel; - } +namespace model +{ +class IBindingModel; +} +/** + * @brief Creates binding models + */ +class BindingModelFactory +{ +public: /** - * @brief Creates binding models + * @brief Construct the BindingModelFactory + * @details All internal binding models are registered here. */ - class BindingModelFactory - { - public: - /** - * @brief Construct the BindingModelFactory - * @details All internal binding models are registered here. - */ - BindingModelFactory(); + BindingModelFactory(); - ~BindingModelFactory(); + ~BindingModelFactory(); - /** - * @brief Creates binding models with the given @p name - * @param [in] name Name of the binding model - * @return The binding model or @c NULL if a binding model with this name does not exist - */ - model::IBindingModel* create(const std::string& name) const; + /** + * @brief Creates binding models with the given @p name + * @param [in] name Name of the binding model + * @return The binding model or @c NULL if a binding model with this name does not exist + */ + model::IBindingModel* create(const std::string& name) const; - /** - * @brief Registers the given binding model implementation - * @param [in] name Name of the IBindingModel implementation - * @param [in] factory Function that creates an object of the IBindingModel class - */ - void registerModel(const std::string& name, std::function factory); + /** + * @brief Registers the given binding model implementation + * @param [in] name Name of the IBindingModel implementation + * @param [in] factory Function that creates an object of the IBindingModel class + */ + void registerModel(const std::string& name, std::function factory); - /** - * @brief Returns whether a binding model of the given name @p name exists - * @param [in] name Name of the binding model - * @return @c true if a binding model of this name exists, otherwise @c false - */ - bool exists(const std::string& name) const; - protected: + /** + * @brief Returns whether a binding model of the given name @p name exists + * @param [in] name Name of the binding model + * @return @c true if a binding model of this name exists, otherwise @c false + */ + bool exists(const std::string& name) const; - /** - * @brief Registers an IBindingModel - * @param [in] name Name of the binding model - * @tparam BindingModel_t Type of the binding model - */ - template - void registerModel(const std::string& name); +protected: + /** + * @brief Registers an IBindingModel + * @param [in] name Name of the binding model + * @tparam BindingModel_t Type of the binding model + */ + template void registerModel(const std::string& name); - /** - * @brief Registers an IBindingModel - * @details The name of the binding model is inferred from the static function IBindingModel::identifier(). - * @tparam BindingModel_t Type of the binding model - */ - template - void registerModel(); + /** + * @brief Registers an IBindingModel + * @details The name of the binding model is inferred from the static function IBindingModel::identifier(). + * @tparam BindingModel_t Type of the binding model + */ + template void registerModel(); - std::unordered_map> _bindingModels; //!< Map with factory functions - }; + std::unordered_map> + _bindingModels; //!< Map with factory functions +}; } // namespace cadet -#endif // LIBCADET_BINDINGMODELFACTORY_HPP_ +#endif // LIBCADET_BINDINGMODELFACTORY_HPP_ diff --git a/src/libcadet/CompileTimeConfig.hpp.in b/src/libcadet/CompileTimeConfig.hpp.in index 899b658cd..739c079c8 100644 --- a/src/libcadet/CompileTimeConfig.hpp.in +++ b/src/libcadet/CompileTimeConfig.hpp.in @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and diff --git a/src/libcadet/ConfigurationHelper.hpp b/src/libcadet/ConfigurationHelper.hpp index 73f925353..149e3c844 100644 --- a/src/libcadet/ConfigurationHelper.hpp +++ b/src/libcadet/ConfigurationHelper.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides an interface to create different subentitites (e.g., IInletProfile) and provide * some aids for configuring models. */ @@ -27,13 +27,13 @@ namespace cadet class IInletProfile; class IExternalFunction; - namespace model - { - class IBindingModel; - class IDynamicReactionModel; - class IParameterStateDependence; - class IParameterParameterDependence; - } +namespace model +{ +class IBindingModel; +class IDynamicReactionModel; +class IParameterStateDependence; +class IParameterParameterDependence; +} // namespace model /** * @brief Provides means to create subentities (e.g., IInletProfile, IBindingModel) @@ -41,7 +41,6 @@ class IExternalFunction; class IConfigHelper { public: - /** * @brief Creates an IInletProfile object of the given @p type * @details The caller owns the returned IInletProfile object. @@ -121,4 +120,4 @@ class IConfigHelper } // namespace cadet -#endif // LIBCADET_CONFIGURATIONHELPER_HPP_ +#endif // LIBCADET_CONFIGURATIONHELPER_HPP_ diff --git a/src/libcadet/FactoryFuncs.cpp b/src/libcadet/FactoryFuncs.cpp index f2c9ba1a8..8d894f301 100644 --- a/src/libcadet/FactoryFuncs.cpp +++ b/src/libcadet/FactoryFuncs.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -17,35 +17,47 @@ namespace cadet { - IModelBuilder* createModelBuilder() - { - return new ModelBuilder(); - } +IModelBuilder* createModelBuilder() +{ + return new ModelBuilder(); +} - void destroyModelBuilder(IModelBuilder* const builder) CADET_NOEXCEPT - { - delete builder; - } +void destroyModelBuilder(IModelBuilder* const builder) CADET_NOEXCEPT +{ + delete builder; +} - ISimulator* createSimulator() - { - return new Simulator(); - } +ISimulator* createSimulator() +{ + return new Simulator(); +} - void destroySimulator(ISimulator* const sim) CADET_NOEXCEPT - { - delete sim; - } +void destroySimulator(ISimulator* const sim) CADET_NOEXCEPT +{ + delete sim; +} } // namespace cadet extern "C" { - cadet::IModelBuilder* cadetCreateModelBuilder() { return cadet::createModelBuilder(); } + cadet::IModelBuilder* cadetCreateModelBuilder() + { + return cadet::createModelBuilder(); + } - void cadetDestroyModelBuilder(cadet::IModelBuilder* const builder) { cadet::destroyModelBuilder(builder); } + void cadetDestroyModelBuilder(cadet::IModelBuilder* const builder) + { + cadet::destroyModelBuilder(builder); + } - cadet::ISimulator* cadetCreateSimulator() { return cadet::createSimulator(); } + cadet::ISimulator* cadetCreateSimulator() + { + return cadet::createSimulator(); + } - void cadetDestroySimulator(cadet::ISimulator* const sim) { cadet::destroySimulator(sim); } + void cadetDestroySimulator(cadet::ISimulator* const sim) + { + cadet::destroySimulator(sim); + } } diff --git a/src/libcadet/LapackInterface.hpp b/src/libcadet/LapackInterface.hpp index 991709dee..c4e87f9f1 100644 --- a/src/libcadet/LapackInterface.hpp +++ b/src/libcadet/LapackInterface.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,146 +11,151 @@ // ============================================================================= #ifdef CADET_LAPACK_64BIT_INT - #include +#include #endif namespace cadet { - #ifdef CADET_LAPACK_64BIT_INT - typedef int64_t lapackInt_t; - #else - typedef int lapackInt_t; - #endif +#ifdef CADET_LAPACK_64BIT_INT +typedef int64_t lapackInt_t; +#else +typedef int lapackInt_t; +#endif - // Determine LAPACK function names - #ifdef CADET_LAPACK_TRAILING_UNDERSCORE - #ifdef CADET_LAPACK_UPPERCASE - #define LAPACK_FUNC(nameLower, nameUpper) nameUpper##_ - #else - #define LAPACK_FUNC(nameLower, nameUpper) nameLower##_ - #endif - #else - #ifdef CADET_LAPACK_PRECEDING_UNDERSCORE - #ifdef CADET_LAPACK_UPPERCASE - #define LAPACK_FUNC(nameLower, nameUpper) _##nameUpper - #else - #define LAPACK_FUNC(nameLower, nameUpper) _##nameLower - #endif - #else - #ifdef CADET_LAPACK_UPPERCASE - #define LAPACK_FUNC(nameLower, nameUpper) nameUpper - #else - #define LAPACK_FUNC(nameLower, nameUpper) nameLower - #endif - #endif - #endif +// Determine LAPACK function names +#ifdef CADET_LAPACK_TRAILING_UNDERSCORE +#ifdef CADET_LAPACK_UPPERCASE +#define LAPACK_FUNC(nameLower, nameUpper) nameUpper##_ +#else +#define LAPACK_FUNC(nameLower, nameUpper) nameLower##_ +#endif +#else +#ifdef CADET_LAPACK_PRECEDING_UNDERSCORE +#ifdef CADET_LAPACK_UPPERCASE +#define LAPACK_FUNC(nameLower, nameUpper) _##nameUpper +#else +#define LAPACK_FUNC(nameLower, nameUpper) _##nameLower +#endif +#else +#ifdef CADET_LAPACK_UPPERCASE +#define LAPACK_FUNC(nameLower, nameUpper) nameUpper +#else +#define LAPACK_FUNC(nameLower, nameUpper) nameLower +#endif +#endif +#endif - extern "C" void LAPACK_FUNC(dgbtrf,DGBTRF) (lapackInt_t* m, lapackInt_t* n, lapackInt_t* kl, lapackInt_t* ku, double* ab, - lapackInt_t* ldab, lapackInt_t* ipiv, lapackInt_t* info); +extern "C" void LAPACK_FUNC(dgbtrf, DGBTRF)(lapackInt_t* m, lapackInt_t* n, lapackInt_t* kl, lapackInt_t* ku, + double* ab, lapackInt_t* ldab, lapackInt_t* ipiv, lapackInt_t* info); - extern "C" void LAPACK_FUNC(dgbtrs,DGBTRS) (char* trans, lapackInt_t* n, lapackInt_t* kl, lapackInt_t* ku, lapackInt_t* nrhs, - double* ab, lapackInt_t* ldab, lapackInt_t* ipiv, double* b, lapackInt_t* ldb, lapackInt_t* info); +extern "C" void LAPACK_FUNC(dgbtrs, DGBTRS)(char* trans, lapackInt_t* n, lapackInt_t* kl, lapackInt_t* ku, + lapackInt_t* nrhs, double* ab, lapackInt_t* ldab, lapackInt_t* ipiv, + double* b, lapackInt_t* ldb, lapackInt_t* info); - extern "C" void LAPACK_FUNC(dgbmv,DGBMV) (char* trans, lapackInt_t* m, lapackInt_t* n, lapackInt_t* kl, lapackInt_t* ku, - double* alpha, double* a, lapackInt_t* lda, double* x, lapackInt_t* incx, double* beta, - double* y, lapackInt_t* incy); +extern "C" void LAPACK_FUNC(dgbmv, DGBMV)(char* trans, lapackInt_t* m, lapackInt_t* n, lapackInt_t* kl, lapackInt_t* ku, + double* alpha, double* a, lapackInt_t* lda, double* x, lapackInt_t* incx, + double* beta, double* y, lapackInt_t* incy); - extern "C" void LAPACK_FUNC(dgetrf,DGETRF) (lapackInt_t* m, lapackInt_t* n, double* A, lapackInt_t* lda, lapackInt_t* ipiv, lapackInt_t* info); +extern "C" void LAPACK_FUNC(dgetrf, DGETRF)(lapackInt_t* m, lapackInt_t* n, double* A, lapackInt_t* lda, + lapackInt_t* ipiv, lapackInt_t* info); - extern "C" void LAPACK_FUNC(dgetrs,DGETRS) (char* trans, lapackInt_t* n, lapackInt_t* nrhs, double* a, - lapackInt_t* lda, lapackInt_t* ipiv, double* b, lapackInt_t* ldb, lapackInt_t* info); +extern "C" void LAPACK_FUNC(dgetrs, DGETRS)(char* trans, lapackInt_t* n, lapackInt_t* nrhs, double* a, lapackInt_t* lda, + lapackInt_t* ipiv, double* b, lapackInt_t* ldb, lapackInt_t* info); - extern "C" void LAPACK_FUNC(dgemv,DGEMV) (char* trans, lapackInt_t* m, lapackInt_t* n, - double* alpha, double* a, lapackInt_t* lda, double* x, lapackInt_t* incx, double* beta, - double* y, lapackInt_t* incy); +extern "C" void LAPACK_FUNC(dgemv, DGEMV)(char* trans, lapackInt_t* m, lapackInt_t* n, double* alpha, double* a, + lapackInt_t* lda, double* x, lapackInt_t* incx, double* beta, double* y, + lapackInt_t* incy); - extern "C" void LAPACK_FUNC(dgels,DGELS) (char* trans, lapackInt_t* M, lapackInt_t* N, lapackInt_t* NRHS, double* A, - lapackInt_t* LDA, double* B, lapackInt_t* LDB, double* WORK, lapackInt_t* LWORK, lapackInt_t* INFO); +extern "C" void LAPACK_FUNC(dgels, DGELS)(char* trans, lapackInt_t* M, lapackInt_t* N, lapackInt_t* NRHS, double* A, + lapackInt_t* LDA, double* B, lapackInt_t* LDB, double* WORK, + lapackInt_t* LWORK, lapackInt_t* INFO); - extern "C" void LAPACK_FUNC(dgelqf,DGELQF) (lapackInt_t* M, lapackInt_t* N, double* A, lapackInt_t* LDA, double* TAU, double* WORK, - lapackInt_t* LWORK, lapackInt_t* INFO); +extern "C" void LAPACK_FUNC(dgelqf, DGELQF)(lapackInt_t* M, lapackInt_t* N, double* A, lapackInt_t* LDA, double* TAU, + double* WORK, lapackInt_t* LWORK, lapackInt_t* INFO); - extern "C" void LAPACK_FUNC(dormlq,DORMLQ) (char* SIDE, char* TRANS, lapackInt_t* M, lapackInt_t* N, lapackInt_t* K, double* A, - lapackInt_t* LDA, double* TAU, double* C, lapackInt_t* LDC, double* WORK, lapackInt_t* LWORK, lapackInt_t* INFO); +extern "C" void LAPACK_FUNC(dormlq, DORMLQ)(char* SIDE, char* TRANS, lapackInt_t* M, lapackInt_t* N, lapackInt_t* K, + double* A, lapackInt_t* LDA, double* TAU, double* C, lapackInt_t* LDC, + double* WORK, lapackInt_t* LWORK, lapackInt_t* INFO); - extern "C" void LAPACK_FUNC(dtrtrs,DTRTRS) (char* UPLO, char* TRANS, char* DIAG, lapackInt_t* N, lapackInt_t* NRHS, double* A, lapackInt_t* LDA, - double* B, lapackInt_t* LDB, lapackInt_t* INFO); +extern "C" void LAPACK_FUNC(dtrtrs, DTRTRS)(char* UPLO, char* TRANS, char* DIAG, lapackInt_t* N, lapackInt_t* NRHS, + double* A, lapackInt_t* LDA, double* B, lapackInt_t* LDB, + lapackInt_t* INFO); - #ifdef CADET_LAPACK_TRAILING_UNDERSCORE - #ifdef CADET_LAPACK_UPPERCASE - #define LapackFactorDenseBanded DGBTRF_ - #define LapackSolveDenseBanded DGBTRS_ - #define LapackMultiplyDenseBanded DGBMV_ - #define LapackFactorDense DGETRF_ - #define LapackSolveDense DGETRS_ - #define LapackMultiplyDense DGEMV_ - #define LapackDenseLeastSquares DGELS_ - #define LapackFactorLQDense DGELQF_ - #define LapackMultiplyFactorizedQ DORMLQ_ - #define LapackSolveTriangular DTRTRS_ - #else - #define LapackFactorDenseBanded dgbtrf_ - #define LapackSolveDenseBanded dgbtrs_ - #define LapackMultiplyDenseBanded dgbmv_ - #define LapackFactorDense dgetrf_ - #define LapackSolveDense dgetrs_ - #define LapackMultiplyDense dgemv_ - #define LapackDenseLeastSquares dgels_ - #define LapackFactorLQDense dgelqf_ - #define LapackMultiplyFactorizedQ dormlq_ - #define LapackSolveTriangular dtrtrs_ - #endif - #else - #ifdef CADET_LAPACK_PRECEDING_UNDERSCORE - #ifdef CADET_LAPACK_UPPERCASE - #define LapackFactorDenseBanded _DGBTRF - #define LapackSolveDenseBanded _DGBTRS - #define LapackMultiplyDenseBanded _DGBMV - #define LapackFactorDense _DGETRF - #define LapackSolveDense _DGETRS - #define LapackMultiplyDense _DGEMV - #define LapackDenseLeastSquares _DGELS - #define LapackFactorLQDense _DGELQF - #define LapackMultiplyFactorizedQ _DORMLQ - #define LapackSolveTriangular _DTRTRS - #else - #define LapackFactorDenseBanded _dgbtrf - #define LapackSolveDenseBanded _dgbtrs - #define LapackMultiplyDenseBanded _dgbmv - #define LapackFactorDense _dgetrf - #define LapackSolveDense _dgetrs - #define LapackMultiplyDense _dgemv - #define LapackDenseLeastSquares _dgels - #define LapackFactorLQDense _dgelqf - #define LapackMultiplyFactorizedQ _dormlq - #define LapackSolveTriangular _dtrtrs - #endif - #else - #ifdef CADET_LAPACK_UPPERCASE - #define LapackFactorDenseBanded DGBTRF - #define LapackSolveDenseBanded DGBTRS - #define LapackMultiplyDenseBanded DGBMV - #define LapackFactorDense DGETRF - #define LapackSolveDense DGETRS - #define LapackMultiplyDense DGEMV - #define LapackDenseLeastSquares DGELS - #define LapackFactorLQDense DGELQF - #define LapackMultiplyFactorizedQ DORMLQ - #define LapackSolveTriangular DTRTRS - #else - #define LapackFactorDenseBanded dgbtrf - #define LapackSolveDenseBanded dgbtrs - #define LapackMultiplyDenseBanded dgbmv - #define LapackFactorDense dgetrf - #define LapackSolveDense dgetrs - #define LapackMultiplyDense dgemv - #define LapackDenseLeastSquares dgels - #define LapackFactorLQDense dgelqf - #define LapackMultiplyFactorizedQ dormlq - #define LapackSolveTriangular dtrtrs - #endif - #endif - #endif +#ifdef CADET_LAPACK_TRAILING_UNDERSCORE +#ifdef CADET_LAPACK_UPPERCASE +#define LapackFactorDenseBanded DGBTRF_ +#define LapackSolveDenseBanded DGBTRS_ +#define LapackMultiplyDenseBanded DGBMV_ +#define LapackFactorDense DGETRF_ +#define LapackSolveDense DGETRS_ +#define LapackMultiplyDense DGEMV_ +#define LapackDenseLeastSquares DGELS_ +#define LapackFactorLQDense DGELQF_ +#define LapackMultiplyFactorizedQ DORMLQ_ +#define LapackSolveTriangular DTRTRS_ +#else +#define LapackFactorDenseBanded dgbtrf_ +#define LapackSolveDenseBanded dgbtrs_ +#define LapackMultiplyDenseBanded dgbmv_ +#define LapackFactorDense dgetrf_ +#define LapackSolveDense dgetrs_ +#define LapackMultiplyDense dgemv_ +#define LapackDenseLeastSquares dgels_ +#define LapackFactorLQDense dgelqf_ +#define LapackMultiplyFactorizedQ dormlq_ +#define LapackSolveTriangular dtrtrs_ +#endif +#else +#ifdef CADET_LAPACK_PRECEDING_UNDERSCORE +#ifdef CADET_LAPACK_UPPERCASE +#define LapackFactorDenseBanded _DGBTRF +#define LapackSolveDenseBanded _DGBTRS +#define LapackMultiplyDenseBanded _DGBMV +#define LapackFactorDense _DGETRF +#define LapackSolveDense _DGETRS +#define LapackMultiplyDense _DGEMV +#define LapackDenseLeastSquares _DGELS +#define LapackFactorLQDense _DGELQF +#define LapackMultiplyFactorizedQ _DORMLQ +#define LapackSolveTriangular _DTRTRS +#else +#define LapackFactorDenseBanded _dgbtrf +#define LapackSolveDenseBanded _dgbtrs +#define LapackMultiplyDenseBanded _dgbmv +#define LapackFactorDense _dgetrf +#define LapackSolveDense _dgetrs +#define LapackMultiplyDense _dgemv +#define LapackDenseLeastSquares _dgels +#define LapackFactorLQDense _dgelqf +#define LapackMultiplyFactorizedQ _dormlq +#define LapackSolveTriangular _dtrtrs +#endif +#else +#ifdef CADET_LAPACK_UPPERCASE +#define LapackFactorDenseBanded DGBTRF +#define LapackSolveDenseBanded DGBTRS +#define LapackMultiplyDenseBanded DGBMV +#define LapackFactorDense DGETRF +#define LapackSolveDense DGETRS +#define LapackMultiplyDense DGEMV +#define LapackDenseLeastSquares DGELS +#define LapackFactorLQDense DGELQF +#define LapackMultiplyFactorizedQ DORMLQ +#define LapackSolveTriangular DTRTRS +#else +#define LapackFactorDenseBanded dgbtrf +#define LapackSolveDenseBanded dgbtrs +#define LapackMultiplyDenseBanded dgbmv +#define LapackFactorDense dgetrf +#define LapackSolveDense dgetrs +#define LapackMultiplyDense dgemv +#define LapackDenseLeastSquares dgels +#define LapackFactorLQDense dgelqf +#define LapackMultiplyFactorizedQ dormlq +#define LapackSolveTriangular dtrtrs +#endif +#endif +#endif - #undef LAPACK_FUNC -} +#undef LAPACK_FUNC +} // namespace cadet diff --git a/src/libcadet/LocalVector.hpp b/src/libcadet/LocalVector.hpp index 50d466c14..9c363f7c2 100644 --- a/src/libcadet/LocalVector.hpp +++ b/src/libcadet/LocalVector.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines local memory versions of some data structures */ @@ -40,14 +40,12 @@ namespace util * @param [in] delta Number of bytes to move the pointer * @return Pointer at new location */ -template -inline T* advancePointer(T* p, std::ptrdiff_t delta) CADET_NOEXCEPT +template inline T* advancePointer(T* p, std::ptrdiff_t delta) CADET_NOEXCEPT { return reinterpret_cast(reinterpret_cast(p) + delta); } -template -inline T* advancePointer(void* p, std::ptrdiff_t delta) CADET_NOEXCEPT +template inline T* advancePointer(void* p, std::ptrdiff_t delta) CADET_NOEXCEPT { return reinterpret_cast(reinterpret_cast(p) + delta); } @@ -68,16 +66,17 @@ inline void* alignPtr(void* ptr, std::size_t alignment) CADET_NOEXCEPT * @brief Represents a fixed size std::vector in a contiguous buffer * @details The contents of the std::vector are appended to the memory of the LocalVector * in a contiguous buffer. Thus, the full data is represented in a linear slab of memory. - * + * * The LocalVector cannot be expanded or shrunk, but its contents can be changed. * Memory is not owned by this class and must be created or destroyed elsewhere. * @tparam T Type of data stored in the vector */ -template -class LocalVector +template class LocalVector { public: - LocalVector() CADET_NOEXCEPT : _size(0), _data(nullptr) { } + LocalVector() CADET_NOEXCEPT : _size(0), _data(nullptr) + { + } ~LocalVector() CADET_NOEXCEPT { if (_data) @@ -96,19 +95,46 @@ class LocalVector inline LocalVector& operator=(LocalVector&& cpy) = default; #endif - inline T& operator[](std::size_t idx) { return _data[idx]; } - inline const T& operator[](std::size_t idx) const { return _data[idx]; } + inline T& operator[](std::size_t idx) + { + return _data[idx]; + } + inline const T& operator[](std::size_t idx) const + { + return _data[idx]; + } - inline T* data() CADET_NOEXCEPT { return _data; } - inline T const* data() const CADET_NOEXCEPT { return _data; } + inline T* data() CADET_NOEXCEPT + { + return _data; + } + inline T const* data() const CADET_NOEXCEPT + { + return _data; + } - inline std::size_t size() const CADET_NOEXCEPT { return _size; } + inline std::size_t size() const CADET_NOEXCEPT + { + return _size; + } - inline T& front() { return _data[0]; } - inline const T& front() const { return _data[0]; } + inline T& front() + { + return _data[0]; + } + inline const T& front() const + { + return _data[0]; + } - inline T& back() { return _data[_size - 1]; } - inline const T& back() const { return _data[_size - 1]; } + inline T& back() + { + return _data[_size - 1]; + } + inline const T& back() const + { + return _data[_size - 1]; + } /** * @brief Assigns the data of the given vector to this instance @@ -138,33 +164,41 @@ class LocalVector * @param [in] v Data to represent in a linearized local buffer * @return Size in bytes required for representing the given data */ - static inline std::size_t requiredMemoryFor(const std::vector& v) CADET_NOEXCEPT { return requiredMemoryFor(v.size()); } - static inline std::size_t requiredMemoryFor(std::size_t n) CADET_NOEXCEPT { return sizeof(LocalVector) + alignof(LocalVector) + sizeof(T) * n + alignof(T); } + static inline std::size_t requiredMemoryFor(const std::vector& v) CADET_NOEXCEPT + { + return requiredMemoryFor(v.size()); + } + static inline std::size_t requiredMemoryFor(std::size_t n) CADET_NOEXCEPT + { + return sizeof(LocalVector) + alignof(LocalVector) + sizeof(T) * n + alignof(T); + } protected: - LocalVector(std::size_t size) CADET_NOEXCEPT : _size(size), _data(nullptr) { } + LocalVector(std::size_t size) CADET_NOEXCEPT : _size(size), _data(nullptr) + { + } std::size_t _size; T* _data; }; - /** * @brief Represents a fixed size SlicedVector in a contiguous buffer * @details The contents of the SlicedVector are appended to the memory of the LocalSlicedVector * in a contiguous buffer. Thus, the full data is represented in a linear slab of memory. - * + * * The LocalSlicedVector cannot be expanded or shrunk, but its contents can be changed. * Memory is not owned by this class and must be created or destroyed elsewhere. * @tparam T Type of data stored in the sliced vector */ -template -class LocalSlicedVector +template class LocalSlicedVector { public: typedef typename SlicedVector::size_type size_type; - LocalSlicedVector() CADET_NOEXCEPT : _indexSize(0), _values(nullptr), _index(nullptr) { } + LocalSlicedVector() CADET_NOEXCEPT : _indexSize(0), _values(nullptr), _index(nullptr) + { + } ~LocalSlicedVector() CADET_NOEXCEPT { if (_values) @@ -189,27 +223,39 @@ class LocalSlicedVector * @brief Checks whether this LocalSlicedVector is empty * @return @c true if it is empty, otherwise @c false */ - inline bool empty() const { return _indexSize == 1; } + inline bool empty() const + { + return _indexSize == 1; + } /** * @brief Returns the number of slices * @return Number of slices */ - inline size_type slices() const CADET_NOEXCEPT { return _indexSize - 1; } + inline size_type slices() const CADET_NOEXCEPT + { + return _indexSize - 1; + } /** * @brief Returns a pointer to the first element of a given slice * @param [in] idxSlice Index of the slice * @return Pointer to the first element of the given slice */ - inline T const* operator[](size_type idxSlice) const { return _values + _index[idxSlice]; } + inline T const* operator[](size_type idxSlice) const + { + return _values + _index[idxSlice]; + } /** * @brief Returns a pointer to the first element of a given slice * @param [in] idxSlice Index of the slice * @return Pointer to the first element of the given slice */ - inline T* operator[](size_type idxSlice) { return _values + _index[idxSlice]; } + inline T* operator[](size_type idxSlice) + { + return _values + _index[idxSlice]; + } /** * @brief Returns the element at the given position in the given slice @@ -217,7 +263,10 @@ class LocalSlicedVector * @param [in] idxElem Index of the element within the slice * @return Element at the given index of the given slice */ - inline const T& operator()(size_type idxSlice, size_type idxElem) const { return _values[_index[idxSlice] + idxElem]; } + inline const T& operator()(size_type idxSlice, size_type idxElem) const + { + return _values[_index[idxSlice] + idxElem]; + } /** * @brief Returns the element at the given position in the given slice @@ -225,21 +274,30 @@ class LocalSlicedVector * @param [in] idxElem Index of the element within the slice * @return Element at the given index of the given slice */ - inline T& operator()(size_type idxSlice, size_type idxElem) { return _values[_index[idxSlice] + idxElem]; } + inline T& operator()(size_type idxSlice, size_type idxElem) + { + return _values[_index[idxSlice] + idxElem]; + } /** * @brief Returns the element at the given linear index * @param [in] idx Linear index of the requested element * @return Element at the given linear index */ - inline T& native(size_type idx) { return _values[idx]; } + inline T& native(size_type idx) + { + return _values[idx]; + } /** * @brief Returns the element at the given linear index * @param [in] idx Linear index of the requested element * @return Element at the given linear index */ - inline const T& native(size_type idx) const { return _values[idx]; } + inline const T& native(size_type idx) const + { + return _values[idx]; + } /** * @brief Returns the element at the given position in the given slice @@ -247,7 +305,10 @@ class LocalSlicedVector * @param [in] idxElem Index of the element within the slice * @return Element at the given index of the given slice */ - inline const T& at(size_type idxSlice, size_type idxElem) const { return _values[_index[idxSlice] + idxElem]; } + inline const T& at(size_type idxSlice, size_type idxElem) const + { + return _values[_index[idxSlice] + idxElem]; + } /** * @brief Returns the element at the given position in the given slice @@ -255,32 +316,41 @@ class LocalSlicedVector * @param [in] idxElem Index of the element within the slice * @return Element at the given index of the given slice */ - inline T& at(size_type idxSlice, size_type idxElem) { return _values[_index[idxSlice] + idxElem]; } + inline T& at(size_type idxSlice, size_type idxElem) + { + return _values[_index[idxSlice] + idxElem]; + } /** * @brief Returns a pointer to the first element of a given slice * @param [in] idxSlice Index of the slice * @return Pointer to the first element of the given slice */ - inline T const* at(size_type idxSlice) const { return _values + _index[idxSlice]; } + inline T const* at(size_type idxSlice) const + { + return _values + _index[idxSlice]; + } /** * @brief Returns a pointer to the first element of a given slice * @param [in] idxSlice Index of the slice * @return Pointer to the first element of the given slice */ - inline T* at(size_type idxSlice) { return _values + _index[idxSlice]; } + inline T* at(size_type idxSlice) + { + return _values + _index[idxSlice]; + } /** * @brief Returns a pointer to the first element of the last slice * @return Pointer to the first element of the last slice */ - inline T const* back() const + inline T const* back() const { cadet_assert(!empty()); return &_values[_index[_indexSize - 2]]; } - + inline T* back() { cadet_assert(!empty()); @@ -292,21 +362,30 @@ class LocalSlicedVector * @param [in] idxSlice Index of the slice * @return Number of elmenets in the slice */ - inline size_type sliceSize(size_type idxSlice) const { return _index[idxSlice + 1] - _index[idxSlice]; } + inline size_type sliceSize(size_type idxSlice) const + { + return _index[idxSlice + 1] - _index[idxSlice]; + } /** * @brief Returns the total number of stored items * @details All items in every slice are counted. * @return Total number of stored items */ - inline size_type size() const CADET_NOEXCEPT { return _index[_indexSize - 1]; } + inline size_type size() const CADET_NOEXCEPT + { + return _index[_indexSize - 1]; + } /** * @brief Returns the offset of the slice with the given index in the linear array * @param [in] idx Index of the slice * @return Offset of the given slice in the linearized storage */ - inline size_type sliceOffset(size_type idx) const { return _index[idx]; } + inline size_type sliceOffset(size_type idx) const + { + return _index[idx]; + } /** * @brief Returns a pointer to the first element of the underlying linear array @@ -378,18 +457,20 @@ class LocalSlicedVector */ static inline std::size_t requiredMemoryFor(const SlicedVector& v) CADET_NOEXCEPT { - return sizeof(LocalSlicedVector) + alignof(LocalSlicedVector) + (v.slices() + 1) * sizeof(size_type) + alignof(size_type) + v.size() * sizeof(T) + alignof(T); + return sizeof(LocalSlicedVector) + alignof(LocalSlicedVector) + (v.slices() + 1) * sizeof(size_type) + + alignof(size_type) + v.size() * sizeof(T) + alignof(T); } protected: - LocalSlicedVector(std::size_t nSlices) CADET_NOEXCEPT : _indexSize(nSlices), _values(nullptr), _index(nullptr) { } + LocalSlicedVector(std::size_t nSlices) CADET_NOEXCEPT : _indexSize(nSlices), _values(nullptr), _index(nullptr) + { + } std::size_t _indexSize; //!< Size of the _index array - T* _values; //!< Data - size_type* _index; //!< Index array with start and end indices of the slices in _values + T* _values; //!< Data + size_type* _index; //!< Index array with start and end indices of the slices in _values }; - /** * @brief Returns the active data of the container * @param [in] v Container @@ -418,7 +499,7 @@ inline active* dataOfLocalVersion(LocalSlicedVector& v) CADET_NOEXCEPT * For containers using additional memory, which may be dynamically allocated, * we need some more memory. The amount of additional memory is computed by * this family of functions. - * + * * Note that this is a generous estimate since additional space for alignment * is included. * @param [in] v Data @@ -438,58 +519,68 @@ inline std::size_t memoryForDataOf(const active& v) CADET_NOEXCEPT return 0; } -template -inline std::size_t memoryForDataOf(const std::vector& v) CADET_NOEXCEPT +template inline std::size_t memoryForDataOf(const std::vector& v) CADET_NOEXCEPT { return v.size() * sizeof(T) + alignof(T); } -template -inline std::size_t memoryForDataOf(const SlicedVector& v) CADET_NOEXCEPT +template inline std::size_t memoryForDataOf(const SlicedVector& v) CADET_NOEXCEPT { // Required memory: Slice index array + items array - return (v.slices() + 1) * sizeof(typename SlicedVector::size_type) + alignof(typename SlicedVector::size_type) + v.size() * sizeof(T) + alignof(T); + return (v.slices() + 1) * sizeof(typename SlicedVector::size_type) + + alignof(typename SlicedVector::size_type) + v.size() * sizeof(T) + alignof(T); } -template -inline std::size_t memoryForDataOf(const LocalVector& v) CADET_NOEXCEPT +template inline std::size_t memoryForDataOf(const LocalVector& v) CADET_NOEXCEPT { return v.size() * sizeof(T) + alignof(T); } -template -inline std::size_t memoryForDataOf(const LocalSlicedVector& v) CADET_NOEXCEPT +template inline std::size_t memoryForDataOf(const LocalSlicedVector& v) CADET_NOEXCEPT { // Required memory: Slice index array + items array - return (v.slices() + 1) * sizeof(typename LocalSlicedVector::size_type) + alignof(typename LocalSlicedVector::size_type) + v.size() * sizeof(T) + alignof(T); + return (v.slices() + 1) * sizeof(typename LocalSlicedVector::size_type) + + alignof(typename LocalSlicedVector::size_type) + v.size() * sizeof(T) + alignof(T); } - /** * @brief Provides the type of the local version of a given data type */ -template -struct localVersionOf { }; +template struct localVersionOf +{ +}; -template <> -struct localVersionOf { typedef double type; }; +template <> struct localVersionOf +{ + typedef double type; +}; -template <> -struct localVersionOf { typedef active type; }; +template <> struct localVersionOf +{ + typedef active type; +}; -template -struct localVersionOf> { typedef LocalVector type; }; +template struct localVersionOf> +{ + typedef LocalVector type; +}; -template -struct localVersionOf> { typedef LocalSlicedVector type; }; +template struct localVersionOf> +{ + typedef LocalSlicedVector type; +}; -template -struct localVersionOf> { typedef LocalVector type; }; +template struct localVersionOf> +{ + typedef LocalVector type; +}; -template -struct localVersionOf> { typedef LocalSlicedVector type; }; +template struct localVersionOf> +{ + typedef LocalSlicedVector type; +}; } // namespace util } // namespace cadet -#endif // LIBCADET_LOCALVECTOR_HPP_ +#endif // LIBCADET_LOCALVECTOR_HPP_ diff --git a/src/libcadet/Logging.cpp b/src/libcadet/Logging.cpp index b7aa0aa25..ffb39b6c2 100644 --- a/src/libcadet/Logging.cpp +++ b/src/libcadet/Logging.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -14,44 +14,55 @@ #include "cadet/cadet.h" #ifndef CADET_LOGGING_DISABLE - namespace +namespace +{ +class CStyleLogReceiver : public cadet::ILogReceiver +{ +public: + CStyleLogReceiver() : _hdlr(nullptr) + { + } + CStyleLogReceiver(cdtLogHandler hdlr) : _hdlr(hdlr) + { + } + virtual ~CStyleLogReceiver() CADET_NOEXCEPT { - class CStyleLogReceiver : public cadet::ILogReceiver - { - public: - CStyleLogReceiver() : _hdlr(nullptr) { } - CStyleLogReceiver(cdtLogHandler hdlr) : _hdlr(hdlr) { } - virtual ~CStyleLogReceiver() CADET_NOEXCEPT { } - - virtual void message(const char* file, const char* func, const unsigned int line, cadet::LogLevel lvl, const char* lvlStr, const char* message) - { - _hdlr(file, func, line, static_cast::type>(lvl), lvlStr, message); - } - - void handler(cdtLogHandler hdlr) CADET_NOEXCEPT { _hdlr = hdlr; } - - protected: - cdtLogHandler _hdlr; - }; - - /** - * @brief Receiver of all log messages created in the libcadet library - */ - cadet::ILogReceiver* logReceiver = nullptr; - - /** - * @brief Receiver for C API - */ - CStyleLogReceiver cApiLogReceiver; } - template <> - cadet::LogLevel cadet::log::RuntimeFilteringLogger::_minLvl = cadet::LogLevel::Trace; + virtual void message(const char* file, const char* func, const unsigned int line, cadet::LogLevel lvl, + const char* lvlStr, const char* message) + { + _hdlr(file, func, line, static_cast::type>(lvl), lvlStr, + message); + } - #ifdef __clang__ - // Silence -Wundefined-var-template warning - template class cadet::log::RuntimeFilteringLogger; - #endif + void handler(cdtLogHandler hdlr) CADET_NOEXCEPT + { + _hdlr = hdlr; + } + +protected: + cdtLogHandler _hdlr; +}; + +/** + * @brief Receiver of all log messages created in the libcadet library + */ +cadet::ILogReceiver* logReceiver = nullptr; + +/** + * @brief Receiver for C API + */ +CStyleLogReceiver cApiLogReceiver; +} // namespace + +template <> +cadet::LogLevel cadet::log::RuntimeFilteringLogger::_minLvl = cadet::LogLevel::Trace; + +#ifdef __clang__ +// Silence -Wundefined-var-template warning +template class cadet::log::RuntimeFilteringLogger; +#endif #endif namespace cadet @@ -59,35 +70,42 @@ namespace cadet #ifdef CADET_LOGGING_DISABLE - void setLogReceiver(ILogReceiver* const recv) { } - void setLogLevel(LogLevel lvl) { } - LogLevel getLogLevel() { return LogLevel::None; } +void setLogReceiver(ILogReceiver* const recv) +{ +} +void setLogLevel(LogLevel lvl) +{ +} +LogLevel getLogLevel() +{ + return LogLevel::None; +} #else - namespace log - { - void emitLog(const char* file, const char* func, const unsigned int line, LogLevel lvl, const char* message) - { - if (logReceiver) - logReceiver->message(file, func, line, lvl, to_string(lvl), message); - } - } +namespace log +{ +void emitLog(const char* file, const char* func, const unsigned int line, LogLevel lvl, const char* message) +{ + if (logReceiver) + logReceiver->message(file, func, line, lvl, to_string(lvl), message); +} +} // namespace log - void setLogReceiver(ILogReceiver* const recv) - { - logReceiver = recv; - } +void setLogReceiver(ILogReceiver* const recv) +{ + logReceiver = recv; +} - void setLogLevel(LogLevel lvl) - { - cadet::log::RuntimeFilteringLogger::level(lvl); - } +void setLogLevel(LogLevel lvl) +{ + cadet::log::RuntimeFilteringLogger::level(lvl); +} - LogLevel getLogLevel() - { - return cadet::log::RuntimeFilteringLogger::level(); - } +LogLevel getLogLevel() +{ + return cadet::log::RuntimeFilteringLogger::level(); +} #endif diff --git a/src/libcadet/Logging.hpp b/src/libcadet/Logging.hpp index 2ebe0dddf..4933cc679 100644 --- a/src/libcadet/Logging.hpp +++ b/src/libcadet/Logging.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Adapter for transmitting the log messages to a receiver. */ @@ -26,54 +26,56 @@ namespace cadet namespace log { - /** - * @brief Dispatches a log message to a receiver - * @param [in] file Filename in which the log message was raised - * @param [in] func Name of the function (implementation defined @c __func__ variable) - * @param [in] line Number of the line in which the log message was raised - * @param [in] lvl LogLevel representing the severity of the message - * @param [in] message Message string - */ - void emitLog(const char* file, const char* func, const unsigned int line, LogLevel lvl, const char* message); - - /** - * @brief Implements a standard formatting policy - */ - class LibCadetFormattingPolicy : public FormattingPolicyBase +/** + * @brief Dispatches a log message to a receiver + * @param [in] file Filename in which the log message was raised + * @param [in] func Name of the function (implementation defined @c __func__ variable) + * @param [in] line Number of the line in which the log message was raised + * @param [in] lvl LogLevel representing the severity of the message + * @param [in] message Message string + */ +void emitLog(const char* file, const char* func, const unsigned int line, LogLevel lvl, const char* message); + +/** + * @brief Implements a standard formatting policy + */ +class LibCadetFormattingPolicy : public FormattingPolicyBase +{ +public: + template + static inline void format(receiver_t& recv, const char* fileName, const char* funcName, unsigned int line, + LogLevel lvl, const paramList_t& p) { - public: - template - static inline void format(receiver_t& recv, const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, const paramList_t& p) - { - writeParams(recv, lvl, p); - } - }; - - /** - * @brief Sends all messages to std::cout - */ - class EmitterWritePolicy : public NonBufferedWritePolicyBase + writeParams(recv, lvl, p); + } +}; + +/** + * @brief Sends all messages to std::cout + */ +class EmitterWritePolicy : public NonBufferedWritePolicyBase +{ +public: + static inline void writeLine(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, + const std::string& msg) { - public: - static inline void writeLine(const char* fileName, const char* funcName, unsigned int line, LogLevel lvl, const std::string& msg) - { - emitLog(fileName, funcName, line, lvl, msg.c_str()); - } - }; + emitLog(fileName, funcName, line, lvl, msg.c_str()); + } +}; - typedef NonFilteringLogger GlobalLogger; +typedef NonFilteringLogger GlobalLogger; #ifndef CADET_LOGGING_DISABLE - typedef Logger, LogLevel::CADET_LOGLEVEL_MIN> DoubleFilterLogger; - - #ifdef __clang__ - // Silence -Wundefined-var-template warning by indicating an - // explicit instantiation of this template in another translation unit - template<> LogLevel RuntimeFilteringLogger::_minLvl; - extern template class RuntimeFilteringLogger; - #endif +typedef Logger, LogLevel::CADET_LOGLEVEL_MIN> DoubleFilterLogger; + +#ifdef __clang__ +// Silence -Wundefined-var-template warning by indicating an +// explicit instantiation of this template in another translation unit +template <> LogLevel RuntimeFilteringLogger::_minLvl; +extern template class RuntimeFilteringLogger; +#endif #else - typedef Logger DiscardingLogger; +typedef Logger DiscardingLogger; #endif } // namespace log @@ -81,24 +83,28 @@ namespace log #ifndef CADET_LOGGING_DISABLE - /** - * @brief Base for logging macros - * @details Note that because of the usage pattern - *
LOG(Info) << "My log line " << arg1;
- * no semicolon is appended. - */ - #define LOG(lvl) cadet::log::DoubleFilterLogger::statement(__FILE__, __func__, __LINE__) = cadet::log::DoubleFilterLogger::template createMessage() +/** + * @brief Base for logging macros + * @details Note that because of the usage pattern + *
LOG(Info) << "My log line " << arg1;
+ * no semicolon is appended. + */ +#define LOG(lvl) \ + cadet::log::DoubleFilterLogger::statement(__FILE__, __func__, __LINE__) = \ + cadet::log::DoubleFilterLogger::template createMessage() #else - /** - * @brief Base for logging macros - * @details Note that because of the usage pattern - *
LOG(Info) << "My log line " << arg1;
- * no semicolon is appended. - */ - #define LOG(lvl) cadet::log::DiscardingLogger::statement(__FILE__, __func__, __LINE__) = cadet::log::DiscardingLogger::template createMessage() +/** + * @brief Base for logging macros + * @details Note that because of the usage pattern + *
LOG(Info) << "My log line " << arg1;
+ * no semicolon is appended. + */ +#define LOG(lvl) \ + cadet::log::DiscardingLogger::statement(__FILE__, __func__, __LINE__) = \ + cadet::log::DiscardingLogger::template createMessage() #endif -#endif // LIBCADET_LOGGING_IMPL_HPP_ +#endif // LIBCADET_LOGGING_IMPL_HPP_ diff --git a/src/libcadet/LoggingUtils.hpp b/src/libcadet/LoggingUtils.hpp index 1a233334e..d992a4e2b 100644 --- a/src/libcadet/LoggingUtils.hpp +++ b/src/libcadet/LoggingUtils.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Utilities for logging various data types. */ @@ -20,43 +20,47 @@ #ifndef CADET_LOGGING_DISABLE // #include "cadet/SolutionExporter.hpp" - #include "AutoDiff.hpp" +#include "AutoDiff.hpp" - #include +#include #endif namespace cadet { namespace log { - /** - * @brief Container for logging arrays given by pointer and number of elements - * @tparam T Type of the underlying array items - */ - template - struct VectorPtr - { - const T* data; - unsigned int nElem; - - VectorPtr(T const* d, unsigned int n) : data(d), nElem(n) { } - }; - - /** - * @brief Container for logging matrices given by pointer to linear array, matrix size, and storage order - * @tparam T Type of the underlying array items - */ - template - struct MatrixPtr +/** + * @brief Container for logging arrays given by pointer and number of elements + * @tparam T Type of the underlying array items + */ +template struct VectorPtr +{ + const T* data; + unsigned int nElem; + + VectorPtr(T const* d, unsigned int n) : data(d), nElem(n) { - const T* data; - unsigned int nRows; - unsigned int nCols; - bool colMajor; + } +}; + +/** + * @brief Container for logging matrices given by pointer to linear array, matrix size, and storage order + * @tparam T Type of the underlying array items + */ +template struct MatrixPtr +{ + const T* data; + unsigned int nRows; + unsigned int nCols; + bool colMajor; - MatrixPtr(T const* d, unsigned int nr, unsigned int nc) : data(d), nRows(nr), nCols(nc), colMajor(false) { } - MatrixPtr(T const* d, unsigned int nr, unsigned int nc, bool cm) : data(d), nRows(nr), nCols(nc), colMajor(cm) { } - }; + MatrixPtr(T const* d, unsigned int nr, unsigned int nc) : data(d), nRows(nr), nCols(nc), colMajor(false) + { + } + MatrixPtr(T const* d, unsigned int nr, unsigned int nc, bool cm) : data(d), nRows(nr), nCols(nc), colMajor(cm) + { + } +}; #ifndef CADET_LOGGING_DISABLE /* @@ -82,7 +86,7 @@ namespace log os << parPtr[j] << ","; os << parPtr[parStride-1] << "]\n"; } - + os << "flux = ["; const unsigned int nFluxDof = v.numFluxDofs(); double const* fluxPtr = v.flux(); @@ -92,90 +96,88 @@ namespace log return os; } */ - - inline std::ostream& operator<<(std::ostream& os, const cadet::active& v) + +inline std::ostream& operator<<(std::ostream& os, const cadet::active& v) +{ + os << static_cast(v) << " ["; + if (cadet::ad::getDirections() > 0) { - os << static_cast(v) << " ["; - if (cadet::ad::getDirections() > 0) - { - for (unsigned int i = 0; i < cadet::ad::getDirections()-1; ++i) - os << v.getADValue(i) << ", "; - os << v.getADValue(cadet::ad::getDirections()-1); - } - os << "]"; - return os; + for (unsigned int i = 0; i < cadet::ad::getDirections() - 1; ++i) + os << v.getADValue(i) << ", "; + os << v.getADValue(cadet::ad::getDirections() - 1); } + os << "]"; + return os; +} - template - inline std::ostream& operator<<(std::ostream& os, const cadet::log::VectorPtr& v) +template inline std::ostream& operator<<(std::ostream& os, const cadet::log::VectorPtr& v) +{ + os << "["; + if (v.nElem > 0) { - os << "["; - if (v.nElem > 0) - { - for (unsigned int i = 0; i < v.nElem-1; ++i) - os << v.data[i] << ","; - os << v.data[v.nElem - 1]; - } - os << "]"; - return os; + for (unsigned int i = 0; i < v.nElem - 1; ++i) + os << v.data[i] << ","; + os << v.data[v.nElem - 1]; } + os << "]"; + return os; +} - template - inline std::ostream& operator<<(std::ostream& os, const cadet::log::MatrixPtr& v) +template inline std::ostream& operator<<(std::ostream& os, const cadet::log::MatrixPtr& v) +{ + os << "["; + if (v.nRows * v.nCols > 0) { - os << "["; - if (v.nRows * v.nCols > 0) + if (v.colMajor) { - if (v.colMajor) + // All rows but last + for (unsigned int i = 0; i < v.nRows - 1; ++i) { - // All rows but last - for (unsigned int i = 0; i < v.nRows-1; ++i) - { - // All columns but last - for (unsigned int j = 0; j < v.nCols - 1; ++j) - { - os << v.data[j * v.nRows + i] << ","; - } - // Last column - os << v.data[(v.nCols - 1) * v.nRows + i] << ";"; - } - - // Last row + // All columns but last for (unsigned int j = 0; j < v.nCols - 1; ++j) { - os << v.data[j * v.nRows + v.nRows - 1] << ","; + os << v.data[j * v.nRows + i] << ","; } - os << v.data[v.nCols * v.nRows - 1]; + // Last column + os << v.data[(v.nCols - 1) * v.nRows + i] << ";"; } - else - { - // All rows but last - for (unsigned int i = 0; i < v.nRows-1; ++i) - { - // All columns but last - for (unsigned int j = 0; j < v.nCols - 1; ++j) - { - os << v.data[i * v.nCols + j] << ","; - } - // Last column - os << v.data[(i + 1) * v.nCols - 1] << ";"; - } - // Last row + // Last row + for (unsigned int j = 0; j < v.nCols - 1; ++j) + { + os << v.data[j * v.nRows + v.nRows - 1] << ","; + } + os << v.data[v.nCols * v.nRows - 1]; + } + else + { + // All rows but last + for (unsigned int i = 0; i < v.nRows - 1; ++i) + { + // All columns but last for (unsigned int j = 0; j < v.nCols - 1; ++j) { - os << v.data[(v.nRows - 1) * v.nCols + j] << ","; + os << v.data[i * v.nCols + j] << ","; } - os << v.data[v.nCols * v.nRows - 1]; + // Last column + os << v.data[(i + 1) * v.nCols - 1] << ";"; } + + // Last row + for (unsigned int j = 0; j < v.nCols - 1; ++j) + { + os << v.data[(v.nRows - 1) * v.nCols + j] << ","; + } + os << v.data[v.nCols * v.nRows - 1]; } - os << "]"; - return os; } + os << "]"; + return os; +} #endif } // namespace log } // namespace cadet -#endif // LIBCADET_LOGGING_UTILS_HPP_ +#endif // LIBCADET_LOGGING_UTILS_HPP_ diff --git a/src/libcadet/MathUtil.hpp b/src/libcadet/MathUtil.hpp index 8902a7cbf..e82f0b0d2 100644 --- a/src/libcadet/MathUtil.hpp +++ b/src/libcadet/MathUtil.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -17,20 +17,23 @@ #include "cadet/cadetCompilerInfo.hpp" -namespace cadet +namespace cadet { - /** - * @brief Squares the given value - * @details Calculates @f$ x * x @f$ - * @param [in] x Value that is squared - * @return Squared value - */ - inline double sqr(const double x) CADET_NOEXCEPT { return x * x; } +/** + * @brief Squares the given value + * @details Calculates @f$ x * x @f$ + * @param [in] x Value that is squared + * @return Squared value + */ +inline double sqr(const double x) CADET_NOEXCEPT +{ + return x * x; +} #if defined(ACTIVE_SFAD) || defined(ACTIVE_SETFAD) #endif } // namespace cadet -#endif // LIBCADET_MATHUTIL_HPP_ +#endif // LIBCADET_MATHUTIL_HPP_ diff --git a/src/libcadet/Memory.hpp b/src/libcadet/Memory.hpp index 4c3d27700..855f6afcd 100644 --- a/src/libcadet/Memory.hpp +++ b/src/libcadet/Memory.hpp @@ -22,897 +22,998 @@ #include #include #ifdef CADET_DEBUG - #include +#include #endif namespace cadet { - /** - * @brief Treats a memory block as array of the given type - * @details Treats a given memory block as an array of the given type. - * It is assumed that the pointer @p mem is correctly aligned for the given type @p T. - * - * The array has to be released after use by calling releaseRawArray(). - * - * See - * https://stackoverflow.com/questions/15254/can-placement-new-for-arrays-be-used-in-a-portable-way - * https://stackoverflow.com/questions/41624685/is-placement-new-legally-required-for-putting-an-int-into-a-char-array - * https://stackoverflow.com/questions/13466556/aligned-storage-and-strict-aliasing - * - * @param [in] mem Pointer to memory block - * @param [in] numElements Number of array elements - * @return Pointer to first array element of the given type - */ - template - inline T* rawMemoryAsArray(void* mem, unsigned int numElements) - { - if (cadet_unlikely((mem == nullptr) || (numElements == 0))) - return nullptr; +/** + * @brief Treats a memory block as array of the given type + * @details Treats a given memory block as an array of the given type. + * It is assumed that the pointer @p mem is correctly aligned for the given type @p T. + * + * The array has to be released after use by calling releaseRawArray(). + * + * See + * https://stackoverflow.com/questions/15254/can-placement-new-for-arrays-be-used-in-a-portable-way + * https://stackoverflow.com/questions/41624685/is-placement-new-legally-required-for-putting-an-int-into-a-char-array + * https://stackoverflow.com/questions/13466556/aligned-storage-and-strict-aliasing + * + * @param [in] mem Pointer to memory block + * @param [in] numElements Number of array elements + * @return Pointer to first array element of the given type + */ +template inline T* rawMemoryAsArray(void* mem, unsigned int numElements) +{ + if (cadet_unlikely((mem == nullptr) || (numElements == 0))) + return nullptr; + + // Call constructor on every element + T* const ptr = reinterpret_cast(mem); + for (T* cur = ptr + 1; cur != ptr + numElements; ++cur) + new (cur) T; + + return new (ptr) T; - // Call constructor on every element - T* const ptr = reinterpret_cast(mem); - for (T* cur = ptr + 1; cur != ptr + numElements; ++cur) - new(cur) T; + // This could be done differently in C++17: Do a full loop + // from 0 to numElements - 1 and use std::launder() on the + // first element instead of taking the result of placement + // new operator `new(ptr) T`. +} - return new(ptr) T; +/** + * @brief Releases an array created from rawMemoryAsArray() + * @details Calls the destructor on each array element. + * This is a no-op for integral types (e.g., int, double). + * + * @param [in] mem Pointer to first element of array in raw memory block + * @param [in] numElements Number of array elements + */ +template inline void releaseRawArray(T* const mem, unsigned int numElements) +{ + if (cadet_unlikely((mem == nullptr) || (numElements == 0))) + return; + + // Call destructor on every element + // This should actually be in reverse order (last items might depend on first items) + for (T* cur = mem; cur != mem + numElements; ++cur) + cur->~T(); +} + +/** + * @brief Releases a scalar + * @details Calls the destructor of the item. + * This is a no-op for integral types (e.g., int, double). + * + * @param [in] mem Pointer to scalar item in raw memory block + */ +template inline void releaseRawScalar(T* const mem) +{ + if (cadet_unlikely(mem == nullptr)) + return; + + mem->~T(); +} - // This could be done differently in C++17: Do a full loop - // from 0 to numElements - 1 and use std::launder() on the - // first element instead of taking the result of placement - // new operator `new(ptr) T`. +/** + * @brief Manages a buffer for arrays of various types + * @details An ArrayPool manages a block of memory that can be used to create arrays of + * various types. It is initialized to allocate a block of memory big enough to + * hold the maximum number of elements of the biggest type. The user can then + * create arrays of different types and sizes without actually allocating new + * memory (saves performance). Note that the elements have to be default constructible. + */ +class ArrayPool +{ +public: + /** + * @brief Creates an empty ArrayPool + */ + ArrayPool() : _mem(nullptr), _numElements(0) + { +#ifdef CADET_DEBUG + _capacity = 0; +#endif } /** - * @brief Releases an array created from rawMemoryAsArray() - * @details Calls the destructor on each array element. - * This is a no-op for integral types (e.g., int, double). + * @brief Creates an ArrayPool with the given size in bytes + * @details The size of the pool should be computed by taking @c sizeof(BiggestType) * maxNumElements * - * @param [in] mem Pointer to first element of array in raw memory block - * @param [in] numElements Number of array elements + * @param [in] maxBytes Size of the pool in bytes + * @param [in] maxAlign Most strict alignment of all types that the pool will hold */ - template - inline void releaseRawArray(T* const mem, unsigned int numElements) + ArrayPool(unsigned int maxBytes, unsigned int maxAlign) : _mem(::operator new(maxBytes + maxAlign)), _numElements(0) { - if (cadet_unlikely((mem == nullptr) || (numElements == 0))) - return; - - // Call destructor on every element - // This should actually be in reverse order (last items might depend on first items) - for (T* cur = mem; cur != mem + numElements; ++cur) - cur->~T(); +#ifdef CADET_DEBUG + _capacity = maxBytes; +#endif } - /** - * @brief Releases a scalar - * @details Calls the destructor of the item. - * This is a no-op for integral types (e.g., int, double). + * @brief Creates an ArrayPool with the given size in bytes + * @details The size of the pool should be computed by taking @c sizeof(BiggestType) * maxNumElements * - * @param [in] mem Pointer to scalar item in raw memory block + * @param [in] maxBytes Size of the pool in bytes */ - template - inline void releaseRawScalar(T* const mem) + ArrayPool(unsigned int maxBytes) : ArrayPool(maxBytes, 0) { - if (cadet_unlikely(mem == nullptr)) - return; - - mem->~T(); } + ~ArrayPool() CADET_NOEXCEPT + { + ::operator delete(_mem); + } /** - * @brief Manages a buffer for arrays of various types - * @details An ArrayPool manages a block of memory that can be used to create arrays of - * various types. It is initialized to allocate a block of memory big enough to - * hold the maximum number of elements of the biggest type. The user can then - * create arrays of different types and sizes without actually allocating new - * memory (saves performance). Note that the elements have to be default constructible. + * @brief Resizes the memory pool to the given size + * @details Already created arrays are invalid as the underlying memory pool is reallocated. + * The user has to make sure that the created array is destroyed prior to calling resize(). + * + * @param [in] maxBytes Size of the pool in bytes */ - class ArrayPool + inline void resize(unsigned int maxBytes) { - public: - - /** - * @brief Creates an empty ArrayPool - */ - ArrayPool() : _mem(nullptr), _numElements(0) - { -#ifdef CADET_DEBUG - _capacity = 0; -#endif - } - - /** - * @brief Creates an ArrayPool with the given size in bytes - * @details The size of the pool should be computed by taking @c sizeof(BiggestType) * maxNumElements - * - * @param [in] maxBytes Size of the pool in bytes - * @param [in] maxAlign Most strict alignment of all types that the pool will hold - */ - ArrayPool(unsigned int maxBytes, unsigned int maxAlign) : _mem(::operator new(maxBytes + maxAlign)), _numElements(0) - { -#ifdef CADET_DEBUG - _capacity = maxBytes; -#endif - } - - /** - * @brief Creates an ArrayPool with the given size in bytes - * @details The size of the pool should be computed by taking @c sizeof(BiggestType) * maxNumElements - * - * @param [in] maxBytes Size of the pool in bytes - */ - ArrayPool(unsigned int maxBytes) : ArrayPool(maxBytes, 0) { } - - ~ArrayPool() CADET_NOEXCEPT { ::operator delete(_mem); } - - /** - * @brief Resizes the memory pool to the given size - * @details Already created arrays are invalid as the underlying memory pool is reallocated. - * The user has to make sure that the created array is destroyed prior to calling resize(). - * - * @param [in] maxBytes Size of the pool in bytes - */ - inline void resize(unsigned int maxBytes) - { - resize(maxBytes, 0); - } + resize(maxBytes, 0); + } - /** - * @brief Resizes the memory pool to the given size - * @details Already created arrays are invalid as the underlying memory pool is reallocated. - * The user has to make sure that the created array is destroyed prior to calling resize(). - * - * @param [in] maxBytes Size of the pool in bytes - * @param [in] maxAlign Most strict alignment of all types that the pool will hold - */ - void resize(unsigned int maxBytes, unsigned int maxAlign) - { - ::operator delete(_mem); - _mem = ::operator new(maxBytes + maxAlign); - _numElements = 0; + /** + * @brief Resizes the memory pool to the given size + * @details Already created arrays are invalid as the underlying memory pool is reallocated. + * The user has to make sure that the created array is destroyed prior to calling resize(). + * + * @param [in] maxBytes Size of the pool in bytes + * @param [in] maxAlign Most strict alignment of all types that the pool will hold + */ + void resize(unsigned int maxBytes, unsigned int maxAlign) + { + ::operator delete(_mem); + _mem = ::operator new(maxBytes + maxAlign); + _numElements = 0; #ifdef CADET_DEBUG - _capacity = maxBytes; + _capacity = maxBytes; #endif - } + } - /** - * @brief Creates an array in the pool - * @details Since the array is always created in the same memory, previous arrays will be overwritten. - * - * @param [in] n Number of elements - * @tparam T Type of elements - * @return Pointer to array of the requested size and type - */ - template - inline T* create(unsigned int n) - { - cadet_assert(sizeof(T) * n <= _capacity); - cadet_assert(sizeof(T) % alignof(T) == 0); - _numElements = n; + /** + * @brief Creates an array in the pool + * @details Since the array is always created in the same memory, previous arrays will be overwritten. + * + * @param [in] n Number of elements + * @tparam T Type of elements + * @return Pointer to array of the requested size and type + */ + template inline T* create(unsigned int n) + { + cadet_assert(sizeof(T) * n <= _capacity); + cadet_assert(sizeof(T) % alignof(T) == 0); + _numElements = n; - // Align the memory - void* ptr = _mem; - std::size_t space = sizeof(T) + alignof(T); + // Align the memory + void* ptr = _mem; + std::size_t space = sizeof(T) + alignof(T); #ifdef CADET_DEBUG - void* const ptr2 = std::align(alignof(T), sizeof(T), ptr, space); - cadet_assert(ptr2 != nullptr); + void* const ptr2 = std::align(alignof(T), sizeof(T), ptr, space); + cadet_assert(ptr2 != nullptr); #else - std::align(alignof(T), sizeof(T), ptr, space); + std::align(alignof(T), sizeof(T), ptr, space); #endif - return rawMemoryAsArray(ptr, n); - } + return rawMemoryAsArray(ptr, n); + } - /** - * @brief Destroys a currently active array - * @details The destructor of the array elements is called - * - * @tparam T Type of elements - */ - template - inline void destroy() - { - // Get aligned pointer to first item - void* ptr = _mem; - std::size_t space = sizeof(T) + alignof(T); + /** + * @brief Destroys a currently active array + * @details The destructor of the array elements is called + * + * @tparam T Type of elements + */ + template inline void destroy() + { + // Get aligned pointer to first item + void* ptr = _mem; + std::size_t space = sizeof(T) + alignof(T); #ifdef CADET_DEBUG - void* const ptr2 = std::align(alignof(T), sizeof(T), ptr, space); - cadet_assert(ptr2); + void* const ptr2 = std::align(alignof(T), sizeof(T), ptr, space); + cadet_assert(ptr2); #else - std::align(alignof(T), sizeof(T), ptr, space); + std::align(alignof(T), sizeof(T), ptr, space); #endif - // Call destructor on every element - releaseRawArray(reinterpret_cast(ptr), _numElements); - _numElements = 0; - } + // Call destructor on every element + releaseRawArray(reinterpret_cast(ptr), _numElements); + _numElements = 0; + } - inline unsigned int numElements() const CADET_NOEXCEPT { return _numElements; } + inline unsigned int numElements() const CADET_NOEXCEPT + { + return _numElements; + } - protected: - void* _mem; //(...) for each item and closing the set with a call to commit(). + * The required size of the buffer in bytes is computed by bufferSize(). + */ +class LinearMemorySizer +{ +public: + LinearMemorySizer() : _maxSize(0), _curSize(0) + { + } /** - * @brief Tool for computing the size of a linear memory buffer - * @details A linear memory buffer can hold different sets of items. This class estimates - * the size of the buffer for different item sets. An item set is defined by - * calling add(...) for each item and closing the set with a call to commit(). - * The required size of the buffer in bytes is computed by bufferSize(). + * @brief Adds a single item of type T to the item set + * @tparam T Type of the array items */ - class LinearMemorySizer - { - public: - LinearMemorySizer() : _maxSize(0), _curSize(0) { } - - /** - * @brief Adds a single item of type T to the item set - * @tparam T Type of the array items - */ - template - inline void add() { _curSize += sizeof(T) + alignof(T); } - - /** - * @brief Adds an array of type T and size @p arraySize to the item set - * @param [in] arraySize Number of elements in the array - * @tparam T Type of the array items - */ - template - inline void add(std::size_t arraySize) { _curSize += arraySize * sizeof(T) + alignof(T); } - - /** - * @brief Adds a memory block of size @p blockSize to the item set - * @param [in] blockSize Size of the block in bytes - */ - inline void addBlock(std::size_t blockSize) { _curSize += blockSize; } - - /** - * @brief Sizes the item set such that a block of given size fits in - * @param [in] block Size of the memory block in bytes - */ - inline void fitBlock(std::size_t block) { _curSize = std::max(_curSize, block); } - - /** - * @brief Commits the current item set - */ - inline void commit() - { - _maxSize = std::max(_maxSize, _curSize); - _curSize = 0; - } - - /** - * @brief Computes the size of the buffer in bytes - * @details Computes the size of the buffer required to hold all item sets recorded so far. - * @return Size of the buffer in bytes - */ - inline std::size_t bufferSize() const { return std::max(_maxSize, _curSize); } - - private: - std::size_t _maxSize; // class ConstBufferedArray; - template class ConstBufferedScalar; + template inline void add() + { + _curSize += sizeof(T) + alignof(T); + } + /** + * @brief Adds an array of type T and size @p arraySize to the item set + * @param [in] arraySize Number of elements in the array + * @tparam T Type of the array items + */ + template inline void add(std::size_t arraySize) + { + _curSize += arraySize * sizeof(T) + alignof(T); + } /** - * @brief Handle to an array constructed in a linear buffer - * @details Calls the destructor of the elements upon destruction. - * @tparam T Underlying type of the array + * @brief Adds a memory block of size @p blockSize to the item set + * @param [in] blockSize Size of the block in bytes */ - template - class BufferedArray + inline void addBlock(std::size_t blockSize) { - public: - friend class LinearBufferAllocator; - friend class ConstBufferedArray; + _curSize += blockSize; + } - BufferedArray() : _ptr(nullptr) { } - BufferedArray(const BufferedArray&) = delete; - BufferedArray(BufferedArray&& rhs) CADET_NOEXCEPT : _ptr(rhs._ptr), _numElements(rhs._numElements) - { - rhs._ptr = nullptr; - rhs._numElements = 0; - } + /** + * @brief Sizes the item set such that a block of given size fits in + * @param [in] block Size of the memory block in bytes + */ + inline void fitBlock(std::size_t block) + { + _curSize = std::max(_curSize, block); + } - ~BufferedArray() - { - releaseRawArray(_ptr, _numElements); - } + /** + * @brief Commits the current item set + */ + inline void commit() + { + _maxSize = std::max(_maxSize, _curSize); + _curSize = 0; + } - BufferedArray& operator=(const BufferedArray&) = delete; - BufferedArray& operator=(BufferedArray&& rhs) CADET_NOEXCEPT - { - _ptr = rhs._ptr; - _numElements = rhs._numElements; + /** + * @brief Computes the size of the buffer in bytes + * @details Computes the size of the buffer required to hold all item sets recorded so far. + * @return Size of the buffer in bytes + */ + inline std::size_t bufferSize() const + { + return std::max(_maxSize, _curSize); + } - rhs._ptr = nullptr; - rhs._numElements = 0; - return *this; - } +private: + std::size_t _maxSize; // class ConstBufferedArray; +template class ConstBufferedScalar; - inline T& operator*() { return *_ptr; } - inline const T& operator*() const { return *_ptr; } +/** + * @brief Handle to an array constructed in a linear buffer + * @details Calls the destructor of the elements upon destruction. + * @tparam T Underlying type of the array + */ +template class BufferedArray +{ +public: + friend class LinearBufferAllocator; + friend class ConstBufferedArray; - inline T* operator->() { return _ptr; } - inline T* operator->() const { return _ptr; } + BufferedArray() : _ptr(nullptr) + { + } + BufferedArray(const BufferedArray&) = delete; + BufferedArray(BufferedArray&& rhs) CADET_NOEXCEPT : _ptr(rhs._ptr), _numElements(rhs._numElements) + { + rhs._ptr = nullptr; + rhs._numElements = 0; + } - explicit operator T*() const { return _ptr; } + ~BufferedArray() + { + releaseRawArray(_ptr, _numElements); + } - private: - BufferedArray(T* ptr, unsigned int numElements) : _ptr(ptr), _numElements(numElements) { } + BufferedArray& operator=(const BufferedArray&) = delete; + BufferedArray& operator=(BufferedArray&& rhs) CADET_NOEXCEPT + { + _ptr = rhs._ptr; + _numElements = rhs._numElements; - T* _ptr; // - class ConstBufferedArray + inline T& operator*() + { + return *_ptr; + } + inline const T& operator*() const { - public: - friend class LinearBufferAllocator; + return *_ptr; + } - ConstBufferedArray() : _ptr(nullptr) { } - ConstBufferedArray(const ConstBufferedArray&) = delete; - ConstBufferedArray(ConstBufferedArray&& rhs) CADET_NOEXCEPT : _ptr(rhs._ptr), _numElements(rhs._numElements) - { - rhs._ptr = nullptr; - rhs._numElements = 0; - } + inline T* operator->() + { + return _ptr; + } + inline T* operator->() const + { + return _ptr; + } - ~ConstBufferedArray() - { - releaseRawArray(_ptr, _numElements); - } + explicit operator T*() const + { + return _ptr; + } - ConstBufferedArray& operator=(const ConstBufferedArray&) = delete; - ConstBufferedArray& operator=(ConstBufferedArray&& rhs) CADET_NOEXCEPT - { - _ptr = rhs._ptr; - _numElements = rhs._numElements; +private: + BufferedArray(T* ptr, unsigned int numElements) : _ptr(ptr), _numElements(numElements) + { + } - rhs._ptr = nullptr; - rhs._numElements = 0; - return *this; - } + T* _ptr; //&& rhs) CADET_NOEXCEPT : _ptr(rhs._ptr) - { - rhs._ptr = nullptr; - rhs._numElements = 0; - } +/** + * @brief Handle to a constant array constructed in a linear buffer + * @details Calls the destructor of the elements upon destruction. + * @tparam T Underlying type of the array + */ +template class ConstBufferedArray +{ +public: + friend class LinearBufferAllocator; - ConstBufferedArray operator=(BufferedArray&& rhs) CADET_NOEXCEPT - { - _ptr = rhs._ptr; - _numElements = rhs._numElements; + ConstBufferedArray() : _ptr(nullptr) + { + } + ConstBufferedArray(const ConstBufferedArray&) = delete; + ConstBufferedArray(ConstBufferedArray&& rhs) CADET_NOEXCEPT : _ptr(rhs._ptr), _numElements(rhs._numElements) + { + rhs._ptr = nullptr; + rhs._numElements = 0; + } - rhs._ptr = nullptr; - rhs._numElements = 0; - return *this; - } + ~ConstBufferedArray() + { + releaseRawArray(_ptr, _numElements); + } - inline const T& operator[](int idx) const { return _ptr[idx]; } - inline const T& operator*() const { return *_ptr; } - inline T const* operator->() const { return _ptr; } + ConstBufferedArray& operator=(const ConstBufferedArray&) = delete; + ConstBufferedArray& operator=(ConstBufferedArray&& rhs) CADET_NOEXCEPT + { + _ptr = rhs._ptr; + _numElements = rhs._numElements; - explicit operator T const*() const { return _ptr; } + rhs._ptr = nullptr; + rhs._numElements = 0; + return *this; + } - private: - ConstBufferedArray(T* ptr, unsigned int numElements) : _ptr(ptr), _numElements(numElements) { } + ConstBufferedArray(BufferedArray&& rhs) CADET_NOEXCEPT : _ptr(rhs._ptr) + { + rhs._ptr = nullptr; + rhs._numElements = 0; + } - T const* _ptr; //&& rhs) CADET_NOEXCEPT + { + _ptr = rhs._ptr; + _numElements = rhs._numElements; + rhs._ptr = nullptr; + rhs._numElements = 0; + return *this; + } - /** - * @brief Handle to a scalar value constructed in a linear buffer - * @details Calls the destructor of the scalar value upon destruction. - * @tparam T Underlying type of the scalar - */ - template - class BufferedScalar + inline const T& operator[](int idx) const + { + return _ptr[idx]; + } + inline const T& operator*() const + { + return *_ptr; + } + inline T const* operator->() const { - public: - friend class LinearBufferAllocator; - friend class ConstBufferedScalar; + return _ptr; + } - BufferedScalar() : _ptr(nullptr) { } - BufferedScalar(const BufferedScalar&) = delete; - BufferedScalar(BufferedScalar&& rhs) CADET_NOEXCEPT : _ptr(rhs._ptr) - { - rhs._ptr = nullptr; - } + explicit operator T const*() const + { + return _ptr; + } - ~BufferedScalar() - { - if (_ptr) - _ptr->~T(); - } +private: + ConstBufferedArray(T* ptr, unsigned int numElements) : _ptr(ptr), _numElements(numElements) + { + } - BufferedScalar& operator=(const BufferedScalar&) = delete; - BufferedScalar& operator=(BufferedScalar&& rhs) CADET_NOEXCEPT - { - _ptr = rhs._ptr; - rhs._ptr = nullptr; - return *this; - } + T const* _ptr; // class BufferedScalar +{ +public: + friend class LinearBufferAllocator; + friend class ConstBufferedScalar; - inline T* operator->() { return _ptr; } - inline T* operator->() const { return _ptr; } + BufferedScalar() : _ptr(nullptr) + { + } + BufferedScalar(const BufferedScalar&) = delete; + BufferedScalar(BufferedScalar&& rhs) CADET_NOEXCEPT : _ptr(rhs._ptr) + { + rhs._ptr = nullptr; + } - explicit operator T*() const { return _ptr; } + ~BufferedScalar() + { + if (_ptr) + _ptr->~T(); + } - private: - BufferedScalar(T* ptr) : _ptr(ptr) { } + BufferedScalar& operator=(const BufferedScalar&) = delete; + BufferedScalar& operator=(BufferedScalar&& rhs) CADET_NOEXCEPT + { + _ptr = rhs._ptr; + rhs._ptr = nullptr; + return *this; + } - T* _ptr; //() + { + return _ptr; + } + inline T* operator->() const + { + return _ptr; + } - /** - * @brief Handle to a constant scalar value constructed in a linear buffer - * @details Calls the destructor of the scalar value upon destruction. - * @tparam T Underlying type of the scalar - */ - template - class ConstBufferedScalar + explicit operator T*() const { - public: - friend class LinearBufferAllocator; + return _ptr; + } - ConstBufferedScalar() : _ptr(nullptr) { } - ConstBufferedScalar(const ConstBufferedScalar&) = delete; - ConstBufferedScalar(ConstBufferedScalar&& rhs) CADET_NOEXCEPT : _ptr(rhs._ptr) - { - rhs._ptr = nullptr; - } +private: + BufferedScalar(T* ptr) : _ptr(ptr) + { + } - ~ConstBufferedScalar() - { - if (_ptr) - _ptr->~T(); - } + T* _ptr; // class ConstBufferedScalar +{ +public: + friend class LinearBufferAllocator; - ConstBufferedScalar(BufferedScalar&& rhs) CADET_NOEXCEPT : _ptr(rhs._ptr) - { - rhs._ptr = nullptr; - } + ConstBufferedScalar() : _ptr(nullptr) + { + } + ConstBufferedScalar(const ConstBufferedScalar&) = delete; + ConstBufferedScalar(ConstBufferedScalar&& rhs) CADET_NOEXCEPT : _ptr(rhs._ptr) + { + rhs._ptr = nullptr; + } - ConstBufferedScalar& operator=(BufferedScalar&& rhs) CADET_NOEXCEPT - { - _ptr = rhs._ptr; - rhs._ptr = nullptr; - return *this; - } + ~ConstBufferedScalar() + { + if (_ptr) + _ptr->~T(); + } - inline const T& operator*() const { return *_ptr; } - inline T const* operator->() const { return _ptr; } + ConstBufferedScalar& operator=(const ConstBufferedScalar&) = delete; + ConstBufferedScalar& operator=(ConstBufferedScalar&& rhs) CADET_NOEXCEPT + { + _ptr = rhs._ptr; + rhs._ptr = nullptr; + return *this; + } - explicit operator T const*() const { return _ptr; } + ConstBufferedScalar(BufferedScalar&& rhs) CADET_NOEXCEPT : _ptr(rhs._ptr) + { + rhs._ptr = nullptr; + } - private: - ConstBufferedScalar(T* ptr) : _ptr(ptr) { } + ConstBufferedScalar& operator=(BufferedScalar&& rhs) CADET_NOEXCEPT + { + _ptr = rhs._ptr; + rhs._ptr = nullptr; + return *this; + } - T const* _ptr; //() const + { + return _ptr; + } + explicit operator T const*() const + { + return _ptr; + } - /** - * @brief Linear heap allocator for scalars and arrays - * @details Uses a given buffer to allocate small objects and arrays. - */ - class LinearBufferAllocator +private: + ConstBufferedScalar(T* ptr) : _ptr(ptr) { - public: + } + + T const* _ptr; // - void advanceArray(std::size_t numElements) - { - cadet_assert(sizeof(T) % alignof(T) == 0); + /** + * @brief Advances the buffer by the size of an array without actually allocating it + * @param [in] numElements Number of array elements + * @tparam T Type of the array + */ + template void advanceArray(std::size_t numElements) + { + cadet_assert(sizeof(T) % alignof(T) == 0); - // Align _mem as required - std::size_t space = sizeof(T) + alignof(T); - void* const ptr2 = std::align(alignof(T), sizeof(T), _mem, space); - cadet_assert(ptr2 != nullptr); + // Align _mem as required + std::size_t space = sizeof(T) + alignof(T); + void* const ptr2 = std::align(alignof(T), sizeof(T), _mem, space); + cadet_assert(ptr2 != nullptr); - _mem = static_cast(_mem) + sizeof(T) * numElements; - } + _mem = static_cast(_mem) + sizeof(T) * numElements; + } - /** - * @brief Allocates an array in the buffer - * @details The lifetime of the allocated array is not managed and has to be - * terminated by the caller. This can be done by calling releaseRawArray() - * on the return value with the corresponding number of elements. - * @param [in] numElements Number of array elements - * @tparam T Type of the array - * @return Pointer to first element of array - */ - template - T* unmanagedArray(std::size_t numElements) - { - cadet_assert(sizeof(T) % alignof(T) == 0); + /** + * @brief Allocates an array in the buffer + * @details The lifetime of the allocated array is not managed and has to be + * terminated by the caller. This can be done by calling releaseRawArray() + * on the return value with the corresponding number of elements. + * @param [in] numElements Number of array elements + * @tparam T Type of the array + * @return Pointer to first element of array + */ + template T* unmanagedArray(std::size_t numElements) + { + cadet_assert(sizeof(T) % alignof(T) == 0); #ifdef CADET_DEBUG - cadet_assert(!_end || std::less()(_mem, _end)); + cadet_assert(!_end || std::less()(_mem, _end)); #endif - // Align _mem as required - std::size_t space = sizeof(T) + alignof(T); + // Align _mem as required + std::size_t space = sizeof(T) + alignof(T); #ifdef CADET_DEBUG - void* const ptr2 = std::align(alignof(T), sizeof(T), _mem, space); - cadet_assert(ptr2 != nullptr); + void* const ptr2 = std::align(alignof(T), sizeof(T), _mem, space); + cadet_assert(ptr2 != nullptr); #else - std::align(alignof(T), sizeof(T), _mem, space); + std::align(alignof(T), sizeof(T), _mem, space); #endif - // Construct elements in buffer and advance pointer - T* const ptr = rawMemoryAsArray(_mem, numElements); - _mem = static_cast(_mem) + sizeof(T) * numElements; + // Construct elements in buffer and advance pointer + T* const ptr = rawMemoryAsArray(_mem, numElements); + _mem = static_cast(_mem) + sizeof(T) * numElements; #ifdef CADET_DEBUG - cadet_assert(!_end || std::less_equal()(_mem, _end)); + cadet_assert(!_end || std::less_equal()(_mem, _end)); #endif - return ptr; - } + return ptr; + } - /** - * @brief Allocates an array in the buffer - * @details The lifetime of the array is managed by wrapping it in a BufferedArray. - * When the BufferedArray handle is destroyed, the array is released automatically. - * @param [in] numElements Number of array elements - * @tparam T Type of the array - * @return Handle to the array - */ - template - BufferedArray array(std::size_t numElements) - { - return BufferedArray(unmanagedArray(numElements), numElements); - } + /** + * @brief Allocates an array in the buffer + * @details The lifetime of the array is managed by wrapping it in a BufferedArray. + * When the BufferedArray handle is destroyed, the array is released automatically. + * @param [in] numElements Number of array elements + * @tparam T Type of the array + * @return Handle to the array + */ + template BufferedArray array(std::size_t numElements) + { + return BufferedArray(unmanagedArray(numElements), numElements); + } - /** - * @brief Advances the buffer by the size of a scalar without actually allocating it - * @tparam T Type of the scalar - */ - template - T* advanceScalar() - { - // Align _mem as required - std::size_t space = sizeof(T) + alignof(T); + /** + * @brief Advances the buffer by the size of a scalar without actually allocating it + * @tparam T Type of the scalar + */ + template T* advanceScalar() + { + // Align _mem as required + std::size_t space = sizeof(T) + alignof(T); #ifdef CADET_DEBUG - void* const ptr2 = std::align(alignof(T), sizeof(T), _mem, space); - cadet_assert(ptr2 != nullptr); + void* const ptr2 = std::align(alignof(T), sizeof(T), _mem, space); + cadet_assert(ptr2 != nullptr); #else - std::align(alignof(T), sizeof(T), _mem, space); + std::align(alignof(T), sizeof(T), _mem, space); #endif - _mem = static_cast(_mem) + sizeof(T); - } + _mem = static_cast(_mem) + sizeof(T); + } - /** - * @brief Allocates a scalar in the buffer - * @details The lifetime of the allocated scalar is not managed and has to be - * terminated by the caller. This can be done by calling releaseRawScalar() - * on the return value with the corresponding number of elements. - * @tparam T Type of the scalar - * @return Pointer to the scalar - */ - template - T* unmanagedScalar() - { + /** + * @brief Allocates a scalar in the buffer + * @details The lifetime of the allocated scalar is not managed and has to be + * terminated by the caller. This can be done by calling releaseRawScalar() + * on the return value with the corresponding number of elements. + * @tparam T Type of the scalar + * @return Pointer to the scalar + */ + template T* unmanagedScalar() + { #ifdef CADET_DEBUG - cadet_assert(!_end || std::less()(_mem, _end)); + cadet_assert(!_end || std::less()(_mem, _end)); #endif - // Align _mem as required - std::size_t space = sizeof(T) + alignof(T); + // Align _mem as required + std::size_t space = sizeof(T) + alignof(T); #ifdef CADET_DEBUG - void* const ptr2 = std::align(alignof(T), sizeof(T), _mem, space); - cadet_assert(ptr2 != nullptr); + void* const ptr2 = std::align(alignof(T), sizeof(T), _mem, space); + cadet_assert(ptr2 != nullptr); #else - std::align(alignof(T), sizeof(T), _mem, space); + std::align(alignof(T), sizeof(T), _mem, space); #endif - // Construct element in buffer and advance pointer - T* const ptr = new(_mem) T; - _mem = static_cast(_mem) + sizeof(T); + // Construct element in buffer and advance pointer + T* const ptr = new (_mem) T; + _mem = static_cast(_mem) + sizeof(T); #ifdef CADET_DEBUG - cadet_assert(!_end || std::less_equal()(_mem, _end)); + cadet_assert(!_end || std::less_equal()(_mem, _end)); #endif - return ptr; - } + return ptr; + } - /** - * @brief Allocates a scalar in the buffer - * @details The lifetime of the scalar is managed by wrapping it in a BufferedScalar. - * When the BufferedScalar handle is destroyed, the scalar is released automatically. - * @tparam T Type of the scalar - * @return Handle to the scalar - */ - template - BufferedScalar scalar() - { - return BufferedScalar(unmanagedScalar()); - } + /** + * @brief Allocates a scalar in the buffer + * @details The lifetime of the scalar is managed by wrapping it in a BufferedScalar. + * When the BufferedScalar handle is destroyed, the scalar is released automatically. + * @tparam T Type of the scalar + * @return Handle to the scalar + */ + template BufferedScalar scalar() + { + return BufferedScalar(unmanagedScalar()); + } - /** - * @brief Allocates a block of raw memory - * @param [in] blockSize Size of the block in bytes - * @return Pointer to block of raw memory - */ - void* raw(std::size_t blockSize) - { + /** + * @brief Allocates a block of raw memory + * @param [in] blockSize Size of the block in bytes + * @return Pointer to block of raw memory + */ + void* raw(std::size_t blockSize) + { #ifdef CADET_DEBUG - cadet_assert(!_end || std::less()(_mem, _end)); + cadet_assert(!_end || std::less()(_mem, _end)); #endif - void* const ptr = _mem; - _mem = static_cast(_mem) + blockSize; + void* const ptr = _mem; + _mem = static_cast(_mem) + blockSize; #ifdef CADET_DEBUG - cadet_assert(!_end || std::less_equal()(_mem, _end)); + cadet_assert(!_end || std::less_equal()(_mem, _end)); #endif - return ptr; - } + return ptr; + } - /** - * @brief Returns a pointer to the remaining memory - * @details Memory is not reserved and can be used in a subsequent call to array(), scalar(), or raw(). - * @return Pointer to memory - */ - void* remainingMemoryAsRaw() - { - return _mem; - } + /** + * @brief Returns a pointer to the remaining memory + * @details Memory is not reserved and can be used in a subsequent call to array(), scalar(), or raw(). + * @return Pointer to memory + */ + void* remainingMemoryAsRaw() + { + return _mem; + } - /** - * @brief Returns a LinearBufferAllocator that manages the remaining memory - * @details This function is used to set a checkpoint on the current LinearBufferAllocator. - * While the remaining memory is used by the returned allocator, the position of - * the current one is left unchanged. A typical use-case consists in partitioning - * a memory block and then reusing the remaining space multiple times in a loop - * without changing the previous items. - * @return LinearBufferAllocator operating on the remaining memory - */ + /** + * @brief Returns a LinearBufferAllocator that manages the remaining memory + * @details This function is used to set a checkpoint on the current LinearBufferAllocator. + * While the remaining memory is used by the returned allocator, the position of + * the current one is left unchanged. A typical use-case consists in partitioning + * a memory block and then reusing the remaining space multiple times in a loop + * without changing the previous items. + * @return LinearBufferAllocator operating on the remaining memory + */ #ifndef CADET_DEBUG - LinearBufferAllocator manageRemainingMemory() const CADET_NOEXCEPT { return LinearBufferAllocator(_mem); } + LinearBufferAllocator manageRemainingMemory() const CADET_NOEXCEPT + { + return LinearBufferAllocator(_mem); + } #else - LinearBufferAllocator manageRemainingMemory() const CADET_NOEXCEPT { return LinearBufferAllocator(_mem, _end); } + LinearBufferAllocator manageRemainingMemory() const CADET_NOEXCEPT + { + return LinearBufferAllocator(_mem, _end); + } #endif - private: - void* _mem; // - BufferedArray array(std::size_t numElements) + if (numBytes != _capacity) { - cadet_assert(sizeof(T) * numElements <= _free); - cadet_assert(sizeof(T) % alignof(T) == 0); + ::operator delete(_mem); + _mem = ::operator new(numBytes); + _capacity = numBytes; + } - // Align _curPos as required - void* const ptr2 = std::align(alignof(T), sizeof(T) * numElements, _curPos, _free); - cadet_assert(ptr2 != nullptr); + reset(); + } - // Decrease available free space - _free -= sizeof(T) * numElements; + /** + * @brief Resets the buffer + * @details All allocated objects have to be destroyed before calling reset(). + */ + void reset() CADET_NOEXCEPT + { + _curPos = _mem; + _free = _capacity; + } - // Construct elements in buffer and advance pointer - T* const ptr = rawMemoryAsArray(_curPos, numElements); - _curPos = static_cast(_curPos) + sizeof(T) * numElements; - return BufferedArray(ptr, numElements); - } + /** + * @brief Allocates an array in the buffer + * @param [in] numElements Number of array elements + * @tparam T Type of the array + * @return Handle to the array + */ + template BufferedArray array(std::size_t numElements) + { + cadet_assert(sizeof(T) * numElements <= _free); + cadet_assert(sizeof(T) % alignof(T) == 0); - /** - * @brief Allocates a scalar in the buffer - * @tparam T Type of the scalar - * @return Handle to the scalar - */ - template - BufferedScalar scalar() - { - cadet_assert(sizeof(T) <= _free); + // Align _curPos as required + void* const ptr2 = std::align(alignof(T), sizeof(T) * numElements, _curPos, _free); + cadet_assert(ptr2 != nullptr); - // Align _curPos as required - void* const ptr2 = std::align(alignof(T), sizeof(T), _curPos, _free); - cadet_assert(ptr2 != nullptr); + // Decrease available free space + _free -= sizeof(T) * numElements; - // Decrease available free space - _free -= sizeof(T); + // Construct elements in buffer and advance pointer + T* const ptr = rawMemoryAsArray(_curPos, numElements); + _curPos = static_cast(_curPos) + sizeof(T) * numElements; + return BufferedArray(ptr, numElements); + } - // Construct element in buffer and advance pointer - T* const ptr = new(_curPos) T; - _curPos = static_cast(_curPos) + sizeof(T); - return BufferedScalar(ptr); - } + /** + * @brief Allocates a scalar in the buffer + * @tparam T Type of the scalar + * @return Handle to the scalar + */ + template BufferedScalar scalar() + { + cadet_assert(sizeof(T) <= _free); - /** - * @brief Allocates a block of raw memory - * @param [in] blockSize Size of the block in bytes - * @return Pointer to block of raw memory - */ - void* raw(std::size_t blockSize) - { - cadet_assert(_free <= blockSize); - void* const ptr = _curPos; + // Align _curPos as required + void* const ptr2 = std::align(alignof(T), sizeof(T), _curPos, _free); + cadet_assert(ptr2 != nullptr); - _free -= blockSize; - _curPos = static_cast(_curPos) + blockSize; - return ptr; - } + // Decrease available free space + _free -= sizeof(T); - /** - * @brief Returns a pointer to the remaining memory - * @details Memory is not reserved and can be used in a subsequent call to array(), scalar(), or raw(). - * @return Pointer to memory - */ - void* remainingMemoryAsRaw() - { - return _curPos; - } + // Construct element in buffer and advance pointer + T* const ptr = new (_curPos) T; + _curPos = static_cast(_curPos) + sizeof(T); + return BufferedScalar(ptr); + } + + /** + * @brief Allocates a block of raw memory + * @param [in] blockSize Size of the block in bytes + * @return Pointer to block of raw memory + */ + void* raw(std::size_t blockSize) + { + cadet_assert(_free <= blockSize); + void* const ptr = _curPos; + + _free -= blockSize; + _curPos = static_cast(_curPos) + blockSize; + return ptr; + } - /** - * @brief Returns a LinearBufferAllocator that manages the remaining memory - * @details This function is used to set a checkpoint on the current LinearBufferAllocator. - * While the remaining memory is used by the returned allocator, the position of - * the current one is left unchanged. A typical use-case consists in partitioning - * a memory block and then reusing the remaining space multiple times in a loop - * without changing the previous items. - * - * Note that the remaining memory is not used up. This can cause problems if the - * memory is overwritten or allocated multiple times. - * @return LinearBufferAllocator operating on the remaining memory - */ + /** + * @brief Returns a pointer to the remaining memory + * @details Memory is not reserved and can be used in a subsequent call to array(), scalar(), or raw(). + * @return Pointer to memory + */ + void* remainingMemoryAsRaw() + { + return _curPos; + } + + /** + * @brief Returns a LinearBufferAllocator that manages the remaining memory + * @details This function is used to set a checkpoint on the current LinearBufferAllocator. + * While the remaining memory is used by the returned allocator, the position of + * the current one is left unchanged. A typical use-case consists in partitioning + * a memory block and then reusing the remaining space multiple times in a loop + * without changing the previous items. + * + * Note that the remaining memory is not used up. This can cause problems if the + * memory is overwritten or allocated multiple times. + * @return LinearBufferAllocator operating on the remaining memory + */ #ifndef CADET_DEBUG - LinearBufferAllocator manageRemainingMemory() const CADET_NOEXCEPT { return LinearBufferAllocator(_curPos); } + LinearBufferAllocator manageRemainingMemory() const CADET_NOEXCEPT + { + return LinearBufferAllocator(_curPos); + } #else - LinearBufferAllocator manageRemainingMemory() const CADET_NOEXCEPT { return LinearBufferAllocator(_curPos, static_cast(_mem) + _capacity); } + LinearBufferAllocator manageRemainingMemory() const CADET_NOEXCEPT + { + return LinearBufferAllocator(_curPos, static_cast(_mem) + _capacity); + } #endif - private: - void* _mem; //>& models); - void registerOutletModel(std::unordered_map>& models); - - void registerGeneralRateModel(std::unordered_map>& models); - void registerLumpedRateModelWithPores(std::unordered_map>& models); - void registerLumpedRateModelWithoutPores(std::unordered_map>& models); - void registerCSTRModel(std::unordered_map>& models); +namespace model +{ +void registerInletModel( + std::unordered_map>& models); +void registerOutletModel( + std::unordered_map>& models); + +void registerGeneralRateModel( + std::unordered_map>& models); +void registerLumpedRateModelWithPores( + std::unordered_map>& models); +void registerLumpedRateModelWithoutPores( + std::unordered_map>& models); +void registerCSTRModel( + std::unordered_map>& models); #ifdef ENABLE_2D_MODELS - void registerGeneralRateModel2D(std::unordered_map>& models); - void registerMultiChannelTransportModel(std::unordered_map>& models); +void registerGeneralRateModel2D( + std::unordered_map>& models); +void registerMultiChannelTransportModel( + std::unordered_map>& models); #endif - namespace inlet - { - void registerPiecewiseCubicPoly(std::unordered_map>& inlets); - } // namespace inlet +namespace inlet +{ +void registerPiecewiseCubicPoly(std::unordered_map>& inlets); +} // namespace inlet - namespace extfun - { - void registerLinearInterpolation(std::unordered_map>& extFuns); - void registerPiecewiseCubicPoly(std::unordered_map>& extFuns); - } // namespace extfun - } // namespace model +namespace extfun +{ +void registerLinearInterpolation(std::unordered_map>& extFuns); +void registerPiecewiseCubicPoly(std::unordered_map>& extFuns); +} // namespace extfun +} // namespace model - ModelBuilder::ModelBuilder() - { - // Register all available models - model::registerInletModel(_modelCreators); - model::registerOutletModel(_modelCreators); - model::registerGeneralRateModel(_modelCreators); - model::registerLumpedRateModelWithPores(_modelCreators); - model::registerLumpedRateModelWithoutPores(_modelCreators); - model::registerCSTRModel(_modelCreators); +ModelBuilder::ModelBuilder() +{ + // Register all available models + model::registerInletModel(_modelCreators); + model::registerOutletModel(_modelCreators); + model::registerGeneralRateModel(_modelCreators); + model::registerLumpedRateModelWithPores(_modelCreators); + model::registerLumpedRateModelWithoutPores(_modelCreators); + model::registerCSTRModel(_modelCreators); #ifdef ENABLE_2D_MODELS - model::registerGeneralRateModel2D(_modelCreators); - model::registerMultiChannelTransportModel(_modelCreators); + model::registerGeneralRateModel2D(_modelCreators); + model::registerMultiChannelTransportModel(_modelCreators); #endif - // Register all available inlet profiles - model::inlet::registerPiecewiseCubicPoly(_inletCreators); + // Register all available inlet profiles + model::inlet::registerPiecewiseCubicPoly(_inletCreators); - // Register all available external functions - model::extfun::registerLinearInterpolation(_extFunCreators); - model::extfun::registerPiecewiseCubicPoly(_extFunCreators); - } + // Register all available external functions + model::extfun::registerLinearInterpolation(_extFunCreators); + model::extfun::registerPiecewiseCubicPoly(_extFunCreators); +} - ModelBuilder::~ModelBuilder() CADET_NOEXCEPT - { - for (IModelSystem* model : _models) - delete model; - } +ModelBuilder::~ModelBuilder() CADET_NOEXCEPT +{ + for (IModelSystem* model : _models) + delete model; +} - template - void ModelBuilder::registerModel(const std::string& name) - { - _modelCreators[name] = [](UnitOpIdx uoId, IParameterProvider&) { return new UnitOpModel_t(uoId); }; - } +template void ModelBuilder::registerModel(const std::string& name) +{ + _modelCreators[name] = [](UnitOpIdx uoId, IParameterProvider&) { return new UnitOpModel_t(uoId); }; +} - template - void ModelBuilder::registerModel() - { - registerModel(UnitOpModel_t::identifier()); - } +template void ModelBuilder::registerModel() +{ + registerModel(UnitOpModel_t::identifier()); +} - IModelSystem* ModelBuilder::createSystem(IParameterProvider& paramProvider) +IModelSystem* ModelBuilder::createSystem(IParameterProvider& paramProvider) +{ + model::ModelSystem* sys = new model::ModelSystem(); + + // Create and configure all unit operations + bool success = true; + unsigned int i = 0; + std::ostringstream oss; + oss << "unit_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << i; + while (paramProvider.exists(oss.str())) { - model::ModelSystem* sys = new model::ModelSystem(); - - // Create and configure all unit operations - bool success = true; - unsigned int i = 0; - std::ostringstream oss; - oss << "unit_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << i; - while (paramProvider.exists(oss.str())) - { - // Create and configure unit operation - paramProvider.pushScope(oss.str()); - - IModel* const unitOp = createUnitOperation(paramProvider, i); - - paramProvider.popScope(); - - if (unitOp) - { - // Model correctly created and configured -> add to system - sys->addModel(unitOp); - } - else - { - // Something went wrong -> abort and exit - success = false; - break; - } - - ++i; - oss.str(""); - oss << "unit_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << i; - } + // Create and configure unit operation + paramProvider.pushScope(oss.str()); + + IModel* const unitOp = createUnitOperation(paramProvider, i); - // Configure the whole system - success = success && sys->configureModelDiscretization(paramProvider, *this) && sys->configure(paramProvider); + paramProvider.popScope(); - if (success) + if (unitOp) { - _models.push_back(sys); - return sys; + // Model correctly created and configured -> add to system + sys->addModel(unitOp); } else { - delete sys; - return nullptr; + // Something went wrong -> abort and exit + success = false; + break; } + + ++i; + oss.str(""); + oss << "unit_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << i; } - IModelSystem* ModelBuilder::createSystem() + // Configure the whole system + success = success && sys->configureModelDiscretization(paramProvider, *this) && sys->configure(paramProvider); + + if (success) { - IModelSystem* sys = new model::ModelSystem(); _models.push_back(sys); return sys; } - - void ModelBuilder::detachSystem(IModelSystem const* sys) - { - for (std::vector::iterator it = _models.begin(); it != _models.end(); ++it) - { - if (*it == sys) - { - _models.erase(it); - break; - } - } - } - - void ModelBuilder::destroySystem(IModelSystem* sys) + else { delete sys; + return nullptr; } +} - IModel* ModelBuilder::createUnitOperation(IParameterProvider& paramProvider, UnitOpIdx uoId) - { - const std::string uoType = paramProvider.getString("UNIT_TYPE"); - const auto it = _modelCreators.find(uoType); - if (it == _modelCreators.end()) - { - // Model was not found - LOG(Error) << "Unknown unit type " << uoType << " for unit " << uoId; - return nullptr; - } - - // Call factory function (thanks to type erasure of std::function we can store - // all factory functions in one container) - IUnitOperation* const model = it->second(uoId, paramProvider); - if (!model) { - LOG(Error) << "Failed to create unit type " << uoType << " for unit " << uoId; - return nullptr; - } +IModelSystem* ModelBuilder::createSystem() +{ + IModelSystem* sys = new model::ModelSystem(); + _models.push_back(sys); + return sys; +} - if (!model->configureModelDiscretization(paramProvider, *this) || !model->configure(paramProvider)) +void ModelBuilder::detachSystem(IModelSystem const* sys) +{ + for (std::vector::iterator it = _models.begin(); it != _models.end(); ++it) + { + if (*it == sys) { - LOG(Error) << "Configuration of unit " << uoId << "(" << uoType << ") failed"; - delete model; - return nullptr; + _models.erase(it); + break; } - - return model; } +} - //IModel* ModelBuilder::createUnitOperation(const std::string& uoType, UnitOpIdx uoId) - //{ - // const auto it = _modelCreators.find(uoType); - // if (it == _modelCreators.end()) - // { - // // Model was not found - // LOG(Error) << "Unknown unit type " << uoType << " for unit " << uoId; - // return nullptr; - // } - - // IUnitOperation* const model = it->second(uoId); - // return model; - //} - - void ModelBuilder::destroyUnitOperation(IModel* unitOp) +void ModelBuilder::destroySystem(IModelSystem* sys) +{ + delete sys; +} + +IModel* ModelBuilder::createUnitOperation(IParameterProvider& paramProvider, UnitOpIdx uoId) +{ + const std::string uoType = paramProvider.getString("UNIT_TYPE"); + const auto it = _modelCreators.find(uoType); + if (it == _modelCreators.end()) { - delete unitOp; + // Model was not found + LOG(Error) << "Unknown unit type " << uoType << " for unit " << uoId; + return nullptr; } - void ModelBuilder::registerInletType(const std::string& name, std::function factory) + // Call factory function (thanks to type erasure of std::function we can store + // all factory functions in one container) + IUnitOperation* const model = it->second(uoId, paramProvider); + if (!model) { - if (_inletCreators.find(name) == _inletCreators.end()) - _inletCreators[name] = factory; - else - throw std::invalid_argument("INLET_TYPE " + name + " is already registered and cannot be overwritten"); + LOG(Error) << "Failed to create unit type " << uoType << " for unit " << uoId; + return nullptr; } - void ModelBuilder::registerExternalFunctionType(const std::string& name, std::function factory) + if (!model->configureModelDiscretization(paramProvider, *this) || !model->configure(paramProvider)) { - if (_extFunCreators.find(name) == _extFunCreators.end()) - _extFunCreators[name] = factory; - else - throw std::invalid_argument("EXTFUN_TYPE " + name + " is already registered and cannot be overwritten"); + LOG(Error) << "Configuration of unit " << uoId << "(" << uoType << ") failed"; + delete model; + return nullptr; } - IInletProfile* ModelBuilder::createInletProfile(const std::string& type) const - { - const InletFactoryContainer_t::const_iterator it = _inletCreators.find(type); - if (it != _inletCreators.end()) - return (it->second)(); + return model; +} - return nullptr; - } +// IModel* ModelBuilder::createUnitOperation(const std::string& uoType, UnitOpIdx uoId) +//{ +// const auto it = _modelCreators.find(uoType); +// if (it == _modelCreators.end()) +// { +// // Model was not found +// LOG(Error) << "Unknown unit type " << uoType << " for unit " << uoId; +// return nullptr; +// } - IExternalFunction* ModelBuilder::createExternalFunction(const std::string& type) const - { - const ExternalFunctionFactoryContainer_t::const_iterator it = _extFunCreators.find(type); - if (it != _extFunCreators.end()) - return (it->second)(); +// IUnitOperation* const model = it->second(uoId); +// return model; +//} - return nullptr; - } +void ModelBuilder::destroyUnitOperation(IModel* unitOp) +{ + delete unitOp; +} - model::IBindingModel* ModelBuilder::createBindingModel(const std::string& name) const - { - return _bindingModels.create(name); - } +void ModelBuilder::registerInletType(const std::string& name, std::function factory) +{ + if (_inletCreators.find(name) == _inletCreators.end()) + _inletCreators[name] = factory; + else + throw std::invalid_argument("INLET_TYPE " + name + " is already registered and cannot be overwritten"); +} + +void ModelBuilder::registerExternalFunctionType(const std::string& name, + std::function factory) +{ + if (_extFunCreators.find(name) == _extFunCreators.end()) + _extFunCreators[name] = factory; + else + throw std::invalid_argument("EXTFUN_TYPE " + name + " is already registered and cannot be overwritten"); +} - bool ModelBuilder::isValidBindingModel(const std::string& name) const - { - return _bindingModels.exists(name); - } +IInletProfile* ModelBuilder::createInletProfile(const std::string& type) const +{ + const InletFactoryContainer_t::const_iterator it = _inletCreators.find(type); + if (it != _inletCreators.end()) + return (it->second)(); - model::IDynamicReactionModel* ModelBuilder::createDynamicReactionModel(const std::string& name) const - { - return _reactionModels.createDynamic(name); - } + return nullptr; +} - bool ModelBuilder::isValidDynamicReactionModel(const std::string& name) const - { - return _reactionModels.existsDynamic(name); - } +IExternalFunction* ModelBuilder::createExternalFunction(const std::string& type) const +{ + const ExternalFunctionFactoryContainer_t::const_iterator it = _extFunCreators.find(type); + if (it != _extFunCreators.end()) + return (it->second)(); - model::IParameterStateDependence* ModelBuilder::createParameterStateDependence(const std::string& name) const - { - return _paramDeps.createStateDependence(name); - } + return nullptr; +} - bool ModelBuilder::isValidParameterStateDependence(const std::string& name) const - { - return _paramDeps.stateDependenceExists(name); - } +model::IBindingModel* ModelBuilder::createBindingModel(const std::string& name) const +{ + return _bindingModels.create(name); +} - model::IParameterParameterDependence* ModelBuilder::createParameterParameterDependence(const std::string& name) const - { - return _paramDeps.createParameterDependence(name); - } +bool ModelBuilder::isValidBindingModel(const std::string& name) const +{ + return _bindingModels.exists(name); +} - bool ModelBuilder::isValidParameterParameterDependence(const std::string& name) const - { - return _paramDeps.parameterDependenceExists(name); - } +model::IDynamicReactionModel* ModelBuilder::createDynamicReactionModel(const std::string& name) const +{ + return _reactionModels.createDynamic(name); +} + +bool ModelBuilder::isValidDynamicReactionModel(const std::string& name) const +{ + return _reactionModels.existsDynamic(name); +} + +model::IParameterStateDependence* ModelBuilder::createParameterStateDependence(const std::string& name) const +{ + return _paramDeps.createStateDependence(name); +} + +bool ModelBuilder::isValidParameterStateDependence(const std::string& name) const +{ + return _paramDeps.stateDependenceExists(name); +} + +model::IParameterParameterDependence* ModelBuilder::createParameterParameterDependence(const std::string& name) const +{ + return _paramDeps.createParameterDependence(name); +} + +bool ModelBuilder::isValidParameterParameterDependence(const std::string& name) const +{ + return _paramDeps.parameterDependenceExists(name); +} } // namespace cadet diff --git a/src/libcadet/ModelBuilderImpl.hpp b/src/libcadet/ModelBuilderImpl.hpp index bb4bd96fe..aaae86595 100644 --- a/src/libcadet/ModelBuilderImpl.hpp +++ b/src/libcadet/ModelBuilderImpl.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * ModelBuilder implementation */ @@ -41,7 +41,6 @@ class IInletProfile; class ModelBuilder : public IModelBuilder, public IConfigHelper { public: - ModelBuilder(); virtual ~ModelBuilder() CADET_NOEXCEPT; @@ -52,7 +51,7 @@ class ModelBuilder : public IModelBuilder, public IConfigHelper virtual void destroySystem(IModelSystem* sys); virtual IModel* createUnitOperation(IParameterProvider& paramProvider, UnitOpIdx uoId); - //virtual IModel* createUnitOperation(const std::string& uoType, UnitOpIdx uoId); + // virtual IModel* createUnitOperation(const std::string& uoType, UnitOpIdx uoId); virtual void destroyUnitOperation(IModel* unitOp); virtual void registerInletType(const std::string& name, std::function factory); @@ -70,33 +69,31 @@ class ModelBuilder : public IModelBuilder, public IConfigHelper virtual IExternalFunction* createExternalFunction(const std::string& type) const; protected: - /** * @brief Registers an IUnitOperation * @param [in] name Name of the model * @tparam UnitOpModel_t Type of the model */ - template - void registerModel(const std::string& name); + template void registerModel(const std::string& name); /** * @brief Registers an IUnitOperation * @details The name of the model is inferred from the static function IUnitOperation::identifier(). * @tparam UnitOpModel_t Type of the model */ - template - void registerModel(); + template void registerModel(); - BindingModelFactory _bindingModels; //!< Factory for IBindingModel implementations - ReactionModelFactory _reactionModels; //!< Factory for IDynamicReactionModel implementations + BindingModelFactory _bindingModels; //!< Factory for IBindingModel implementations + ReactionModelFactory _reactionModels; //!< Factory for IDynamicReactionModel implementations ParameterDependenceFactory _paramDeps; //!< Factory for IParameterStateDependence implementations - typedef std::unordered_map> ModelFactoryContainer_t; + typedef std::unordered_map> + ModelFactoryContainer_t; typedef std::unordered_map> InletFactoryContainer_t; typedef std::unordered_map> ExternalFunctionFactoryContainer_t; - ModelFactoryContainer_t _modelCreators; //!< Map with factory functions for models - InletFactoryContainer_t _inletCreators; //!< Map with factory functions for inlet profiles + ModelFactoryContainer_t _modelCreators; //!< Map with factory functions for models + InletFactoryContainer_t _inletCreators; //!< Map with factory functions for inlet profiles ExternalFunctionFactoryContainer_t _extFunCreators; //!< Map with factory functions for external functions std::vector _models; //!< Models @@ -104,4 +101,4 @@ class ModelBuilder : public IModelBuilder, public IConfigHelper } // namespace cadet -#endif // LIBCADET_MODELBUILDER_IMPL_HPP_ +#endif // LIBCADET_MODELBUILDER_IMPL_HPP_ diff --git a/src/libcadet/ParallelSupport.hpp b/src/libcadet/ParallelSupport.hpp index 1ed096050..ffa7dfb80 100644 --- a/src/libcadet/ParallelSupport.hpp +++ b/src/libcadet/ParallelSupport.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Helper functions and macros for parallelization. */ @@ -23,224 +23,234 @@ #include "Memory.hpp" #ifdef CADET_PARALLELIZE - #define CADET_PARFOR_END ) - #define CADET_PARNODE_END ) - #define CADET_PAR_CONTINUE return +#define CADET_PARFOR_END ) +#define CADET_PARNODE_END ) +#define CADET_PAR_CONTINUE return - #include "tbb/task_arena.h" - #include +#include "tbb/task_arena.h" +#include - namespace cadet - { - namespace util +namespace cadet +{ +namespace util +{ + +/** + * @brief Provides a distinct portion of memory for each thread (thread local storage) + * @details For each thread, a memory buffer of certain size is allocated. + * The buffers are aligned to cache lines in order to avoid false sharing. + * Note that it is not guaranteed that the exact same thread always obtains + * the same buffers as the thread index of a thread may change over time. + */ +class ThreadLocalStorage +{ +public: + ThreadLocalStorage() : _data(0) { + } + ~ThreadLocalStorage() = default; + + ThreadLocalStorage(const ThreadLocalStorage&) = delete; + ThreadLocalStorage(ThreadLocalStorage&& mv) CADET_NOEXCEPT = default; + + ThreadLocalStorage& operator=(const ThreadLocalStorage&) = delete; - /** - * @brief Provides a distinct portion of memory for each thread (thread local storage) - * @details For each thread, a memory buffer of certain size is allocated. - * The buffers are aligned to cache lines in order to avoid false sharing. - * Note that it is not guaranteed that the exact same thread always obtains - * the same buffers as the thread index of a thread may change over time. - */ - class ThreadLocalStorage +#ifdef COMPILER_SUPPORT_NOEXCEPT_DEFAULTED_MOVE + ThreadLocalStorage& operator=(ThreadLocalStorage&&) CADET_NOEXCEPT = default; +#else + ThreadLocalStorage& operator=(ThreadLocalStorage&&) = default; +#endif + + /** + * @brief Allocates storage for each thread with the given number of bytes + * @details The number of threads can expand or shrink and the buffers are + * created or released accordingly. + * @param [in] numThreads Number of threads + * @param [in] storageSize Number of bytes to store + */ + inline void resize(unsigned int numThreads, unsigned int storageSize) + { + if (numThreads < _data.size()) + { + // Remove superfluous buffers at the end + for (std::size_t i = 0; i < numThreads - _data.size(); ++i) + _data.pop_back(); + } + else if (numThreads > _data.size()) { - public: - ThreadLocalStorage() : _data(0) { } - ~ThreadLocalStorage() = default; - - ThreadLocalStorage(const ThreadLocalStorage&) = delete; - ThreadLocalStorage(ThreadLocalStorage&& mv) CADET_NOEXCEPT = default; - - ThreadLocalStorage& operator=(const ThreadLocalStorage&) = delete; - - #ifdef COMPILER_SUPPORT_NOEXCEPT_DEFAULTED_MOVE - ThreadLocalStorage& operator=(ThreadLocalStorage&&) CADET_NOEXCEPT = default; - #else - ThreadLocalStorage& operator=(ThreadLocalStorage&&) = default; - #endif - - /** - * @brief Allocates storage for each thread with the given number of bytes - * @details The number of threads can expand or shrink and the buffers are - * created or released accordingly. - * @param [in] numThreads Number of threads - * @param [in] storageSize Number of bytes to store - */ - inline void resize(unsigned int numThreads, unsigned int storageSize) - { - if (numThreads < _data.size()) - { - // Remove superfluous buffers at the end - for (std::size_t i = 0; i < numThreads - _data.size(); ++i) - _data.pop_back(); - } - else if (numThreads > _data.size()) - { - // Add some more buffers - const unsigned int oldSize = _data.size(); - _data.resize(numThreads); - for (std::size_t i = oldSize; i < _data.size(); ++i) - _data[i].resize(storageSize); - } - } - - /** - * @brief Allocates storage for each thread with the given number of bytes - * @details The number of threads can expand or shrink and the buffers are - * created or released accordingly. - * - * The number of threads is inferred from TBB. - * @param [in] storageSize Number of bytes to store - */ - inline void resize(unsigned int storageSize) - { - resize(tbb::this_task_arena::max_concurrency(), storageSize); - } - - /** - * @brief Resets the memory blocks for another use - */ - inline void reset() CADET_NOEXCEPT - { - for (LinearHeapAllocator& lha : _data) - lha.reset(); - } - - /** - * @brief Access memory buffer of the current thread - * @return Memory buffer of the current thread - */ - inline LinearBufferAllocator get() const - { - #ifdef CADET_DEBUG - const int threadIdx = tbb::this_task_arena::current_thread_index(); - cadet_assert(threadIdx != tbb::task_arena::not_initialized); - cadet_assert(threadIdx >= 0); - cadet_assert(threadIdx < _data.size()); - #endif - return _data[tbb::this_task_arena::current_thread_index()].manageRemainingMemory(); - } - - /** - * @brief Access memory buffer of the given thread index - * @param [in] idx Thread index - * @return Memory buffer of the given thread index - */ - inline LinearBufferAllocator get(unsigned int idx) const - { - #ifdef CADET_DEBUG - cadet_assert(idx < _data.size()); - #endif - return _data[idx].manageRemainingMemory(); - } - - private: - std::vector _data; - }; - - /** - * @brief Returns the maximum number of threads at this point in the code - * @details The current maximum number of threads may change from point in code - * to point in code. It is governed by several TBB settings, most importantly - * the first instantiation of tbb::task_scheduler_init. - * @return Maximum number of threads - */ - inline unsigned int getMaxThreads() { return tbb::this_task_arena::max_concurrency(); } - - } // namespace util - } // namespace cadet + // Add some more buffers + const unsigned int oldSize = _data.size(); + _data.resize(numThreads); + for (std::size_t i = oldSize; i < _data.size(); ++i) + _data[i].resize(storageSize); + } + } + + /** + * @brief Allocates storage for each thread with the given number of bytes + * @details The number of threads can expand or shrink and the buffers are + * created or released accordingly. + * + * The number of threads is inferred from TBB. + * @param [in] storageSize Number of bytes to store + */ + inline void resize(unsigned int storageSize) + { + resize(tbb::this_task_arena::max_concurrency(), storageSize); + } + + /** + * @brief Resets the memory blocks for another use + */ + inline void reset() CADET_NOEXCEPT + { + for (LinearHeapAllocator& lha : _data) + lha.reset(); + } + + /** + * @brief Access memory buffer of the current thread + * @return Memory buffer of the current thread + */ + inline LinearBufferAllocator get() const + { +#ifdef CADET_DEBUG + const int threadIdx = tbb::this_task_arena::current_thread_index(); + cadet_assert(threadIdx != tbb::task_arena::not_initialized); + cadet_assert(threadIdx >= 0); + cadet_assert(threadIdx < _data.size()); +#endif + return _data[tbb::this_task_arena::current_thread_index()].manageRemainingMemory(); + } + + /** + * @brief Access memory buffer of the given thread index + * @param [in] idx Thread index + * @return Memory buffer of the given thread index + */ + inline LinearBufferAllocator get(unsigned int idx) const + { +#ifdef CADET_DEBUG + cadet_assert(idx < _data.size()); +#endif + return _data[idx].manageRemainingMemory(); + } + +private: + std::vector _data; +}; +/** + * @brief Returns the maximum number of threads at this point in the code + * @details The current maximum number of threads may change from point in code + * to point in code. It is governed by several TBB settings, most importantly + * the first instantiation of tbb::task_scheduler_init. + * @return Maximum number of threads + */ +inline unsigned int getMaxThreads() +{ + return tbb::this_task_arena::max_concurrency(); +} + +} // namespace util +} // namespace cadet + +#else +#define CADET_PARFOR_END +#define CADET_PARNODE_END +#define CADET_PAR_CONTINUE continue + +namespace cadet +{ +namespace util +{ + +/** + * @brief Provides a distinct portion of memory for each thread (thread local storage) + * @details As there is only one thread, this class simply wraps a single memory buffer. + */ +class ThreadLocalStorage +{ +public: + ThreadLocalStorage() + { + } + ThreadLocalStorage(const ThreadLocalStorage&) = delete; + ThreadLocalStorage(ThreadLocalStorage&& mv) CADET_NOEXCEPT = default; + ~ThreadLocalStorage() = default; + + ThreadLocalStorage& operator=(const ThreadLocalStorage&) = delete; + +#ifdef COMPILER_SUPPORT_NOEXCEPT_DEFAULTED_MOVE + ThreadLocalStorage& operator=(ThreadLocalStorage&&) CADET_NOEXCEPT = default; #else - #define CADET_PARFOR_END - #define CADET_PARNODE_END - #define CADET_PAR_CONTINUE continue + ThreadLocalStorage& operator=(ThreadLocalStorage&&) = default; +#endif - namespace cadet + /** + * @brief Allocates storage for each thread with the given number of bytes + * @param [in] numThreads Number of threads + * @param [in] storageSize Number of bytes to store + */ + inline void resize(unsigned int numThreads, unsigned int storageSize) { - namespace util + _memory.resize(storageSize); + } + + /** + * @brief Allocates arrays for each thread with the given number of elements + * @param [in] numThreads Number of threads + * @param [in] storageSize Number of bytes to store + */ + inline void resize(unsigned int storageSize) { + resize(1u, storageSize); + } - /** - * @brief Provides a distinct portion of memory for each thread (thread local storage) - * @details As there is only one thread, this class simply wraps a single memory buffer. - */ - class ThreadLocalStorage - { - public: - ThreadLocalStorage() { } - ThreadLocalStorage(const ThreadLocalStorage&) = delete; - ThreadLocalStorage(ThreadLocalStorage&& mv) CADET_NOEXCEPT = default; - ~ThreadLocalStorage() = default; - - ThreadLocalStorage& operator=(const ThreadLocalStorage&) = delete; - - #ifdef COMPILER_SUPPORT_NOEXCEPT_DEFAULTED_MOVE - ThreadLocalStorage& operator=(ThreadLocalStorage&&) CADET_NOEXCEPT = default; - #else - ThreadLocalStorage& operator=(ThreadLocalStorage&&) = default; - #endif - - /** - * @brief Allocates storage for each thread with the given number of bytes - * @param [in] numThreads Number of threads - * @param [in] storageSize Number of bytes to store - */ - inline void resize(unsigned int numThreads, unsigned int storageSize) - { - _memory.resize(storageSize); - } - - /** - * @brief Allocates arrays for each thread with the given number of elements - * @param [in] numThreads Number of threads - * @param [in] storageSize Number of bytes to store - */ - inline void resize(unsigned int storageSize) - { - resize(1u, storageSize); - } - - /** - * @brief Resets the memory blocks for another use - */ - inline void reset() CADET_NOEXCEPT - { - _memory.reset(); - } - - /** - * @brief Access memory buffer of the current thread - * @return Memory buffer of the current thread - */ - inline LinearBufferAllocator get() const CADET_NOEXCEPT - { - return _memory.manageRemainingMemory(); - } - - /** - * @brief Access memory buffer of the given thread index - * @param [in] idx Thread index - * @return Memory buffer of the given thread index - */ - inline LinearBufferAllocator get(unsigned int idx) const CADET_NOEXCEPT - { - return _memory.manageRemainingMemory(); - } - - private: - LinearHeapAllocator _memory; // - struct hash +template <> struct hash +{ + inline std::size_t operator()(const cadet::ParameterId& pId) const CADET_NOEXCEPT { - inline std::size_t operator()(const cadet::ParameterId& pId) const CADET_NOEXCEPT - { - return cadet::hashParameter(pId); - } - }; + return cadet::hashParameter(pId); + } +}; } // namespace std namespace cadet { - inline bool operator==(const ParameterId& a, const ParameterId& b) CADET_NOEXCEPT - { - return (a.name == b.name) && (a.unitOperation == b.unitOperation) && (a.component == b.component) - && (a.particleType == b.particleType) && (a.boundState == b.boundState) && (a.reaction == b.reaction) - && (a.section == b.section); - } +inline bool operator==(const ParameterId& a, const ParameterId& b) CADET_NOEXCEPT +{ + return (a.name == b.name) && (a.unitOperation == b.unitOperation) && (a.component == b.component) && + (a.particleType == b.particleType) && (a.boundState == b.boundState) && (a.reaction == b.reaction) && + (a.section == b.section); +} - inline bool operator!=(const ParameterId& a, const ParameterId& b) CADET_NOEXCEPT { return !(a == b); } +inline bool operator!=(const ParameterId& a, const ParameterId& b) CADET_NOEXCEPT +{ + return !(a == b); +} - inline bool operator<(const ParameterId& a, const ParameterId& b) CADET_NOEXCEPT - { - return std::tie(a.name, a.unitOperation, a.component, a.particleType, a.boundState, a.reaction, a.section) < std::tie(b.name, b.unitOperation, b.component, b.particleType, b.boundState, b.reaction, b.section); - } +inline bool operator<(const ParameterId& a, const ParameterId& b) CADET_NOEXCEPT +{ + return std::tie(a.name, a.unitOperation, a.component, a.particleType, a.boundState, a.reaction, a.section) < + std::tie(b.name, b.unitOperation, b.component, b.particleType, b.boundState, b.reaction, b.section); +} - inline std::ostream& operator<<(std::ostream& out, const ParameterId& pId) - { - out << "{" << hashParameter(pId) << " = " << pId.name << ", Unit " << static_cast(pId.unitOperation) << " Comp " << static_cast(pId.component) - << " ParticleType " << static_cast(pId.particleType) << " BoundState " << static_cast(pId.boundState) << " Reaction " << static_cast(pId.reaction) - << " Section " << static_cast(pId.section) << "}"; - return out; - } +inline std::ostream& operator<<(std::ostream& out, const ParameterId& pId) +{ + out << "{" << hashParameter(pId) << " = " << pId.name << ", Unit " << static_cast(pId.unitOperation) + << " Comp " << static_cast(pId.component) << " ParticleType " + << static_cast(pId.particleType) << " BoundState " << static_cast(pId.boundState) + << " Reaction " << static_cast(pId.reaction) << " Section " + << static_cast(pId.section) << "}"; + return out; +} - inline std::string to_string(const ParameterId& pId) - { - std::stringstream out; - out << "{" << hashParameter(pId) << " = " << pId.name << ", Unit " << static_cast(pId.unitOperation) << " Comp " << static_cast(pId.component) - << " ParticleType " << static_cast(pId.particleType) << " BoundState " << static_cast(pId.boundState) << " Reaction " << static_cast(pId.reaction) - << " Section " << static_cast(pId.section) << "}"; - return out.str(); - } +inline std::string to_string(const ParameterId& pId) +{ + std::stringstream out; + out << "{" << hashParameter(pId) << " = " << pId.name << ", Unit " << static_cast(pId.unitOperation) + << " Comp " << static_cast(pId.component) << " ParticleType " + << static_cast(pId.particleType) << " BoundState " << static_cast(pId.boundState) + << " Reaction " << static_cast(pId.reaction) << " Section " + << static_cast(pId.section) << "}"; + return out.str(); +} } // namespace cadet -#endif // LIBCADET_PARAMIDUTIL_HPP_ +#endif // LIBCADET_PARAMIDUTIL_HPP_ diff --git a/src/libcadet/ParamReaderHelper.hpp b/src/libcadet/ParamReaderHelper.hpp index f44fd3384..fdfd2aa16 100644 --- a/src/libcadet/ParamReaderHelper.hpp +++ b/src/libcadet/ParamReaderHelper.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides helper functions for handling parameters */ @@ -31,603 +31,657 @@ namespace cadet { - /** - * @brief Reads a scalar, possibly vectorial, parameter into a vector - * @details This function automatically detects if the parameter is vectorial. - * If the user always wants a vectorial result, the single scalar value is - * replicated to reach the requested array size. - * - * @param [in] dest Destination vector in which the data is saved - * @param [in] paramProvider Parameter provider from which is read - * @param [in] dataSet Name of the dataset - * @param [in] nExpand How often a single scalar parameter is replicated (@c 1 if scalars should remain scalar) - * @return @c true if a scalar value was read, or @c false if an actual array (more than 1 element) was read - * @tparam ValType Type of the parameter, such as @c active or @c double - */ - template - inline bool readScalarParameterOrArray(std::vector& dest, IParameterProvider& paramProvider, const std::string& dataSet, unsigned int nExpand) +/** + * @brief Reads a scalar, possibly vectorial, parameter into a vector + * @details This function automatically detects if the parameter is vectorial. + * If the user always wants a vectorial result, the single scalar value is + * replicated to reach the requested array size. + * + * @param [in] dest Destination vector in which the data is saved + * @param [in] paramProvider Parameter provider from which is read + * @param [in] dataSet Name of the dataset + * @param [in] nExpand How often a single scalar parameter is replicated (@c 1 if scalars should remain scalar) + * @return @c true if a scalar value was read, or @c false if an actual array (more than 1 element) was read + * @tparam ValType Type of the parameter, such as @c active or @c double + */ +template +inline bool readScalarParameterOrArray(std::vector& dest, IParameterProvider& paramProvider, + const std::string& dataSet, unsigned int nExpand) +{ + dest.clear(); + if (paramProvider.isArray(dataSet)) { - dest.clear(); - if (paramProvider.isArray(dataSet)) - { - // Copy all values from provider to destination - const std::vector vals = paramProvider.getDoubleArray(dataSet); - dest.resize(vals.size()); - for (std::size_t i = 0; i < vals.size(); ++i) - dest[i] = vals[i]; - - return vals.size() == 1; - } - else - { - // Only scalar value in provider, expand to given size - const double val = paramProvider.getDouble(dataSet); - dest.resize(nExpand); - for (unsigned int i = 0; i < nExpand; ++i) - dest[i] = val; + // Copy all values from provider to destination + const std::vector vals = paramProvider.getDoubleArray(dataSet); + dest.resize(vals.size()); + for (std::size_t i = 0; i < vals.size(); ++i) + dest[i] = vals[i]; - return true; - } + return vals.size() == 1; } - - - /** - * @brief Reads a vector valued parameter, that may also be a matrix - * @details It is automatically detected if the parameter is matrixoid or vectorial. - * If the user always wants a matrixoid result, the vectorial parameter - * is replicated to reach the requested size. The ordering is - * vec0rep0, vec1rep0, vec2rep0, vec0rep1, vec1rep1, vec2rep1, etc. - * - * @param [in] dest Destination vector in which the data is saved - * @param [in] paramProvider Parameter provider from which is read - * @param [in] dataSet Name of the dataset - * @param [in] nExpect Expected number of elements in the vectorial parameter - * @param [in] nExpand How often the vectorial parameter is replicated (@c 1 if vectorials should remain vectorial) - * @tparam ValType Type of the parameter, such as @c active or @c double - */ - template - inline void readParameterMatrix(std::vector& dest, IParameterProvider& paramProvider, const std::string& dataSet, unsigned int nExpect, unsigned int nExpand) + else { - dest.clear(); - const std::vector vals = paramProvider.getDoubleArray(dataSet); + // Only scalar value in provider, expand to given size + const double val = paramProvider.getDouble(dataSet); + dest.resize(nExpand); + for (unsigned int i = 0; i < nExpand; ++i) + dest[i] = val; - if (vals.size() == nExpect) - { - // Read expected number of values, so copy them - dest.resize(nExpect); - for (unsigned int i = 0; i < nExpect; ++i) - dest[i] = vals[i]; - } - else - { - // We copy the read values nExpand times - dest.resize(vals.size() * nExpand); - for (unsigned int j = 0; j < nExpand; ++j) - { - for (std::size_t i = 0; i < vals.size(); ++i) - dest[i + j * vals.size()] = vals[i]; - } - } + return true; } +} +/** + * @brief Reads a vector valued parameter, that may also be a matrix + * @details It is automatically detected if the parameter is matrixoid or vectorial. + * If the user always wants a matrixoid result, the vectorial parameter + * is replicated to reach the requested size. The ordering is + * vec0rep0, vec1rep0, vec2rep0, vec0rep1, vec1rep1, vec2rep1, etc. + * + * @param [in] dest Destination vector in which the data is saved + * @param [in] paramProvider Parameter provider from which is read + * @param [in] dataSet Name of the dataset + * @param [in] nExpect Expected number of elements in the vectorial parameter + * @param [in] nExpand How often the vectorial parameter is replicated (@c 1 if vectorials should remain vectorial) + * @tparam ValType Type of the parameter, such as @c active or @c double + */ +template +inline void readParameterMatrix(std::vector& dest, IParameterProvider& paramProvider, + const std::string& dataSet, unsigned int nExpect, unsigned int nExpand) +{ + dest.clear(); + const std::vector vals = paramProvider.getDoubleArray(dataSet); - /** - * @brief Reads a parameter that depends on components and bound states available in a state-major matrix - * @details The ordering is expected to be state-major, that is, for a 3 component and 2 bound state parameter - * the parameters @f$p_{\text{State},\text{Comp}} @f$ are stored like this: - * @f[ \begin{pmatrix} p_{11} & p_{12} & p_{13} \\ p_{21} & p_{22} & p_{23} \\ p_{31} & p_{32} & p_{33} - * \Leftrightarrow \left( p_{11}, p_{12}, p_{13}, p_{21}, \dots, p_{23} \right). @f] - * The rows of the matrix which contain the parameters for all components of one specific bound state are - * appended to one another to form a long array. - * - * @param [in] dest Destination vector in which the data is saved - * @param [in] paramProvider Parameter provider from which is read - * @param [in] dataSet Name of the dataset - * @param [nComp] Number of required components - * @param [nStates] Number of required bound states - * @tparam SliceContainer_t Type of the slice container, such as @c cadet::util::SlicedVector - * @tparam ValType Type of the parameter, such as @c active or @c double - */ - template - inline void readBoundStateDependentParameter(SliceContainer_t& dest, IParameterProvider& paramProvider, const std::string& dataSet, unsigned int nComp, unsigned int nStates) + if (vals.size() == nExpect) { - dest.clear(); - const std::vector vals = paramProvider.getDoubleArray(dataSet); - - if (vals.size() < nComp * nStates) - { - throw InvalidParameterException("Not enough elements in dataset " + dataSet + " (expected " + std::to_string(nComp * nStates) - + " but got only " + std::to_string(vals.size()) + ")"); - } - - for (unsigned int i = 0; i < nStates; ++i) - { - dest.pushBackSlice(nComp); - ValType* const destCur = dest[i]; - for (unsigned int j = 0; j < nComp; ++j) - destCur[j] = vals[i * nComp + j]; - } + // Read expected number of values, so copy them + dest.resize(nExpect); + for (unsigned int i = 0; i < nExpect; ++i) + dest[i] = vals[i]; } - - - /** - * @brief Reads a parameter that depends on components and bound states available in a component-major matrix - * @details The ordering is expected to be component-major, that is, for a 3 component and 2 bound state parameter - * the parameters @f$p_{\text{State},\text{Comp}} @f$ are stored like this: - * @f[ \begin{pmatrix} p_{11} & p_{12} & p_{13} \\ p_{21} & p_{22} & p_{23} \\ p_{31} & p_{32} & p_{33} - * \Leftrightarrow \left( p_{11}, p_{21}, p_{12}, p_{22}, \dots, p_{23} \right). @f] - * The rows of the matrix which contain the parameters for all components of one specific bound state are - * appended to one another to form a long array. - * - * @param [in] dest Destination vector in which the data is saved - * @param [in] paramProvider Parameter provider from which is read - * @param [in] dataSet Name of the dataset - * @param [nComp] Number of required components - * @param [nStates] Array with number of bound states for each component - * @tparam SliceContainer_t Type of the slice container, such as @c cadet::util::SlicedVector - * @tparam ValType Type of the parameter, such as @c active or @c double - */ - template - inline void readBoundStateDependentParameter(SliceContainer_t& dest, IParameterProvider& paramProvider, const std::string& dataSet, unsigned int nComp, unsigned int const* const nStates) + else { - dest.clear(); - const std::vector vals = paramProvider.getDoubleArray(dataSet); - - unsigned int curIdx = 0; - for (unsigned int i = 0; i < nComp; ++i) + // We copy the read values nExpand times + dest.resize(vals.size() * nExpand); + for (unsigned int j = 0; j < nExpand; ++j) { - if (vals.size() < curIdx + nStates[i]) - throw InvalidParameterException("Not enough elements in dataset " + dataSet + " (expected at least " + std::to_string(curIdx + nStates[i]) - + " but got only " + std::to_string(vals.size()) + ")"); - - dest.pushBackSlice(nStates[i]); - ValType* const destCur = dest[i]; - for (unsigned int j = 0; j < nStates[i]; ++j, ++curIdx) - destCur[j] = vals[curIdx]; + for (std::size_t i = 0; i < vals.size(); ++i) + dest[i + j * vals.size()] = vals[i]; } } +} +/** + * @brief Reads a parameter that depends on components and bound states available in a state-major matrix + * @details The ordering is expected to be state-major, that is, for a 3 component and 2 bound state parameter + * the parameters @f$p_{\text{State},\text{Comp}} @f$ are stored like this: + * @f[ \begin{pmatrix} p_{11} & p_{12} & p_{13} \\ p_{21} & p_{22} & p_{23} \\ p_{31} & p_{32} & p_{33} + * \Leftrightarrow \left( p_{11}, p_{12}, p_{13}, p_{21}, \dots, p_{23} \right). @f] + * The rows of the matrix which contain the parameters for all components of one specific bound state are + * appended to one another to form a long array. + * + * @param [in] dest Destination vector in which the data is saved + * @param [in] paramProvider Parameter provider from which is read + * @param [in] dataSet Name of the dataset + * @param [nComp] Number of required components + * @param [nStates] Number of required bound states + * @tparam SliceContainer_t Type of the slice container, such as @c cadet::util::SlicedVector + * @tparam ValType Type of the parameter, such as @c active or @c double + */ +template +inline void readBoundStateDependentParameter(SliceContainer_t& dest, IParameterProvider& paramProvider, + const std::string& dataSet, unsigned int nComp, unsigned int nStates) +{ + dest.clear(); + const std::vector vals = paramProvider.getDoubleArray(dataSet); - /** - * @brief Reads a matrix valued parameter that depends on components and bound states available in a component-row-major 3D array - * @details The ordering is expected to be component-row-major, that is, for a 3 component and 2 bound state matrix valued parameter - * the parameters @f$p_{\text{Comp},\text{State},\text{State}} @f$ are stored like this: - * @f[ p_{i,\cdot,\cdot} = \begin{pmatrix} p_{i,1,1} & p_{i,1,2} \\ p_{i,2,1} & p_{i,2,2} \end{pmatrix} - * \Leftrightarrow \left( p_{111}, p_{112}, p_{121}, p_{122}, p_{211}, p_{212}, p_{221}, p_{222}, \dots, p_{322} \right). @f] - * The parameters are stored in the same ordering as they are read. - * - * @param [in] dest Destination vector in which the data is saved - * @param [in] paramProvider Parameter provider from which is read - * @param [in] dataSet Name of the dataset - * @param [nComp] Number of required components - * @param [nStates] Array with number of bound states for each component - * @tparam SliceContainer_t Type of the slice container, such as @c cadet::util::SlicedVector - * @tparam ValType Type of the parameter, such as @c active or @c double - */ - template - inline void readMatrixValuedBoundStateDependentParameter(SliceContainer_t& dest, IParameterProvider& paramProvider, const std::string& dataSet, unsigned int nComp, unsigned int const* const nStates) + if (vals.size() < nComp * nStates) { - dest.clear(); - const std::vector vals = paramProvider.getDoubleArray(dataSet); - - unsigned int curIdx = 0; - for (unsigned int i = 0; i < nComp; ++i) - { - const unsigned int curSize = nStates[i] * nStates[i]; - if (vals.size() < curIdx + curSize) - throw InvalidParameterException("Not enough elements in dataset " + dataSet + " (expected at least " + std::to_string(curIdx + curSize) - + " but got only " + std::to_string(vals.size()) + ")"); - - dest.pushBackSlice(curSize); - ValType* const destCur = dest[i]; - for (unsigned int j = 0; j < curSize; ++j, ++curIdx) - destCur[j] = vals[curIdx]; - } + throw InvalidParameterException("Not enough elements in dataset " + dataSet + " (expected " + + std::to_string(nComp * nStates) + " but got only " + + std::to_string(vals.size()) + ")"); } - - /** - * @brief Selects an array of possible section dependent vectorial parameters from a list - * @details The parameters may be section dependent, but do not have to. The function is given - * a vector with all parameter values for all indices (vectorial parameter) and sections - * (if section dependent). The vector contains only @p nElements elements if the - * parameter is not section dependent. Otherwise, it should contain as much elements - * as @c nSec * nElements. The correct element array is selected according to the index of - * the current section and the section dependency of the parameter. - * - * @param [in] data Vector with all parameter values - * @param [in] nElements Number of elements of the vectorial parameter (without taking section dependency into account) - * @param [in] secIdx Index of the current section - * @return Pointer to the first element of the correct vectorial parameter array - */ - template - inline T const* getSectionDependentSlice(const std::vector& data, unsigned int nElements, unsigned int secIdx) + for (unsigned int i = 0; i < nStates; ++i) { - // Check if the vector contains enough elements - if (cadet_unlikely(data.size() >= nElements * (secIdx + 1))) - { - // Advance data pointer to correct position - T const* out = data.data(); - return out + nElements * secIdx; - } - else - return data.data(); + dest.pushBackSlice(nComp); + ValType* const destCur = dest[i]; + for (unsigned int j = 0; j < nComp; ++j) + destCur[j] = vals[i * nComp + j]; } +} +/** + * @brief Reads a parameter that depends on components and bound states available in a component-major matrix + * @details The ordering is expected to be component-major, that is, for a 3 component and 2 bound state parameter + * the parameters @f$p_{\text{State},\text{Comp}} @f$ are stored like this: + * @f[ \begin{pmatrix} p_{11} & p_{12} & p_{13} \\ p_{21} & p_{22} & p_{23} \\ p_{31} & p_{32} & p_{33} + * \Leftrightarrow \left( p_{11}, p_{21}, p_{12}, p_{22}, \dots, p_{23} \right). @f] + * The rows of the matrix which contain the parameters for all components of one specific bound state are + * appended to one another to form a long array. + * + * @param [in] dest Destination vector in which the data is saved + * @param [in] paramProvider Parameter provider from which is read + * @param [in] dataSet Name of the dataset + * @param [nComp] Number of required components + * @param [nStates] Array with number of bound states for each component + * @tparam SliceContainer_t Type of the slice container, such as @c cadet::util::SlicedVector + * @tparam ValType Type of the parameter, such as @c active or @c double + */ +template +inline void readBoundStateDependentParameter(SliceContainer_t& dest, IParameterProvider& paramProvider, + const std::string& dataSet, unsigned int nComp, + unsigned int const* const nStates) +{ + dest.clear(); + const std::vector vals = paramProvider.getDoubleArray(dataSet); - /** - * @brief Selects a possibly section dependent scalar parameter from a list of parameters - * @details The parameter may be section dependent, but does not have to. The function is given - * a vector with all parameter values. The vector contains only one element if the - * parameter is not section dependent. Otherwise, it should contain as much elements - * as there are sections. The correct element is selected according to the index of - * the current section and the section dependency of the parameter. - * - * @param [in] data Vector with all parameter values (for every section, or just one) - * @param [in] secIdx Index of the current section - * @return The correct scalar parameter - */ - template - inline const T& getSectionDependentScalar(const std::vector& data, unsigned int secIdx) + unsigned int curIdx = 0; + for (unsigned int i = 0; i < nComp; ++i) { - if (cadet_unlikely(data.size() > 1)) - { - cadet_assert(data.size() > secIdx); - return data[secIdx]; - } - else - { - cadet_assert(data.size() == 1); - return data[0]; - } + if (vals.size() < curIdx + nStates[i]) + throw InvalidParameterException("Not enough elements in dataset " + dataSet + " (expected at least " + + std::to_string(curIdx + nStates[i]) + " but got only " + + std::to_string(vals.size()) + ")"); + + dest.pushBackSlice(nStates[i]); + ValType* const destCur = dest[i]; + for (unsigned int j = 0; j < nStates[i]; ++j, ++curIdx) + destCur[j] = vals[curIdx]; } +} +/** + * @brief Reads a matrix valued parameter that depends on components and bound states available in a component-row-major + * 3D array + * @details The ordering is expected to be component-row-major, that is, for a 3 component and 2 bound state matrix + * valued parameter the parameters @f$p_{\text{Comp},\text{State},\text{State}} @f$ are stored like this: + * @f[ p_{i,\cdot,\cdot} = \begin{pmatrix} p_{i,1,1} & p_{i,1,2} \\ p_{i,2,1} & p_{i,2,2} \end{pmatrix} + * \Leftrightarrow \left( p_{111}, p_{112}, p_{121}, p_{122}, p_{211}, p_{212}, p_{221}, p_{222}, \dots, + * p_{322} \right). @f] The parameters are stored in the same ordering as they are read. + * + * @param [in] dest Destination vector in which the data is saved + * @param [in] paramProvider Parameter provider from which is read + * @param [in] dataSet Name of the dataset + * @param [nComp] Number of required components + * @param [nStates] Array with number of bound states for each component + * @tparam SliceContainer_t Type of the slice container, such as @c cadet::util::SlicedVector + * @tparam ValType Type of the parameter, such as @c active or @c double + */ +template +inline void readMatrixValuedBoundStateDependentParameter(SliceContainer_t& dest, IParameterProvider& paramProvider, + const std::string& dataSet, unsigned int nComp, + unsigned int const* const nStates) +{ + dest.clear(); + const std::vector vals = paramProvider.getDoubleArray(dataSet); - /** - * @brief Registers a 1D parameter array - * @param [in,out] map Map to which the parameters are added - * @param [in] params Array with parameters to be registered - * @param [in] size Number of elements in the array - * @param [in] pic Callable that returns a ParameterId based on whether there is more than one item in the array and the array index - */ - template - inline void registerParam1DArray(std::unordered_map& map, active* params, unsigned int size, ParamIdCreator&& pic) + unsigned int curIdx = 0; + for (unsigned int i = 0; i < nComp; ++i) { - const bool multi = size > 1; - for (unsigned int i = 0; i < size; ++i) - { - map[pic(multi, i)] = params + i; - } + const unsigned int curSize = nStates[i] * nStates[i]; + if (vals.size() < curIdx + curSize) + throw InvalidParameterException("Not enough elements in dataset " + dataSet + " (expected at least " + + std::to_string(curIdx + curSize) + " but got only " + + std::to_string(vals.size()) + ")"); + + dest.pushBackSlice(curSize); + ValType* const destCur = dest[i]; + for (unsigned int j = 0; j < curSize; ++j, ++curIdx) + destCur[j] = vals[curIdx]; } +} - - /** - * @brief Registers a 1D parameter array - * @param [in,out] map Map to which the parameters are added - * @param [in] params Vector with parameters to be registered - * @param [in] pic Callable that returns a ParameterId based on whether there is more than one item in the array and the array index - */ - template - inline void registerParam1DArray(std::unordered_map& map, std::vector& params, ParamIdCreator&& pic) +/** + * @brief Selects an array of possible section dependent vectorial parameters from a list + * @details The parameters may be section dependent, but do not have to. The function is given + * a vector with all parameter values for all indices (vectorial parameter) and sections + * (if section dependent). The vector contains only @p nElements elements if the + * parameter is not section dependent. Otherwise, it should contain as much elements + * as @c nSec * nElements. The correct element array is selected according to the index of + * the current section and the section dependency of the parameter. + * + * @param [in] data Vector with all parameter values + * @param [in] nElements Number of elements of the vectorial parameter (without taking section dependency into account) + * @param [in] secIdx Index of the current section + * @return Pointer to the first element of the correct vectorial parameter array + */ +template +inline T const* getSectionDependentSlice(const std::vector& data, unsigned int nElements, unsigned int secIdx) +{ + // Check if the vector contains enough elements + if (cadet_unlikely(data.size() >= nElements * (secIdx + 1))) { - registerParam1DArray(map, params.data(), params.size(), std::forward(pic)); + // Advance data pointer to correct position + T const* out = data.data(); + return out + nElements * secIdx; } + else + return data.data(); +} +/** + * @brief Selects a possibly section dependent scalar parameter from a list of parameters + * @details The parameter may be section dependent, but does not have to. The function is given + * a vector with all parameter values. The vector contains only one element if the + * parameter is not section dependent. Otherwise, it should contain as much elements + * as there are sections. The correct element is selected according to the index of + * the current section and the section dependency of the parameter. + * + * @param [in] data Vector with all parameter values (for every section, or just one) + * @param [in] secIdx Index of the current section + * @return The correct scalar parameter + */ +template inline const T& getSectionDependentScalar(const std::vector& data, unsigned int secIdx) +{ + if (cadet_unlikely(data.size() > 1)) + { + cadet_assert(data.size() > secIdx); + return data[secIdx]; + } + else + { + cadet_assert(data.size() == 1); + return data[0]; + } +} - /** - * @brief Registers a sliced 1D parameter array - * @param [in,out] map Map to which the parameters are added - * @param [in] params Sliced vector with parameters to be registered - * @param [in] pic Callable that returns a ParameterId based on slice index and item index within slice - */ - template - inline void registerParam1DNonUniform(std::unordered_map& map, SliceContainer_t& params, ParamIdCreator pic) +/** + * @brief Registers a 1D parameter array + * @param [in,out] map Map to which the parameters are added + * @param [in] params Array with parameters to be registered + * @param [in] size Number of elements in the array + * @param [in] pic Callable that returns a ParameterId based on whether there is more than one item in the array and the + * array index + */ +template +inline void registerParam1DArray(std::unordered_map& map, active* params, unsigned int size, + ParamIdCreator&& pic) +{ + const bool multi = size > 1; + for (unsigned int i = 0; i < size; ++i) { - const unsigned int numSlices = params.slices(); - for (unsigned int s = 0; s < numSlices; ++s) - { - active* const slice = params[s]; - for (unsigned int i = 0; i < params.sliceSize(s); ++i) - { - map[pic(numSlices > 1, s, i)] = slice + i; - } - } + map[pic(multi, i)] = params + i; } +} +/** + * @brief Registers a 1D parameter array + * @param [in,out] map Map to which the parameters are added + * @param [in] params Vector with parameters to be registered + * @param [in] pic Callable that returns a ParameterId based on whether there is more than one item in the array and the + * array index + */ +template +inline void registerParam1DArray(std::unordered_map& map, std::vector& params, + ParamIdCreator&& pic) +{ + registerParam1DArray(map, params.data(), params.size(), std::forward(pic)); +} - /** - * @brief Registers a 2D parameter array - * @details The linearized array is processed sequentially. For each linear index, the corresponding outer and inner index - * is computed. For a row-major storage, this corresponds to row and column index, respectively. In this case, - * the @p innerSize is the number of columns. The callable @p pic is used to construct a ParameterId based on - * whether there are multiple outer indices (e.g., more than one row), the outer index, and the inner index. - * - * @param [in,out] map Map to which the parameters are added - * @param [in] params Linearized array with parameters to be registered - * @param [in] size Number of elements in the array - * @param [in] pic Callable that returns a ParameterId based on whether more than one outer index is present, the outer index, and the inner index - * @param [in] innerSize Size of the inner dimension - */ - template - inline void registerParam2DArray(std::unordered_map& map, active* params, unsigned int size, ParamIdCreator&& pic, unsigned int innerSize) +/** + * @brief Registers a sliced 1D parameter array + * @param [in,out] map Map to which the parameters are added + * @param [in] params Sliced vector with parameters to be registered + * @param [in] pic Callable that returns a ParameterId based on slice index and item index within slice + */ +template +inline void registerParam1DNonUniform(std::unordered_map& map, SliceContainer_t& params, + ParamIdCreator pic) +{ + const unsigned int numSlices = params.slices(); + for (unsigned int s = 0; s < numSlices; ++s) { - const bool multiOuter = size > innerSize; - for (unsigned int i = 0; i < size; ++i) + active* const slice = params[s]; + for (unsigned int i = 0; i < params.sliceSize(s); ++i) { - const unsigned int idxOuter = i / innerSize; - const unsigned int idxInner = i % innerSize; - map[pic(multiOuter, idxOuter, idxInner)] = params + i; + map[pic(numSlices > 1, s, i)] = slice + i; } } +} - - /** - * @brief Registers a 2D parameter array - * @details The linearized array is processed sequentially. For each linear index, the corresponding outer and inner index - * is computed. For a row-major storage, this corresponds to row and column index, respectively. In this case, - * the @p innerSize is the number of columns. The callable @p pic is used to construct a ParameterId based on - * whether there are multiple outer indices (e.g., more than one row), the outer index, and the inner index. - * - * @param [in,out] map Map to which the parameters are added - * @param [in] params Linearized vector with parameters to be registered - * @param [in] pic Callable that returns a ParameterId based on whether more than one outer index is present, the outer index, and the inner index - * @param [in] innerSize Size of the inner dimension - */ - template - inline void registerParam2DArray(std::unordered_map& map, std::vector& params, ParamIdCreator&& pic, unsigned int innerSize) +/** + * @brief Registers a 2D parameter array + * @details The linearized array is processed sequentially. For each linear index, the corresponding outer and inner + * index is computed. For a row-major storage, this corresponds to row and column index, respectively. In this case, the + * @p innerSize is the number of columns. The callable @p pic is used to construct a ParameterId based on whether there + * are multiple outer indices (e.g., more than one row), the outer index, and the inner index. + * + * @param [in,out] map Map to which the parameters are added + * @param [in] params Linearized array with parameters to be registered + * @param [in] size Number of elements in the array + * @param [in] pic Callable that returns a ParameterId based on whether more than one outer index is present, the outer + * index, and the inner index + * @param [in] innerSize Size of the inner dimension + */ +template +inline void registerParam2DArray(std::unordered_map& map, active* params, unsigned int size, + ParamIdCreator&& pic, unsigned int innerSize) +{ + const bool multiOuter = size > innerSize; + for (unsigned int i = 0; i < size; ++i) { - registerParam2DArray(map, params.data(), params.size(), std::forward(pic), innerSize); + const unsigned int idxOuter = i / innerSize; + const unsigned int idxInner = i % innerSize; + map[pic(multiOuter, idxOuter, idxInner)] = params + i; } +} +/** + * @brief Registers a 2D parameter array + * @details The linearized array is processed sequentially. For each linear index, the corresponding outer and inner + * index is computed. For a row-major storage, this corresponds to row and column index, respectively. In this case, the + * @p innerSize is the number of columns. The callable @p pic is used to construct a ParameterId based on whether there + * are multiple outer indices (e.g., more than one row), the outer index, and the inner index. + * + * @param [in,out] map Map to which the parameters are added + * @param [in] params Linearized vector with parameters to be registered + * @param [in] pic Callable that returns a ParameterId based on whether more than one outer index is present, the outer + * index, and the inner index + * @param [in] innerSize Size of the inner dimension + */ +template +inline void registerParam2DArray(std::unordered_map& map, std::vector& params, + ParamIdCreator&& pic, unsigned int innerSize) +{ + registerParam2DArray(map, params.data(), params.size(), std::forward(pic), innerSize); +} - /** - * @brief Registers a sliced 2D parameter array - * @details The linearized array is processed sequentially. For each linear index, the corresponding outer, slice index, - * and item index in slice is computed. For a row-major storage, this corresponds to row, column slice, and - * slice item index, respectively. In this case, the sizes of the column slices are given by @p innerSizes. - * The callable @p pic is used to construct a ParameterId based on whether there are multiple outer indices - * (e.g., more than one row), the outer index, the inner slice index, and the item index in the slice. - * - * @param [in,out] map Map to which the parameters are added - * @param [in] params Linearized vector with parameters to be registered - * @param [in] pic Callable that returns a ParameterId based on whether more than one outer index is present, outer index, inner slice index, item index within slice - * @param [in] innerSices Array with sizes of the inner slices - * @param [in] numSizes Number of slices, number of elements in @p innerSlices - */ - template - inline void registerParam2DNonUniformArray(std::unordered_map& map, std::vector& params, ParamIdCreator pic, unsigned int const* innerSizes, unsigned int numSizes) +/** + * @brief Registers a sliced 2D parameter array + * @details The linearized array is processed sequentially. For each linear index, the corresponding outer, slice index, + * and item index in slice is computed. For a row-major storage, this corresponds to row, column slice, and + * slice item index, respectively. In this case, the sizes of the column slices are given by @p innerSizes. + * The callable @p pic is used to construct a ParameterId based on whether there are multiple outer indices + * (e.g., more than one row), the outer index, the inner slice index, and the item index in the slice. + * + * @param [in,out] map Map to which the parameters are added + * @param [in] params Linearized vector with parameters to be registered + * @param [in] pic Callable that returns a ParameterId based on whether more than one outer index is present, outer + * index, inner slice index, item index within slice + * @param [in] innerSices Array with sizes of the inner slices + * @param [in] numSizes Number of slices, number of elements in @p innerSlices + */ +template +inline void registerParam2DNonUniformArray(std::unordered_map& map, std::vector& params, + ParamIdCreator pic, unsigned int const* innerSizes, unsigned int numSizes) +{ + const bool multiOuter = params.size() > std::accumulate(innerSizes, innerSizes + numSizes, 0u); + unsigned int idx = 0; + unsigned int idxOuter = 0; + while (idx < params.size()) { - const bool multiOuter = params.size() > std::accumulate(innerSizes, innerSizes + numSizes, 0u); - unsigned int idx = 0; - unsigned int idxOuter = 0; - while (idx < params.size()) + for (unsigned int i = 0; i < numSizes; ++i) { - for (unsigned int i = 0; i < numSizes; ++i) + for (unsigned j = 0; j < innerSizes[i]; ++j, ++idx) { - for (unsigned j = 0; j < innerSizes[i]; ++j, ++idx) - { - map[pic(multiOuter, idxOuter, i, j)] = ¶ms[idx]; - } + map[pic(multiOuter, idxOuter, i, j)] = ¶ms[idx]; } - ++idxOuter; - } - } - - - /** - * @brief Registers a 3D parameter array - * @details The linearized array is processed sequentially. For each linear index, the corresponding outer, mid, and inner index - * is computed. For a page-row-major storage, this corresponds to page, row, and column index, respectively. In this case, - * the @p innerSize is the number of columns and @p midSize is the number of rows. The callable @p pic is used to - * construct a ParameterId based on whether there are multiple outer indices (e.g., more than one page), the outer index, - * the mid index, and the inner index. - * - * @param [in,out] map Map to which the parameters are added - * @param [in] params Linearized array with parameters to be registered - * @param [in] size Number of elements in the array - * @param [in] pic Callable that returns a ParameterId based on whether more than one outer index is present, outer index, mid index, and inner index - * @param [in] innerSize Size of the inner dimension - * @param [in] midSize Size of the mid dimension - */ - template - inline void registerParam3DArray(std::unordered_map& map, active* params, unsigned int size, ParamIdCreator&& pic, unsigned int innerSize, unsigned int midSize) - { - const bool multiOuter = size > innerSize * midSize; - for (unsigned int i = 0; i < size; ++i) - { - const unsigned int idxOuter = i / (innerSize * midSize); - const unsigned int idxRem = i % (innerSize * midSize); - const unsigned int idxMid = idxRem / innerSize; - const unsigned int idxInner = idxRem % innerSize; - map[pic(multiOuter, idxOuter, idxMid, idxInner)] = params + i; } + ++idxOuter; } +} - - /** - * @brief Registers a 3D parameter array - * @details The linearized array is processed sequentially. For each linear index, the corresponding outer, mid, and inner index - * is computed. For a page-row-major storage, this corresponds to page, row, and column index, respectively. In this case, - * the @p innerSize is the number of columns and @p midSize is the number of rows. The callable @p pic is used to - * construct a ParameterId based on whether there are multiple outer indices (e.g., more than one page), the outer index, - * the mid index, and the inner index. - * - * @param [in,out] map Map to which the parameters are added - * @param [in] params Linearized vector with parameters to be registered - * @param [in] pic Callable that returns a ParameterId based on whether more than one outer index is present, outer index, mid index, and inner index - * @param [in] innerSize Size of the inner dimension - * @param [in] midSize Size of the mid dimension - */ - template - inline void registerParam3DArray(std::unordered_map& map, std::vector& params, ParamIdCreator&& pic, unsigned int innerSize, unsigned int midSize) +/** + * @brief Registers a 3D parameter array + * @details The linearized array is processed sequentially. For each linear index, the corresponding outer, mid, and + * inner index is computed. For a page-row-major storage, this corresponds to page, row, and column index, respectively. + * In this case, the @p innerSize is the number of columns and @p midSize is the number of rows. The callable @p pic is + * used to construct a ParameterId based on whether there are multiple outer indices (e.g., more than one page), the + * outer index, the mid index, and the inner index. + * + * @param [in,out] map Map to which the parameters are added + * @param [in] params Linearized array with parameters to be registered + * @param [in] size Number of elements in the array + * @param [in] pic Callable that returns a ParameterId based on whether more than one outer index is present, outer + * index, mid index, and inner index + * @param [in] innerSize Size of the inner dimension + * @param [in] midSize Size of the mid dimension + */ +template +inline void registerParam3DArray(std::unordered_map& map, active* params, unsigned int size, + ParamIdCreator&& pic, unsigned int innerSize, unsigned int midSize) +{ + const bool multiOuter = size > innerSize * midSize; + for (unsigned int i = 0; i < size; ++i) { - registerParam3DArray(map, params.data(), params.size(), std::forward(pic), innerSize, midSize); + const unsigned int idxOuter = i / (innerSize * midSize); + const unsigned int idxRem = i % (innerSize * midSize); + const unsigned int idxMid = idxRem / innerSize; + const unsigned int idxInner = idxRem % innerSize; + map[pic(multiOuter, idxOuter, idxMid, idxInner)] = params + i; } +} +/** + * @brief Registers a 3D parameter array + * @details The linearized array is processed sequentially. For each linear index, the corresponding outer, mid, and + * inner index is computed. For a page-row-major storage, this corresponds to page, row, and column index, respectively. + * In this case, the @p innerSize is the number of columns and @p midSize is the number of rows. The callable @p pic is + * used to construct a ParameterId based on whether there are multiple outer indices (e.g., more than one page), the + * outer index, the mid index, and the inner index. + * + * @param [in,out] map Map to which the parameters are added + * @param [in] params Linearized vector with parameters to be registered + * @param [in] pic Callable that returns a ParameterId based on whether more than one outer index is present, outer + * index, mid index, and inner index + * @param [in] innerSize Size of the inner dimension + * @param [in] midSize Size of the mid dimension + */ +template +inline void registerParam3DArray(std::unordered_map& map, std::vector& params, + ParamIdCreator&& pic, unsigned int innerSize, unsigned int midSize) +{ + registerParam3DArray(map, params.data(), params.size(), std::forward(pic), innerSize, midSize); +} - /** - * @brief Registers a sliced 3D parameter array - * @details The linearized array is processed sequentially. For each linear index, the corresponding outer, mid, slice index, - * and item index in slice is computed. For a page-row-major storage, this corresponds to page, row, column slice, and - * slice item index, respectively. In this case, the sizes of the column slices are given by @p innerSizes and - * @p midSize is the number of rows. The callable @p pic is used to construct a ParameterId based on whether there are - * multiple outer indices (e.g., more than one page), the outer index, the mid index, the inner slice index, and the - * item index in the slice. - * - * @param [in,out] map Map to which the parameters are added - * @param [in] params Linearized vector with parameters to be registered - * @param [in] pic Callable that returns a ParameterId based on whether more than one outer index is present, outer index, mid index, inner slice index, item index within slice - * @param [in] innerSices Array with sizes of the inner slices - * @param [in] numSizes Number of slices, number of elements in @p innerSlices - * @param [in] midSize Size of the mid dimension - */ - template - inline void registerParam3DNonUniformArray(std::unordered_map& map, std::vector& params, ParamIdCreator pic, unsigned int const* innerSizes, unsigned int numSizes, unsigned int midSize) +/** + * @brief Registers a sliced 3D parameter array + * @details The linearized array is processed sequentially. For each linear index, the corresponding outer, mid, slice + * index, and item index in slice is computed. For a page-row-major storage, this corresponds to page, row, column + * slice, and slice item index, respectively. In this case, the sizes of the column slices are given by @p innerSizes + * and + * @p midSize is the number of rows. The callable @p pic is used to construct a ParameterId based on whether + * there are multiple outer indices (e.g., more than one page), the outer index, the mid index, the inner slice index, + * and the item index in the slice. + * + * @param [in,out] map Map to which the parameters are added + * @param [in] params Linearized vector with parameters to be registered + * @param [in] pic Callable that returns a ParameterId based on whether more than one outer index is present, outer + * index, mid index, inner slice index, item index within slice + * @param [in] innerSices Array with sizes of the inner slices + * @param [in] numSizes Number of slices, number of elements in @p innerSlices + * @param [in] midSize Size of the mid dimension + */ +template +inline void registerParam3DNonUniformArray(std::unordered_map& map, std::vector& params, + ParamIdCreator pic, unsigned int const* innerSizes, unsigned int numSizes, + unsigned int midSize) +{ + const bool multiOuter = params.size() > std::accumulate(innerSizes, innerSizes + numSizes, 0u) * midSize; + unsigned int idx = 0; + unsigned int idxOuter = 0; + while (idx < params.size()) { - const bool multiOuter = params.size() > std::accumulate(innerSizes, innerSizes + numSizes, 0u) * midSize; - unsigned int idx = 0; - unsigned int idxOuter = 0; - while (idx < params.size()) + for (unsigned int k = 0; k < midSize; ++k) { - for (unsigned int k = 0; k < midSize; ++k) + for (unsigned int i = 0; i < numSizes; ++i) { - for (unsigned int i = 0; i < numSizes; ++i) + for (unsigned j = 0; j < innerSizes[i]; ++j, ++idx) { - for (unsigned j = 0; j < innerSizes[i]; ++j, ++idx) - { - map[pic(multiOuter, idxOuter, k, i, j)] = ¶ms[idx]; - } + map[pic(multiOuter, idxOuter, k, i, j)] = ¶ms[idx]; } } - ++idxOuter; } + ++idxOuter; } +} +/** + * @brief Registers scalar parameters that may be section dependent + * + * @param [in] nameHash Hashed name of the parameter + * @param [in,out] map Map to which the parameters are added + * @param [in] params Vector with parameters to be registered (only 1 if not section dependent) + * @param [in] unitOpIdx Index of the unit operation + */ +inline void registerScalarSectionDependentParam(const StringHash nameHash, + std::unordered_map& map, + std::vector& params, UnitOpIdx unitOpIdx, + ParticleTypeIdx parType) +{ + registerParam1DArray(map, params, [=](bool multi, unsigned int sec) { + return makeParamId(nameHash, unitOpIdx, CompIndep, parType, BoundStateIndep, ReactionIndep, + multi ? sec : SectionIndep); + }); +} - /** - * @brief Registers scalar parameters that may be section dependent - * - * @param [in] nameHash Hashed name of the parameter - * @param [in,out] map Map to which the parameters are added - * @param [in] params Vector with parameters to be registered (only 1 if not section dependent) - * @param [in] unitOpIdx Index of the unit operation - */ - inline void registerScalarSectionDependentParam(const StringHash nameHash, std::unordered_map& map, std::vector& params, UnitOpIdx unitOpIdx, ParticleTypeIdx parType) - { - registerParam1DArray(map, params, [=](bool multi, unsigned int sec) { return makeParamId(nameHash, unitOpIdx, CompIndep, parType, BoundStateIndep, ReactionIndep, multi ? sec : SectionIndep); }); - } - - - /** - * @brief Registers scalar parameters that may be bound phase dependent - * - * @param [in] nameHash Hashed name of the parameter - * @param [in,out] map Map to which the parameters are added - * @param [in] params Vector with parameters to be registered (only 1 if not bound state dependent) - * @param [in] unitOpIdx Index of the unit operation - */ - inline void registerScalarBoundStateDependentParam(const StringHash nameHash, std::unordered_map& map, std::vector& params, UnitOpIdx unitOpIdx, ParticleTypeIdx parType) - { - registerParam1DArray(map, params, [=](bool multi, unsigned int bnd) { return makeParamId(nameHash, unitOpIdx, CompIndep, parType, bnd, ReactionIndep, SectionIndep); }); - } - - - /** - * @brief Registers vector parameters (component dependent) - * @param [in] nameHash Hashed name of the parameter - * @param [in,out] map Map to which the parameters are added - * @param [in] params Vector with parameters to be registered - * @param [in] unitOpIdx Index of the unit operation - */ - inline void registerComponentDependentParam(const StringHash nameHash, std::unordered_map& map, std::vector& params, UnitOpIdx unitOpIdx, ParticleTypeIdx parType) - { - registerParam1DArray(map, params, [=](bool multi, unsigned int comp) { return makeParamId(nameHash, unitOpIdx, comp, parType, BoundStateIndep, ReactionIndep, SectionIndep); }); - } - - - /** - * @brief Registers vector parameters (component dependent) that may be section dependent - * @details The ordering is section-major, that is comp0sec0, comp1sec0, comp2sec0, comp0sec1, comp1sec1, comp2sec1, ... - * The number of components is given in @p compStride. - * - * @param [in] nameHash Hashed name of the parameter - * @param [in,out] map Map to which the parameters are added - * @param [in] params Vector with parameters to be registered - * @param [in] unitOpIdx Index of the unit operation - * @param [in] compStride Distance between two parameters with consecutive sections - */ - inline void registerComponentSectionDependentParam(const StringHash nameHash, std::unordered_map& map, std::vector& params, UnitOpIdx unitOpIdx, ParticleTypeIdx parType, unsigned int compStride) - { - registerParam2DArray(map, params, [=](bool multi, unsigned int sec, unsigned int comp) { return makeParamId(nameHash, unitOpIdx, comp, parType, BoundStateIndep, ReactionIndep, multi ? sec : SectionIndep); }, compStride); - } - - - /** - * @brief Registers vector parameters (component dependent) that are also dependent on the bound phase - * @details The ordering is state-major, that is comp0state0, comp1state0, comp2state0, comp0state1, comp1state1, comp2state1, ... - * The number of components is given in @p compStride. - * - * @param [in] nameHash Hashed name of the parameter - * @param [in,out] map Map to which the parameters are added - * @param [in] params Vector with parameters to be registered - * @param [in] unitOpIdx Index of the unit operation - * @param [in] compStride Distance between two parameters with consecutive bound phases - */ - inline void registerComponentBoundStateDependentParam(const StringHash nameHash, std::unordered_map& map, std::vector& params, UnitOpIdx unitOpIdx, ParticleTypeIdx parType, unsigned int compStride) - { - registerParam2DArray(map, params, [=](bool multi, unsigned int bnd, unsigned int comp) { return makeParamId(nameHash, unitOpIdx, comp, parType, bnd, ReactionIndep, SectionIndep); }, compStride); - } +/** + * @brief Registers scalar parameters that may be bound phase dependent + * + * @param [in] nameHash Hashed name of the parameter + * @param [in,out] map Map to which the parameters are added + * @param [in] params Vector with parameters to be registered (only 1 if not bound state dependent) + * @param [in] unitOpIdx Index of the unit operation + */ +inline void registerScalarBoundStateDependentParam(const StringHash nameHash, + std::unordered_map& map, + std::vector& params, UnitOpIdx unitOpIdx, + ParticleTypeIdx parType) +{ + registerParam1DArray(map, params, [=](bool multi, unsigned int bnd) { + return makeParamId(nameHash, unitOpIdx, CompIndep, parType, bnd, ReactionIndep, SectionIndep); + }); +} +/** + * @brief Registers vector parameters (component dependent) + * @param [in] nameHash Hashed name of the parameter + * @param [in,out] map Map to which the parameters are added + * @param [in] params Vector with parameters to be registered + * @param [in] unitOpIdx Index of the unit operation + */ +inline void registerComponentDependentParam(const StringHash nameHash, std::unordered_map& map, + std::vector& params, UnitOpIdx unitOpIdx, ParticleTypeIdx parType) +{ + registerParam1DArray(map, params, [=](bool multi, unsigned int comp) { + return makeParamId(nameHash, unitOpIdx, comp, parType, BoundStateIndep, ReactionIndep, SectionIndep); + }); +} - /** - * @brief Registers vector parameters (component dependent) that are also dependent on the bound phase - * @details The ordering is state-major, that is comp0state0, comp1state0, comp2state0, comp0state1, comp1state1, comp2state1, ... - * It is assumed that only one bound state is present. - * - * @param [in] nameHash Hashed name of the parameter - * @param [in,out] map Map to which the parameters are added - * @param [in] params Vector with parameters to be registered - * @param [in] unitOpIdx Index of the unit operation - */ - inline void registerComponentBoundStateDependentParam(const StringHash nameHash, std::unordered_map& map, std::vector& params, UnitOpIdx unitOpIdx, ParticleTypeIdx parType) - { - registerComponentBoundStateDependentParam(nameHash, map, params, unitOpIdx, parType, params.size()); - } +/** + * @brief Registers vector parameters (component dependent) that may be section dependent + * @details The ordering is section-major, that is comp0sec0, comp1sec0, comp2sec0, comp0sec1, comp1sec1, comp2sec1, ... + * The number of components is given in @p compStride. + * + * @param [in] nameHash Hashed name of the parameter + * @param [in,out] map Map to which the parameters are added + * @param [in] params Vector with parameters to be registered + * @param [in] unitOpIdx Index of the unit operation + * @param [in] compStride Distance between two parameters with consecutive sections + */ +inline void registerComponentSectionDependentParam(const StringHash nameHash, + std::unordered_map& map, + std::vector& params, UnitOpIdx unitOpIdx, + ParticleTypeIdx parType, unsigned int compStride) +{ + registerParam2DArray( + map, params, + [=](bool multi, unsigned int sec, unsigned int comp) { + return makeParamId(nameHash, unitOpIdx, comp, parType, BoundStateIndep, ReactionIndep, + multi ? sec : SectionIndep); + }, + compStride); +} +/** + * @brief Registers vector parameters (component dependent) that are also dependent on the bound phase + * @details The ordering is state-major, that is comp0state0, comp1state0, comp2state0, comp0state1, comp1state1, + * comp2state1, ... The number of components is given in @p compStride. + * + * @param [in] nameHash Hashed name of the parameter + * @param [in,out] map Map to which the parameters are added + * @param [in] params Vector with parameters to be registered + * @param [in] unitOpIdx Index of the unit operation + * @param [in] compStride Distance between two parameters with consecutive bound phases + */ +inline void registerComponentBoundStateDependentParam(const StringHash nameHash, + std::unordered_map& map, + std::vector& params, UnitOpIdx unitOpIdx, + ParticleTypeIdx parType, unsigned int compStride) +{ + registerParam2DArray( + map, params, + [=](bool multi, unsigned int bnd, unsigned int comp) { + return makeParamId(nameHash, unitOpIdx, comp, parType, bnd, ReactionIndep, SectionIndep); + }, + compStride); +} - /** - * @brief Registers parameters that depend on component and bound state stored in state-major ordering - * @details The ordering is state-major, that is comp0state0, comp1state0, comp2state0, comp0state1, comp1state1, comp2state1, ... - * - * @param [in] nameHash Hashed name of the parameter - * @param [in,out] map Map to which the parameters are added - * @param [in] params Sliced vector with parameters to be registered (each slice corresponds to one bound state and contains all components) - * @param [in] unitOpIdx Index of the unit operation - * @tparam SliceContainer_t Type of the slice container, such as @c cadet::util::SlicedVector - */ - template - inline void registerComponentBoundStateDependentParam(const StringHash nameHash, std::unordered_map& map, SliceContainer_t& params, UnitOpIdx unitOpIdx, ParticleTypeIdx parType) - { - registerParam1DNonUniform(map, params, [=](bool multi, unsigned int state, unsigned int comp) { return makeParamId(nameHash, unitOpIdx, comp, parType, state, ReactionIndep, SectionIndep); }); - } +/** + * @brief Registers vector parameters (component dependent) that are also dependent on the bound phase + * @details The ordering is state-major, that is comp0state0, comp1state0, comp2state0, comp0state1, comp1state1, + * comp2state1, ... It is assumed that only one bound state is present. + * + * @param [in] nameHash Hashed name of the parameter + * @param [in,out] map Map to which the parameters are added + * @param [in] params Vector with parameters to be registered + * @param [in] unitOpIdx Index of the unit operation + */ +inline void registerComponentBoundStateDependentParam(const StringHash nameHash, + std::unordered_map& map, + std::vector& params, UnitOpIdx unitOpIdx, + ParticleTypeIdx parType) +{ + registerComponentBoundStateDependentParam(nameHash, map, params, unitOpIdx, parType, params.size()); +} +/** + * @brief Registers parameters that depend on component and bound state stored in state-major ordering + * @details The ordering is state-major, that is comp0state0, comp1state0, comp2state0, comp0state1, comp1state1, + * comp2state1, ... + * + * @param [in] nameHash Hashed name of the parameter + * @param [in,out] map Map to which the parameters are added + * @param [in] params Sliced vector with parameters to be registered (each slice corresponds to one bound state and + * contains all components) + * @param [in] unitOpIdx Index of the unit operation + * @tparam SliceContainer_t Type of the slice container, such as @c cadet::util::SlicedVector + */ +template +inline void registerComponentBoundStateDependentParam(const StringHash nameHash, + std::unordered_map& map, + SliceContainer_t& params, UnitOpIdx unitOpIdx, + ParticleTypeIdx parType) +{ + registerParam1DNonUniform(map, params, [=](bool multi, unsigned int state, unsigned int comp) { + return makeParamId(nameHash, unitOpIdx, comp, parType, state, ReactionIndep, SectionIndep); + }); +} - /** - * @brief Registers parameters that depend on component and bound state stored in component-major ordering - * @details The ordering is component-major, that is comp0state0, comp0state1, comp1state0, comp1state1, comp2state0, comp2state1, ... - * - * @param [in] nameHash Hashed name of the parameter - * @param [in,out] map Map to which the parameters are added - * @param [in] params Sliced vector with parameters to be registered (each slice corresponds to one component and contains all bound states) - * @param [in] unitOpIdx Index of the unit operation - * @tparam SliceContainer_t Type of the slice container, such as @c cadet::util::SlicedVector - */ - template - inline void registerComponentBoundStateDependentParamCompMajor(const StringHash nameHash, std::unordered_map& map, SliceContainer_t& params, UnitOpIdx unitOpIdx, ParticleTypeIdx parType) - { - registerParam1DNonUniform(map, params, [=](bool multi, unsigned int comp, unsigned int state) { return makeParamId(nameHash, unitOpIdx, comp, parType, state, ReactionIndep, SectionIndep); }); - } +/** + * @brief Registers parameters that depend on component and bound state stored in component-major ordering + * @details The ordering is component-major, that is comp0state0, comp0state1, comp1state0, comp1state1, comp2state0, + * comp2state1, ... + * + * @param [in] nameHash Hashed name of the parameter + * @param [in,out] map Map to which the parameters are added + * @param [in] params Sliced vector with parameters to be registered (each slice corresponds to one component and + * contains all bound states) + * @param [in] unitOpIdx Index of the unit operation + * @tparam SliceContainer_t Type of the slice container, such as @c cadet::util::SlicedVector + */ +template +inline void registerComponentBoundStateDependentParamCompMajor(const StringHash nameHash, + std::unordered_map& map, + SliceContainer_t& params, UnitOpIdx unitOpIdx, + ParticleTypeIdx parType) +{ + registerParam1DNonUniform(map, params, [=](bool multi, unsigned int comp, unsigned int state) { + return makeParamId(nameHash, unitOpIdx, comp, parType, state, ReactionIndep, SectionIndep); + }); +} } // namespace cadet -#endif // LIBCADET_PARAMREADERHELPER_HPP_ +#endif // LIBCADET_PARAMREADERHELPER_HPP_ diff --git a/src/libcadet/ParamReaderScopes.hpp b/src/libcadet/ParamReaderScopes.hpp index a741dc989..359439ca0 100644 --- a/src/libcadet/ParamReaderScopes.hpp +++ b/src/libcadet/ParamReaderScopes.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides tools for managing scopes of IParameterProvider */ @@ -27,81 +27,38 @@ namespace cadet { +/** + * @brief ParameterProvider scope guard for a multiplexed group + * @details Opens and closes the ParameterProvider scope. + */ +class MultiplexedScopeSelector +{ +public: /** - * @brief ParameterProvider scope guard for a multiplexed group - * @details Opens and closes the ParameterProvider scope. + * @brief Enters a multiplexed scope in the IParameterProvider + * @details If multiplexing is disabled, the scope @p baseName or @c baseName_000 is entered, if it exists. + * If the scope does not exist, but it is @p required, an exception is thrown. + * If multiplxing is enabled, the scope @c baseName_xxx is entered, where @c xxx is a given @p index. + * If this scope does not exist and @p fallbackSingle is enabled, we additionally check the scope @p + * baseName. If all this fails, an exception is thrown if the scope is @p required. + * + * On destruction of the object, the scope is popped back from the IParameterProvider (if it has been + * entered). + * @param [in,out] paramProvider IParameterProvider instance + * @param [in] baseName Base name of the group + * @param [in] single Determines whether a single scope is multiplexed on multiple items + * @param [in] index Index of the non-multiplexed scope + * @param [in] fallbackSingle Determines whether a fallback scope (@p baseName) is tried if the scope identified by + * @p index is not found + * @param [in] required Determines whether the scope is required */ - class MultiplexedScopeSelector + MultiplexedScopeSelector(IParameterProvider& paramProvider, const std::string& baseName, bool single, + unsigned int index, bool fallbackSingle, bool required) + : _pp(paramProvider) { - public: - - /** - * @brief Enters a multiplexed scope in the IParameterProvider - * @details If multiplexing is disabled, the scope @p baseName or @c baseName_000 is entered, if it exists. - * If the scope does not exist, but it is @p required, an exception is thrown. - * If multiplxing is enabled, the scope @c baseName_xxx is entered, where @c xxx is a given @p index. - * If this scope does not exist and @p fallbackSingle is enabled, we additionally check the scope @p baseName. - * If all this fails, an exception is thrown if the scope is @p required. - * - * On destruction of the object, the scope is popped back from the IParameterProvider (if it has been entered). - * @param [in,out] paramProvider IParameterProvider instance - * @param [in] baseName Base name of the group - * @param [in] single Determines whether a single scope is multiplexed on multiple items - * @param [in] index Index of the non-multiplexed scope - * @param [in] fallbackSingle Determines whether a fallback scope (@p baseName) is tried if the scope identified by @p index is not found - * @param [in] required Determines whether the scope is required - */ - MultiplexedScopeSelector(IParameterProvider& paramProvider, const std::string& baseName, bool single, unsigned int index, bool fallbackSingle, bool required) : _pp(paramProvider) - { - _active = true; - if (single) - { - if (paramProvider.exists(baseName)) - paramProvider.pushScope(baseName); - else if (paramProvider.exists(baseName + "_000")) - paramProvider.pushScope(baseName + "_000"); - else - { - _active = false; - if (required) - throw InvalidParameterException("Group \"" + baseName + "\" or \"" + baseName + "_000\" required"); - } - } - else - { - std::ostringstream oss; - oss << baseName + "_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << index; - - if (paramProvider.exists(oss.str())) - paramProvider.pushScope(oss.str()); - else - { - // If there is just one type, allow legacy "baseName" scope - if (paramProvider.exists(baseName) && fallbackSingle) - paramProvider.pushScope(baseName); - else - { - _active = false; - if (required) - throw InvalidParameterException("Group \"" + oss.str() + "\" or \"" + baseName + "\" required"); - } - } - } - } - - /** - * @brief Enters a multiplexed scope in the IParameterProvider - * @details The scope @p baseName or @c baseName_000 is entered, if it exists. - * If the scope does not exist, but it is @p required, an exception is thrown. - * - * On destruction of the object, the scope is popped back from the IParameterProvider (if it has been entered). - * @param [in,out] paramProvider IParameterProvider instance - * @param [in] baseName Base name of the group - * @param [in] required Determines whether the scope is required - */ - MultiplexedScopeSelector(IParameterProvider& paramProvider, const std::string& baseName, bool required) : _pp(paramProvider) + _active = true; + if (single) { - _active = true; if (paramProvider.exists(baseName)) paramProvider.pushScope(baseName); else if (paramProvider.exists(baseName + "_000")) @@ -113,23 +70,8 @@ namespace cadet throw InvalidParameterException("Group \"" + baseName + "\" or \"" + baseName + "_000\" required"); } } - - /** - * @brief Enters a multiplexed scope in the IParameterProvider - * @details The scope @c baseName_xxx is entered, where @c xxx is a given @p index. - * If this scope does not exist and @p fallbackSingle is enabled, we additionally check the scope @p baseName. - * If all this fails, an exception is thrown if the scope is @p required. - * - * On destruction of the object, the scope is popped back from the IParameterProvider (if it has been entered). - * @param [in,out] paramProvider IParameterProvider instance - * @param [in] baseName Base name of the group - * @param [in] index Index of the non-multiplexed scope - * @param [in] fallbackSingle Determines whether a fallback scope (@p baseName) is tried if the scope identified by @p index is not found - * @param [in] required Determines whether the scope is required - */ - MultiplexedScopeSelector(IParameterProvider& paramProvider, const std::string& baseName, unsigned int index, bool fallbackSingle, bool required) : _pp(paramProvider) + else { - _active = true; std::ostringstream oss; oss << baseName + "_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << index; @@ -137,7 +79,7 @@ namespace cadet paramProvider.pushScope(oss.str()); else { - // If there is just one type, allow legacy "baseName" scope + // If there is just one type, allow legacy "baseName" scope if (paramProvider.exists(baseName) && fallbackSingle) paramProvider.pushScope(baseName); else @@ -148,26 +90,96 @@ namespace cadet } } } + } + + /** + * @brief Enters a multiplexed scope in the IParameterProvider + * @details The scope @p baseName or @c baseName_000 is entered, if it exists. + * If the scope does not exist, but it is @p required, an exception is thrown. + * + * On destruction of the object, the scope is popped back from the IParameterProvider (if it has been + * entered). + * @param [in,out] paramProvider IParameterProvider instance + * @param [in] baseName Base name of the group + * @param [in] required Determines whether the scope is required + */ + MultiplexedScopeSelector(IParameterProvider& paramProvider, const std::string& baseName, bool required) + : _pp(paramProvider) + { + _active = true; + if (paramProvider.exists(baseName)) + paramProvider.pushScope(baseName); + else if (paramProvider.exists(baseName + "_000")) + paramProvider.pushScope(baseName + "_000"); + else + { + _active = false; + if (required) + throw InvalidParameterException("Group \"" + baseName + "\" or \"" + baseName + "_000\" required"); + } + } + + /** + * @brief Enters a multiplexed scope in the IParameterProvider + * @details The scope @c baseName_xxx is entered, where @c xxx is a given @p index. + * If this scope does not exist and @p fallbackSingle is enabled, we additionally check the scope @p + * baseName. If all this fails, an exception is thrown if the scope is @p required. + * + * On destruction of the object, the scope is popped back from the IParameterProvider (if it has been + * entered). + * @param [in,out] paramProvider IParameterProvider instance + * @param [in] baseName Base name of the group + * @param [in] index Index of the non-multiplexed scope + * @param [in] fallbackSingle Determines whether a fallback scope (@p baseName) is tried if the scope identified by + * @p index is not found + * @param [in] required Determines whether the scope is required + */ + MultiplexedScopeSelector(IParameterProvider& paramProvider, const std::string& baseName, unsigned int index, + bool fallbackSingle, bool required) + : _pp(paramProvider) + { + _active = true; + std::ostringstream oss; + oss << baseName + "_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << index; - ~MultiplexedScopeSelector() + if (paramProvider.exists(oss.str())) + paramProvider.pushScope(oss.str()); + else { - if (_active) + // If there is just one type, allow legacy "baseName" scope + if (paramProvider.exists(baseName) && fallbackSingle) + paramProvider.pushScope(baseName); + else { - _pp.popScope(); + _active = false; + if (required) + throw InvalidParameterException("Group \"" + oss.str() + "\" or \"" + baseName + "\" required"); } } + } - /** - * @brief Returns whether a scope has been entered - * @return @c true if a scope has been entered, otherwise @c false - */ - inline bool isActive() const CADET_NOEXCEPT { return _active; } + ~MultiplexedScopeSelector() + { + if (_active) + { + _pp.popScope(); + } + } + + /** + * @brief Returns whether a scope has been entered + * @return @c true if a scope has been entered, otherwise @c false + */ + inline bool isActive() const CADET_NOEXCEPT + { + return _active; + } - private: - bool _active; - IParameterProvider& _pp; - }; +private: + bool _active; + IParameterProvider& _pp; +}; } // namespace cadet -#endif // LIBCADET_PARAMREADERSCOPES_HPP_ +#endif // LIBCADET_PARAMREADERSCOPES_HPP_ diff --git a/src/libcadet/ParameterDependenceFactory.cpp b/src/libcadet/ParameterDependenceFactory.cpp index 37b433d8f..ea9b35f2b 100644 --- a/src/libcadet/ParameterDependenceFactory.cpp +++ b/src/libcadet/ParameterDependenceFactory.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -15,109 +15,118 @@ namespace cadet { - namespace model - { - namespace paramdep - { - void registerLiquidSaltSolidParamDependence(std::unordered_map>& paramDeps); - void registerDummyParamDependence(std::unordered_map>& paramDeps); - void registerDummyParamDependence(std::unordered_map>& paramDeps); - void registerIdentityParamDependence(std::unordered_map>& paramDeps); - void registerIdentityParamDependence(std::unordered_map>& paramDeps); - void registerPowerLawParamDependence(std::unordered_map>& paramDeps); - } - } - - ParameterDependenceFactory::ParameterDependenceFactory() - { - // Register all ParamState dependencies - model::paramdep::registerLiquidSaltSolidParamDependence(_paramStateDeps); - model::paramdep::registerDummyParamDependence(_paramStateDeps); - model::paramdep::registerIdentityParamDependence(_paramStateDeps); - - // Register all ParamParam dependencies - model::paramdep::registerDummyParamDependence(_paramParamDeps); - model::paramdep::registerIdentityParamDependence(_paramParamDeps); - model::paramdep::registerPowerLawParamDependence(_paramParamDeps); - } - - ParameterDependenceFactory::~ParameterDependenceFactory() { } +namespace model +{ +namespace paramdep +{ +void registerLiquidSaltSolidParamDependence( + std::unordered_map>& paramDeps); +void registerDummyParamDependence( + std::unordered_map>& paramDeps); +void registerDummyParamDependence( + std::unordered_map>& paramDeps); +void registerIdentityParamDependence( + std::unordered_map>& paramDeps); +void registerIdentityParamDependence( + std::unordered_map>& paramDeps); +void registerPowerLawParamDependence( + std::unordered_map>& paramDeps); +} // namespace paramdep +} // namespace model + +ParameterDependenceFactory::ParameterDependenceFactory() +{ + // Register all ParamState dependencies + model::paramdep::registerLiquidSaltSolidParamDependence(_paramStateDeps); + model::paramdep::registerDummyParamDependence(_paramStateDeps); + model::paramdep::registerIdentityParamDependence(_paramStateDeps); + + // Register all ParamParam dependencies + model::paramdep::registerDummyParamDependence(_paramParamDeps); + model::paramdep::registerIdentityParamDependence(_paramParamDeps); + model::paramdep::registerPowerLawParamDependence(_paramParamDeps); +} + +ParameterDependenceFactory::~ParameterDependenceFactory() +{ +} - template - void ParameterDependenceFactory::registerStateDependence(const std::string& name) - { - _paramStateDeps[name] = []() { return new ParamDep_t(); }; - } +template void ParameterDependenceFactory::registerStateDependence(const std::string& name) +{ + _paramStateDeps[name] = []() { return new ParamDep_t(); }; +} - template - void ParameterDependenceFactory::registerStateDependence() - { - registerStateDependence(ParamDep_t::identifier()); - } +template void ParameterDependenceFactory::registerStateDependence() +{ + registerStateDependence(ParamDep_t::identifier()); +} - template - void ParameterDependenceFactory::registerParameterDependence(const std::string& name) - { - _paramParamDeps[name] = []() { return new ParamDep_t(); }; - } +template void ParameterDependenceFactory::registerParameterDependence(const std::string& name) +{ + _paramParamDeps[name] = []() { return new ParamDep_t(); }; +} - template - void ParameterDependenceFactory::registerParameterDependence() - { - registerParameterDependence(ParamDep_t::identifier()); - } +template void ParameterDependenceFactory::registerParameterDependence() +{ + registerParameterDependence(ParamDep_t::identifier()); +} - model::IParameterStateDependence* ParameterDependenceFactory::createStateDependence(const std::string& name) const +model::IParameterStateDependence* ParameterDependenceFactory::createStateDependence(const std::string& name) const +{ + const auto it = _paramStateDeps.find(name); + if (it == _paramStateDeps.end()) { - const auto it = _paramStateDeps.find(name); - if (it == _paramStateDeps.end()) - { - // ParameterDependence was not found - return nullptr; - } - - // Call factory function (thanks to type erasure of std::function we can store - // all factory functions in one container) - return it->second(); + // ParameterDependence was not found + return nullptr; } - model::IParameterParameterDependence* ParameterDependenceFactory::createParameterDependence(const std::string& name) const - { - const auto it = _paramParamDeps.find(name); - if (it == _paramParamDeps.end()) - { - // ParameterDependence was not found - return nullptr; - } - - // Call factory function (thanks to type erasure of std::function we can store - // all factory functions in one container) - return it->second(); - } + // Call factory function (thanks to type erasure of std::function we can store + // all factory functions in one container) + return it->second(); +} - void ParameterDependenceFactory::registerModel(const std::string& name, std::function factory) +model::IParameterParameterDependence* ParameterDependenceFactory::createParameterDependence( + const std::string& name) const +{ + const auto it = _paramParamDeps.find(name); + if (it == _paramParamDeps.end()) { - if (_paramStateDeps.find(name) == _paramStateDeps.end()) - _paramStateDeps[name] = factory; - else - throw InvalidParameterException("IParameterStateDependence implementation with the name " + name + " is already registered and cannot be overwritten"); + // ParameterDependence was not found + return nullptr; } - void ParameterDependenceFactory::registerModel(const std::string& name, std::function factory) - { - if (_paramParamDeps.find(name) == _paramParamDeps.end()) - _paramParamDeps[name] = factory; - else - throw InvalidParameterException("IParameterParameterDependence implementation with the name " + name + " is already registered and cannot be overwritten"); - } + // Call factory function (thanks to type erasure of std::function we can store + // all factory functions in one container) + return it->second(); +} - bool ParameterDependenceFactory::stateDependenceExists(const std::string& name) const - { - return _paramStateDeps.find(name) != _paramStateDeps.end(); - } +void ParameterDependenceFactory::registerModel(const std::string& name, + std::function factory) +{ + if (_paramStateDeps.find(name) == _paramStateDeps.end()) + _paramStateDeps[name] = factory; + else + throw InvalidParameterException("IParameterStateDependence implementation with the name " + name + + " is already registered and cannot be overwritten"); +} + +void ParameterDependenceFactory::registerModel(const std::string& name, + std::function factory) +{ + if (_paramParamDeps.find(name) == _paramParamDeps.end()) + _paramParamDeps[name] = factory; + else + throw InvalidParameterException("IParameterParameterDependence implementation with the name " + name + + " is already registered and cannot be overwritten"); +} + +bool ParameterDependenceFactory::stateDependenceExists(const std::string& name) const +{ + return _paramStateDeps.find(name) != _paramStateDeps.end(); +} - bool ParameterDependenceFactory::parameterDependenceExists(const std::string& name) const - { - return _paramParamDeps.find(name) != _paramParamDeps.end(); - } +bool ParameterDependenceFactory::parameterDependenceExists(const std::string& name) const +{ + return _paramParamDeps.find(name) != _paramParamDeps.end(); +} } // namespace cadet diff --git a/src/libcadet/ParameterDependenceFactory.hpp b/src/libcadet/ParameterDependenceFactory.hpp index e42a3492e..e2a1b2db7 100644 --- a/src/libcadet/ParameterDependenceFactory.hpp +++ b/src/libcadet/ParameterDependenceFactory.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the ParameterDependenceFactory */ @@ -25,105 +25,105 @@ namespace cadet { - namespace model - { - class IParameterStateDependence; - class IParameterParameterDependence; - } +namespace model +{ +class IParameterStateDependence; +class IParameterParameterDependence; +} // namespace model + +/** + * @brief Creates parameter dependency objects + */ +class ParameterDependenceFactory +{ +public: + /** + * @brief Construct the ParameterDependenceFactory + * @details All internal parameter dependencies are registered here. + */ + ParameterDependenceFactory(); + + ~ParameterDependenceFactory(); + + /** + * @brief Creates parameter dependencies with the given @p name + * @param [in] name Name of the parameter dependence + * @return The parameter dependence or @c NULL if a parameter dependence with this name does not exist + */ + model::IParameterStateDependence* createStateDependence(const std::string& name) const; + + /** + * @brief Creates parameter dependencies with the given @p name + * @param [in] name Name of the parameter dependence + * @return The parameter dependence or @c NULL if a parameter dependence with this name does not exist + */ + model::IParameterParameterDependence* createParameterDependence(const std::string& name) const; + + /** + * @brief Registers the given parameter dependence implementation + * @param [in] name Name of the IParameterStateDependence implementation + * @param [in] factory Function that creates an object of the IParameterStateDependence class + */ + void registerModel(const std::string& name, std::function factory); + + /** + * @brief Registers the given parameter dependence implementation + * @param [in] name Name of the IParameterParameterDependence implementation + * @param [in] factory Function that creates an object of the IParameterParameterDependence class + */ + void registerModel(const std::string& name, std::function factory); + + /** + * @brief Returns whether a parameter dependence of the given name @p name exists + * @param [in] name Name of the parameter dependence + * @return @c true if a parameter dependence of this name exists, otherwise @c false + */ + bool stateDependenceExists(const std::string& name) const; + + /** + * @brief Returns whether a parameter dependence of the given name @p name exists + * @param [in] name Name of the parameter dependence + * @return @c true if a parameter dependence of this name exists, otherwise @c false + */ + bool parameterDependenceExists(const std::string& name) const; + +protected: + /** + * @brief Registers an IParameterStateDependence + * @param [in] name Name of the parameter dependence + * @tparam ParamDep_t Type of the parameter dependence + */ + template void registerStateDependence(const std::string& name); + + /** + * @brief Registers an IParameterStateDependence + * @details The name of the parameter dependence is inferred from the static function + * IParameterStateDependence::identifier(). + * @tparam ParamDep_t Type of the parameter dependence + */ + template void registerStateDependence(); /** - * @brief Creates parameter dependency objects + * @brief Registers an IParameterParameterDependence + * @param [in] name Name of the parameter dependence + * @tparam ParamDep_t Type of the parameter dependence */ - class ParameterDependenceFactory - { - public: - /** - * @brief Construct the ParameterDependenceFactory - * @details All internal parameter dependencies are registered here. - */ - ParameterDependenceFactory(); - - ~ParameterDependenceFactory(); - - /** - * @brief Creates parameter dependencies with the given @p name - * @param [in] name Name of the parameter dependence - * @return The parameter dependence or @c NULL if a parameter dependence with this name does not exist - */ - model::IParameterStateDependence* createStateDependence(const std::string& name) const; - - /** - * @brief Creates parameter dependencies with the given @p name - * @param [in] name Name of the parameter dependence - * @return The parameter dependence or @c NULL if a parameter dependence with this name does not exist - */ - model::IParameterParameterDependence* createParameterDependence(const std::string& name) const; - - /** - * @brief Registers the given parameter dependence implementation - * @param [in] name Name of the IParameterStateDependence implementation - * @param [in] factory Function that creates an object of the IParameterStateDependence class - */ - void registerModel(const std::string& name, std::function factory); - - /** - * @brief Registers the given parameter dependence implementation - * @param [in] name Name of the IParameterParameterDependence implementation - * @param [in] factory Function that creates an object of the IParameterParameterDependence class - */ - void registerModel(const std::string& name, std::function factory); - - /** - * @brief Returns whether a parameter dependence of the given name @p name exists - * @param [in] name Name of the parameter dependence - * @return @c true if a parameter dependence of this name exists, otherwise @c false - */ - bool stateDependenceExists(const std::string& name) const; - - /** - * @brief Returns whether a parameter dependence of the given name @p name exists - * @param [in] name Name of the parameter dependence - * @return @c true if a parameter dependence of this name exists, otherwise @c false - */ - bool parameterDependenceExists(const std::string& name) const; - protected: - - /** - * @brief Registers an IParameterStateDependence - * @param [in] name Name of the parameter dependence - * @tparam ParamDep_t Type of the parameter dependence - */ - template - void registerStateDependence(const std::string& name); - - /** - * @brief Registers an IParameterStateDependence - * @details The name of the parameter dependence is inferred from the static function IParameterStateDependence::identifier(). - * @tparam ParamDep_t Type of the parameter dependence - */ - template - void registerStateDependence(); - - /** - * @brief Registers an IParameterParameterDependence - * @param [in] name Name of the parameter dependence - * @tparam ParamDep_t Type of the parameter dependence - */ - template - void registerParameterDependence(const std::string& name); - - /** - * @brief Registers an IParameterParameterDependence - * @details The name of the parameter dependence is inferred from the static function IParameterParameterDependence::identifier(). - * @tparam ParamDep_t Type of the parameter dependence - */ - template - void registerParameterDependence(); - - std::unordered_map> _paramStateDeps; //!< Map with factory functions - std::unordered_map> _paramParamDeps; //!< Map with factory functions - }; + template void registerParameterDependence(const std::string& name); + + /** + * @brief Registers an IParameterParameterDependence + * @details The name of the parameter dependence is inferred from the static function + * IParameterParameterDependence::identifier(). + * @tparam ParamDep_t Type of the parameter dependence + */ + template void registerParameterDependence(); + + std::unordered_map> + _paramStateDeps; //!< Map with factory functions + std::unordered_map> + _paramParamDeps; //!< Map with factory functions +}; } // namespace cadet -#endif // LIBCADET_PARAMETERDEPENDENCYFACTORY_HPP_ +#endif // LIBCADET_PARAMETERDEPENDENCYFACTORY_HPP_ diff --git a/src/libcadet/ReactionModelFactory.cpp b/src/libcadet/ReactionModelFactory.cpp index 4c86ec63b..66808674e 100644 --- a/src/libcadet/ReactionModelFactory.cpp +++ b/src/libcadet/ReactionModelFactory.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -15,65 +15,68 @@ namespace cadet { - namespace model - { - namespace reaction - { - void registerMassActionLawReaction(std::unordered_map>& reactions); - void registerDummyReaction(std::unordered_map>& reactions); - void registerMichaelisMentenReaction(std::unordered_map>& reactions); +namespace model +{ +namespace reaction +{ +void registerMassActionLawReaction( + std::unordered_map>& reactions); +void registerDummyReaction(std::unordered_map>& reactions); +void registerMichaelisMentenReaction( + std::unordered_map>& reactions); - } - } +} // namespace reaction +} // namespace model - ReactionModelFactory::ReactionModelFactory() - { - // Register all reaction models here - model::reaction::registerDummyReaction(_dynamicModels); - model::reaction::registerMassActionLawReaction(_dynamicModels); - model::reaction::registerMichaelisMentenReaction(_dynamicModels); +ReactionModelFactory::ReactionModelFactory() +{ + // Register all reaction models here + model::reaction::registerDummyReaction(_dynamicModels); + model::reaction::registerMassActionLawReaction(_dynamicModels); + model::reaction::registerMichaelisMentenReaction(_dynamicModels); +} - } +ReactionModelFactory::~ReactionModelFactory() +{ +} - ReactionModelFactory::~ReactionModelFactory() { } +template void ReactionModelFactory::registerDynamicModel(const std::string& name) +{ + _dynamicModels[name] = []() { return new ReactionModel_t(); }; +} - template - void ReactionModelFactory::registerDynamicModel(const std::string& name) - { - _dynamicModels[name] = []() { return new ReactionModel_t(); }; - } +template void ReactionModelFactory::registerDynamicModel() +{ + registerDynamicModel(ReactionModel_t::identifier()); +} - template - void ReactionModelFactory::registerDynamicModel() +model::IDynamicReactionModel* ReactionModelFactory::createDynamic(const std::string& name) const +{ + const auto it = _dynamicModels.find(name); + if (it == _dynamicModels.end()) { - registerDynamicModel(ReactionModel_t::identifier()); + // Reaction model was not found + return nullptr; } - model::IDynamicReactionModel* ReactionModelFactory::createDynamic(const std::string& name) const - { - const auto it = _dynamicModels.find(name); - if (it == _dynamicModels.end()) - { - // Reaction model was not found - return nullptr; - } - - // Call factory function (thanks to type erasure of std::function we can store - // all factory functions in one container) - return it->second(); - } + // Call factory function (thanks to type erasure of std::function we can store + // all factory functions in one container) + return it->second(); +} - void ReactionModelFactory::registerModel(const std::string& name, std::function factory) - { - if (_dynamicModels.find(name) == _dynamicModels.end()) - _dynamicModels[name] = factory; - else - throw InvalidParameterException("IDynamicReactionModel implementation with the name " + name + " is already registered and cannot be overwritten"); - } +void ReactionModelFactory::registerModel(const std::string& name, + std::function factory) +{ + if (_dynamicModels.find(name) == _dynamicModels.end()) + _dynamicModels[name] = factory; + else + throw InvalidParameterException("IDynamicReactionModel implementation with the name " + name + + " is already registered and cannot be overwritten"); +} - bool ReactionModelFactory::existsDynamic(const std::string& name) const - { - return _dynamicModels.find(name) != _dynamicModels.end(); - } +bool ReactionModelFactory::existsDynamic(const std::string& name) const +{ + return _dynamicModels.find(name) != _dynamicModels.end(); +} } // namespace cadet diff --git a/src/libcadet/ReactionModelFactory.hpp b/src/libcadet/ReactionModelFactory.hpp index 936d5e0ee..cc554f254 100644 --- a/src/libcadet/ReactionModelFactory.hpp +++ b/src/libcadet/ReactionModelFactory.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the ReactionModelFactory */ @@ -25,66 +25,65 @@ namespace cadet { - namespace model - { - class IDynamicReactionModel; - } +namespace model +{ +class IDynamicReactionModel; +} +/** + * @brief Creates reaction models + */ +class ReactionModelFactory +{ +public: /** - * @brief Creates reaction models + * @brief Construct the ReactionModelFactory + * @details All internal reaction models are registered here. */ - class ReactionModelFactory - { - public: - /** - * @brief Construct the ReactionModelFactory - * @details All internal reaction models are registered here. - */ - ReactionModelFactory(); + ReactionModelFactory(); - ~ReactionModelFactory(); + ~ReactionModelFactory(); - /** - * @brief Creates dynamic reaction models with the given @p name - * @param [in] name Name of the dynamic reaction model - * @return The dynamic reaction model or @c NULL if a dynamic reaction model with this name does not exist - */ - model::IDynamicReactionModel* createDynamic(const std::string& name) const; + /** + * @brief Creates dynamic reaction models with the given @p name + * @param [in] name Name of the dynamic reaction model + * @return The dynamic reaction model or @c NULL if a dynamic reaction model with this name does not exist + */ + model::IDynamicReactionModel* createDynamic(const std::string& name) const; - /** - * @brief Registers the given dynamic reaction model implementation - * @param [in] name Name of the IBindingModel implementation - * @param [in] factory Function that creates an object of the IBindingModel class - */ - void registerModel(const std::string& name, std::function factory); + /** + * @brief Registers the given dynamic reaction model implementation + * @param [in] name Name of the IBindingModel implementation + * @param [in] factory Function that creates an object of the IBindingModel class + */ + void registerModel(const std::string& name, std::function factory); - /** - * @brief Returns whether a dynamic reaction model of the given name @p name exists - * @param [in] name Name of the dynamic reaction model - * @return @c true if a dynamic reaction model of this name exists, otherwise @c false - */ - bool existsDynamic(const std::string& name) const; - protected: + /** + * @brief Returns whether a dynamic reaction model of the given name @p name exists + * @param [in] name Name of the dynamic reaction model + * @return @c true if a dynamic reaction model of this name exists, otherwise @c false + */ + bool existsDynamic(const std::string& name) const; - /** - * @brief Registers an IDynamicReactionModel - * @param [in] name Name of the binding model - * @tparam ReactionModel_t Type of the binding model - */ - template - void registerDynamicModel(const std::string& name); +protected: + /** + * @brief Registers an IDynamicReactionModel + * @param [in] name Name of the binding model + * @tparam ReactionModel_t Type of the binding model + */ + template void registerDynamicModel(const std::string& name); - /** - * @brief Registers an IDynamicReactionModel - * @details The name of the binding model is inferred from the static function IDynamicReactionModel::identifier(). - * @tparam ReactionModel_t Type of the binding model - */ - template - void registerDynamicModel(); + /** + * @brief Registers an IDynamicReactionModel + * @details The name of the binding model is inferred from the static function IDynamicReactionModel::identifier(). + * @tparam ReactionModel_t Type of the binding model + */ + template void registerDynamicModel(); - std::unordered_map> _dynamicModels; //!< Map with factory functions - }; + std::unordered_map> + _dynamicModels; //!< Map with factory functions +}; } // namespace cadet -#endif // LIBCADET_REACTIONMODELFACTORY_HPP_ +#endif // LIBCADET_REACTIONMODELFACTORY_HPP_ diff --git a/src/libcadet/SensParamUtil.hpp b/src/libcadet/SensParamUtil.hpp index e2fbf46bd..cf863a548 100644 --- a/src/libcadet/SensParamUtil.hpp +++ b/src/libcadet/SensParamUtil.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,8 +11,8 @@ // ============================================================================= /** - * @file - * Provides helper functions for entities that can contain sensitive parameters. + * @file + * Provides helper functions for entities that can contain sensitive parameters. */ #ifndef LIBCADET_SENSPARAMUTIL_HPP_ @@ -23,18 +23,16 @@ namespace cadet { - template - inline bool contains(const typename std::vector& vec, const Elem_t& item) - { - const typename std::vector::const_iterator it = std::find(vec.begin(), vec.end(), item); - return it != vec.end(); - } +template inline bool contains(const typename std::vector& vec, const Elem_t& item) +{ + const typename std::vector::const_iterator it = std::find(vec.begin(), vec.end(), item); + return it != vec.end(); +} - template - inline bool contains(const typename std::unordered_set& set, const Elem_t& item) - { - return set.find(item) != set.end(); - } +template inline bool contains(const typename std::unordered_set& set, const Elem_t& item) +{ + return set.find(item) != set.end(); +} } // namespace cadet -#endif // LIBCADET_SENSPARAMUTIL_HPP_ +#endif // LIBCADET_SENSPARAMUTIL_HPP_ diff --git a/src/libcadet/SimulatableModel.hpp b/src/libcadet/SimulatableModel.hpp index f6cefc3bb..568b137d9 100644 --- a/src/libcadet/SimulatableModel.hpp +++ b/src/libcadet/SimulatableModel.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines an internal interface for models that can be simulated. */ @@ -43,8 +43,9 @@ struct ConstSimulationState; class ISimulatableModel : public IModelSystem { public: - - virtual ~ISimulatableModel() CADET_NOEXCEPT { } + virtual ~ISimulatableModel() CADET_NOEXCEPT + { + } /** * @brief Return the number of required DOFs @@ -77,16 +78,17 @@ class ISimulatableModel : public IModelSystem virtual unsigned int requiredADdirs() const CADET_NOEXCEPT = 0; /** - * @brief Configures the model discretization by extracting all structural parameters from the given @p paramProvider + * @brief Configures the model discretization by extracting all structural parameters from the given @p + * paramProvider * @details The scope of the cadet::IParameterProvider is left unchanged on return. - * + * * Only the structure / discretization (e.g., number of components, adsorption * model, bound states) is configured. The actual model parameters are configured - * in configure(). - * + * in configure(). + * * This function can only be called once. Once the discretization is set, it cannot * be changed. - * + * * @param [in] paramProvider Parameter provider * @param [in] helper Used to inject or create required objects * @return @c true if the configuration was successful, otherwise @c false @@ -94,26 +96,28 @@ class ISimulatableModel : public IModelSystem virtual bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper) = 0; /** - * @brief (Re-)configures the model by extracting all non-structural parameters (e.g., model parameters) from the given @p paramProvider + * @brief (Re-)configures the model by extracting all non-structural parameters (e.g., model parameters) from the + * given @p paramProvider * @details The scope of the cadet::IParameterProvider is left unchanged on return. - * + * * The structure of the model is left unchanged, that is, the number of degrees of * freedom stays the same. Only true (non-structural) model parameters are read and * changed. Parameters that concern discretization (e.g., number of cells), model * structure (e.g., number of components, binding model), and numerical solution * (e.g., tolerances in GMRES iterations) are left untouched. - * + * * This function may only be called if configureModelDiscretization() has been called * in the past. Contrary to configureModelDiscretization(), it can be called multiple * times. - * + * * @param [in] paramProvider Parameter provider * @return @c true if the configuration was successful, otherwise @c false */ virtual bool configure(IParameterProvider& paramProvider) = 0; /** - * @brief (Re-)configures a unit operation model by extracting its non-structural parameters (e.g., model parameters) from the given @p paramProvider + * @brief (Re-)configures a unit operation model by extracting its non-structural parameters (e.g., model + * parameters) from the given @p paramProvider * @details The scope of the cadet::IParameterProvider is left unchanged on return. * @param [in] paramProvider Parameter provider * @param [in] unitOpIdx ID of the unit operation model (creation order) @@ -148,7 +152,7 @@ class ISimulatableModel : public IModelSystem * @details This also sets values of parameters that are not marked as sensitive at the moment. * If the parameter is part of a fused sensitivity, then all fused parameters are set * to the same value by calling this function for each parameter. - * + * * Note that the AD directions are kept invariant (i.e., they are not resetted). * @param [in] id Parameter ID of the parameter to be manipulated * @param [in] value Value of the parameter @@ -165,19 +169,21 @@ class ISimulatableModel : public IModelSystem * @details This function is called after time integration of a section has finished and a new * section is about to be integrated. This allows the model to update internal state before * consistent initialization is performed. - * + * * This function is also called at the beginning of the time integration, which allows * the model to perform setup operations. - * + * * If AD is used by the model, the function has the opportunity to update the seed vectors. * The general initialization of the seed vectors is performed by prepareADvectors(). - * + * * @param [in] t Current time point * @param [in] secIdx Index of the new section that is about to be integrated * @param [in] simState State of the simulation * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) */ - virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) = 0; + virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac) = 0; /** * @brief Applies initial conditions to the state vector and its time derivative @@ -188,15 +194,16 @@ class ISimulatableModel : public IModelSystem * the initial conditions set by this function will be corrected for consistency. * Note that the state vector and its time derivative are pre-initialized with zero by the * time integrator. - * - * @param [in,out] simState State of the simulation (state vector and its time derivatives) to be updated with initial values + * + * @param [in,out] simState State of the simulation (state vector and its time derivatives) to be updated with + * initial values */ virtual void applyInitialCondition(const SimulationState& simState) const = 0; /** * @brief Reads initial conditions for the state vector and its time derivative from the given parameter provider * @details The scope of the cadet::IParameterProvider is left unchanged on return. - * + * * @param [in] paramProvider Parameter provider */ virtual void readInitialCondition(IParameterProvider& paramProvider) = 0; @@ -204,14 +211,14 @@ class ISimulatableModel : public IModelSystem /** * @brief Sets initial sensitivity system state * @details Given the DAE \f[ F(t, y, \dot{y}, p) = 0, \f] the corresponding (linear) forward sensitivity - * system reads \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial value of \f$ s_0 = \frac{\mathrm{d} y_0}{\mathrm{d}p} \f$ is set by this function. - * Note that consistent initialization is performed later, which in particular calculates a matching - * \f$ \dot{s}_0 = \frac{\mathrm{d} \dot{y}_0}{\mathrm{d}p}. \f$ - * + * system reads \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, + * y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial value of \f$ s_0 = + * \frac{\mathrm{d} y_0}{\mathrm{d}p} \f$ is set by this function. Note that consistent initialization is performed + * later, which in particular calculates a matching \f$ \dot{s}_0 = \frac{\mathrm{d} \dot{y}_0}{\mathrm{d}p}. \f$ + * * The sensitivity state vectors passed to this function are initialized to @c 0. They only have * to change in case of sensitivities with respect to initial conditions. - * + * * @param [out] vecSensY Sensitivity subsystem state vectors */ virtual void initializeSensitivityStates(const std::vector& vecSensY) const = 0; @@ -221,74 +228,84 @@ class ISimulatableModel : public IModelSystem * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time * derivative \f$ \dot{y}_0 \f$ such that they are consistent. - * + * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) - * @param [in,out] simState State of the simulation (state vector and its time derivatives) with initial values that are to be updated for consistency + * @param [in,out] simState State of the simulation (state vector and its time derivatives) with initial values that + * are to be updated for consistency * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) * @param [in] errorTol Error tolerance for algebraic equations */ - virtual void consistentInitialConditions(const SimulationTime& simTime, const SimulationState& simState, const AdJacobianParams& adJac, double errorTol) = 0; + virtual void consistentInitialConditions(const SimulationTime& simTime, const SimulationState& simState, + const AdJacobianParams& adJac, double errorTol) = 0; /** * @brief Computes consistent initial conditions for all sensitivity subsystems * @details Given the DAE \f[ F(t, y, \dot{y}, p) = 0, \f] the corresponding (linear) forward sensitivity - * system reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of \f$ s_0 = \frac{\mathrm{d} y_0}{\mathrm{d}p} \f$ and \f$ \dot{s}_0 = \frac{\mathrm{d} \dot{y}_0}{\mathrm{d}p} \f$ - * have to be consistent, that means, they have to satisfy the sensitivity equation. This function computes the correct \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ - * given \f$ y_0 \f$ and \f$ s_0 \f$. - * + * system reads + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of \f$ s_0 = \frac{\mathrm{d} + * y_0}{\mathrm{d}p} \f$ and \f$ \dot{s}_0 = \frac{\mathrm{d} \dot{y}_0}{\mathrm{d}p} \f$ have to be consistent, + * that means, they have to satisfy the sensitivity equation. This function computes the correct \f$ s_0 \f$ and \f$ + * \dot{s}_0 \f$ given \f$ y_0 \f$ and \f$ s_0 \f$. + * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] simState State of the simulation (state vector and its time derivative) * @param [in,out] vecSensY Sensitivity subsystem state vectors * @param [in,out] vecSensYdot Time derivative state vectors of the sensitivity subsystems to be initialized * @param [in,out] adRes Pointer to global residual vector of AD datatypes for computing the parameter sensitivities - * @param [in,out] adY Pointer to global state vector of AD datatypes that can be used for computing the Jacobian (or @c nullptr if AD is disabled) + * @param [in,out] adY Pointer to global state vector of AD datatypes that can be used for computing the Jacobian + * (or @c nullptr if AD is disabled) */ virtual void consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active* const adRes, active* const adY) = 0; + std::vector& vecSensY, std::vector& vecSensYdot, + active* const adRes, active* const adY) = 0; /** * @brief Computes approximately / partially consistent initial values and time derivatives * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have * to be consistent. This functions updates parts of the initial state \f$ y_0 \f$ and overwrites parts of * the time derivative \f$ \dot{y}_0 \f$ such that they are approximately consistent. - * + * * This function is possibly faster than consistentInitialConditions(), but updates only a part of the - * state and time derivative vector. Hence, the result is not guaranteed to be consistent. - * + * state and time derivative vector. Hence, the result is not guaranteed to be consistent. + * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) - * @param [in,out] simState State of the simulation (state vector and its time derivatives) with initial values that are to be updated for consistency + * @param [in,out] simState State of the simulation (state vector and its time derivatives) with initial values that + * are to be updated for consistency * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) * @param [in] errorTol Error tolerance for algebraic equations */ - virtual void leanConsistentInitialConditions(const SimulationTime& simTime, const SimulationState& simState, const AdJacobianParams& adJac, double errorTol) = 0; + virtual void leanConsistentInitialConditions(const SimulationTime& simTime, const SimulationState& simState, + const AdJacobianParams& adJac, double errorTol) = 0; /** * @brief Computes approximately / partially consistent initial conditions for all sensitivity subsystems * @details Given the DAE \f[ F(t, y, \dot{y}, p) = 0, \f] the corresponding (linear) forward sensitivity - * system reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of \f$ s_0 = \frac{\mathrm{d} y_0}{\mathrm{d}p} \f$ and \f$ \dot{s}_0 = \frac{\mathrm{d} \dot{y}_0}{\mathrm{d}p} \f$ - * have to be consistent, that means, they have to satisfy the sensitivity equation. This function computes the correct \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ - * given \f$ y_0 \f$ and \f$ s_0 \f$. - * + * system reads + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of \f$ s_0 = \frac{\mathrm{d} + * y_0}{\mathrm{d}p} \f$ and \f$ \dot{s}_0 = \frac{\mathrm{d} \dot{y}_0}{\mathrm{d}p} \f$ have to be consistent, + * that means, they have to satisfy the sensitivity equation. This function computes the correct \f$ s_0 \f$ and \f$ + * \dot{s}_0 \f$ given \f$ y_0 \f$ and \f$ s_0 \f$. + * * This function is possibly faster than consistentInitialSensitivity(), but updates only a part of the - * vectors. Hence, the result is not guaranteed to be consistent. - * + * vectors. Hence, the result is not guaranteed to be consistent. + * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] simState State of the simulation (state vector and its time derivative) * @param [in,out] vecSensY Sensitivity subsystem state vectors * @param [in,out] vecSensYdot Time derivative state vectors of the sensitivity subsystems to be initialized * @param [in,out] adRes Pointer to global residual vector of AD datatypes for computing the parameter sensitivities - * @param [in,out] adY Pointer to global state vector of AD datatypes that can be used for computing the Jacobian (or @c nullptr if AD is disabled) + * @param [in,out] adY Pointer to global state vector of AD datatypes that can be used for computing the Jacobian + * (or @c nullptr if AD is disabled) */ virtual void leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active* const adRes, active* const adY) = 0; + std::vector& vecSensY, std::vector& vecSensYdot, + active* const adRes, active* const adY) = 0; /** * @brief Computes the residual - * + * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] simState State of the simulation (state vector and its time derivative) * @param [out] res Pointer to global residual vector @@ -305,22 +322,24 @@ class ISimulatableModel : public IModelSystem * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ - virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac) = 0; + virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac) = 0; /** * @brief Computes the residual and updates the Jacobian - * + * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] simState State of the simulation (state vector and its time derivative) * @param [out] res Pointer to global residual vector * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ - virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac) = 0; + virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac) = 0; /** * @brief Computes the @f$ \ell^\infty@f$-norm of the residual vector - * + * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] simState State of the simulation (state vector and its time derivative) * @return the @f$ \ell^\infty@f$-norm of the residual vector @@ -329,7 +348,7 @@ class ISimulatableModel : public IModelSystem /** * @brief Computes the residual of the forward sensitivity systems - * + * * @param [in] nSens Number of sensitivity subsystems * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] simState State of the simulation (state vector and its time derivative) @@ -343,14 +362,14 @@ class ISimulatableModel : public IModelSystem * @param [in] tmp3 Temporary storage in the size of global state vector of @p y * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ - virtual int residualSensFwd(unsigned int nSens, const SimulationTime& simTime, - const ConstSimulationState& simState, double const* const res, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, - active* const adRes, double* const tmp1, double* const tmp2, double* const tmp3) = 0; + virtual int residualSensFwd(unsigned int nSens, const SimulationTime& simTime, const ConstSimulationState& simState, + double const* const res, const std::vector& yS, + const std::vector& ySdot, const std::vector& resS, + active* const adRes, double* const tmp1, double* const tmp2, double* const tmp3) = 0; /** * @brief Computes the residual of the forward sensitivity systems and evaluates the Jacobian - * + * * @param [in] nSens Number of sensitivity subsystems * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] simState State of the simulation (state vector and its time derivative) @@ -365,13 +384,15 @@ class ISimulatableModel : public IModelSystem * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ virtual int residualSensFwdWithJacobian(unsigned int nSens, const SimulationTime& simTime, - const ConstSimulationState& simState, double const* const res, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, - const AdJacobianParams& adJac, double* const tmp1, double* const tmp2, double* const tmp3) = 0; + const ConstSimulationState& simState, double const* const res, + const std::vector& yS, + const std::vector& ySdot, const std::vector& resS, + const AdJacobianParams& adJac, double* const tmp1, double* const tmp2, + double* const tmp3) = 0; /** * @brief Computes the @f$ \ell^\infty@f$-norms of the forward sensitivity residual vectors - * + * * @param [in] nSens Number of sensitivity subsystems * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] simState State of the simulation (state vector and its time derivative) @@ -381,18 +402,19 @@ class ISimulatableModel : public IModelSystem * @param [in,out] adRes Pointer to global residual vector of AD datatypes for computing the sensitivity derivatives * @param [in] tmp Temporary storage in the size of global state vector @p y */ - virtual void residualSensFwdNorm(unsigned int nSens, const SimulationTime& simTime, - const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, double* const norms, - active* const adRes, double* const tmp) = 0; + virtual void residualSensFwdNorm(unsigned int nSens, const SimulationTime& simTime, + const ConstSimulationState& simState, const std::vector& yS, + const std::vector& ySdot, double* const norms, active* const adRes, + double* const tmp) = 0; /** * @brief Computes the solution of the linear system involving the system Jacobian - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the - * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) \f$, - * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p rhs. - * + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) + * x = b \f] has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the + * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, + * \dot{y}) \f$, may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned + * in @p rhs. + * * Prior to calling linearSolve() the time integrator calls assembleDAEJacobian() with the same point * in time and state \f$(t, y, \dot{y})\f$. * @@ -405,19 +427,19 @@ class ISimulatableModel : public IModelSystem * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) = 0; + const ConstSimulationState& simState) = 0; /** * @brief Prepares the AD system vectors by constructing seed vectors * @details Sets the seed vectors used in AD. Since the AD vector is fully managed by the model, * the seeds are unchanged during one time integration (except for possible changes in * notifyDiscontinuousSectionTransition()). This function is called at the beginning of - * every time integration and should initialize the AD seed vectors. - * + * every time integration and should initialize the AD seed vectors. + * * If those vectors do not change during one time integration, their initialization should * be performed in this function. The notifyDiscontinuousSectionTransition() function is * only used to update seed vectors during time integration. - * + * * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) */ virtual void prepareADvectors(const AdJacobianParams& adJac) const = 0; @@ -429,7 +451,7 @@ class ISimulatableModel : public IModelSystem * provide means to implement discontinuous behavior (e.g., pulse injection profiles, * switching of valves). After initialization, the simulator notifies all entities * such as models or data sources of its section times. - * + * * The vector of section times consists of strictly increasing time points * @f[ t_0 < t_1 < t_2 < \dots t_N @f] * which mark the beginning and end of a section. The @f$ i@f$-th section is given by @@ -437,10 +459,10 @@ class ISimulatableModel : public IModelSystem * If a transition from one section to the next is continuous, the @p secContinuity flag * for that transition is @c true. In this case, the time integrator will not stop at the * transition time point and reinitialize consistently (which will be done for discontinuous - * transitions). - * + * transitions). + * * @param [in] secTimes Vector with section time points (length is @p nSections + 1) - * @param [in] secContinuity Vector of flags that indicate a continuous (@c true) or discontinuous (@c false) + * @param [in] secContinuity Vector of flags that indicate a continuous (@c true) or discontinuous (@c false) * transition from the current section to the next one (length is @p nSections - 1). For instance, * the first element indicates whether the transition from section @c 0 to @c 1 is continuous. * @param [in] nSections Number of sections @@ -453,7 +475,7 @@ class ISimulatableModel : public IModelSystem * are error tolerances for each component in each phase (independent of the specific * discretization of the model). The short error spec is expanded into a full one which * contains an error tolerance for each (pure) DOF of the model. - * + * * @param [in] errorSpec Pointer to first element of an array containing the short error spec * @param [in] errorSpecSize Size of the short error tolerance spec * @param [out] expandOut Pointer to the first element of an array receiving the full error specification @@ -466,12 +488,13 @@ class ISimulatableModel : public IModelSystem * These additional DOFs don't get an error tolerance from the user because he shouldn't be * aware of those (implementation detail). This function is responsible for calculating error * tolerances for these additional coupling DOFs. - * + * * @param [in] errorTol Pointer to array of error tolerances for system without coupling DOFs * @param [in] errorTolLength Length of @p errorTol array * @return Vector with error tolerances for additional coupling DOFs */ - virtual std::vector calculateErrorTolsForAdditionalDofs(double const* errorTol, unsigned int errorTolLength) = 0; + virtual std::vector calculateErrorTolsForAdditionalDofs(double const* errorTol, + unsigned int errorTolLength) = 0; /** * @brief Performs setup of parallelization for the given number of threads @@ -485,4 +508,4 @@ class ISimulatableModel : public IModelSystem } // namespace cadet -#endif // LIBCADET_SIMULATABLEMODEL_HPP_ +#endif // LIBCADET_SIMULATABLEMODEL_HPP_ diff --git a/src/libcadet/SimulationTypes.hpp b/src/libcadet/SimulationTypes.hpp index f5b68488f..e140c5073 100644 --- a/src/libcadet/SimulationTypes.hpp +++ b/src/libcadet/SimulationTypes.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines useful types for the simulation. */ @@ -28,8 +28,8 @@ namespace cadet */ struct ColumnPosition { - double axial; //!< Axial bulk coordinate z - double radial; //!< Radial bulk coordinate rho + double axial; //!< Axial bulk coordinate z + double radial; //!< Radial bulk coordinate rho double particle; //!< Radial particle coordinate r }; @@ -38,8 +38,8 @@ struct ColumnPosition */ struct AdJacobianParams { - active* adRes; //!< Residual vector - active* adY; //!< State vector + active* adRes; //!< Residual vector + active* adY; //!< State vector unsigned int adDirOffset; //!< Number of reserved AD directions (e.g., for sensitivities) }; @@ -48,7 +48,7 @@ struct AdJacobianParams */ struct SimulationTime { - double t; //!< Time of the simulation + double t; //!< Time of the simulation unsigned int secIdx; //!< Index of the current section }; @@ -57,7 +57,7 @@ struct SimulationTime */ struct SimulationState { - double* vecStateY; //!< State vector + double* vecStateY; //!< State vector double* vecStateYdot; //!< Time derivative of state vector }; @@ -66,7 +66,7 @@ struct SimulationState */ struct ConstSimulationState { - double const* vecStateY; //!< State vector + double const* vecStateY; //!< State vector double const* vecStateYdot; //!< Time derivative of state vector }; @@ -83,12 +83,16 @@ inline ConstSimulationState toConst(const SimulationState& simState) /** * @brief Type tag for including parameter sensitivities */ -struct WithParamSensitivity {}; +struct WithParamSensitivity +{ +}; /** * @brief Type tag for excluding parameter sensitivities */ -struct WithoutParamSensitivity {}; +struct WithoutParamSensitivity +{ +}; /** * @brief Type used for choosing the parameter sensitivity tag type @@ -96,15 +100,20 @@ struct WithoutParamSensitivity {}; * WithParamSensitivity if @p T is @c active. * @tparam T Parameter data type */ -template -struct ParamSens { }; +template struct ParamSens +{ +}; -template <> -struct ParamSens { typedef WithoutParamSensitivity enabled; }; +template <> struct ParamSens +{ + typedef WithoutParamSensitivity enabled; +}; -template <> -struct ParamSens { typedef WithParamSensitivity enabled; }; +template <> struct ParamSens +{ + typedef WithParamSensitivity enabled; +}; } // namespace cadet -#endif // LIBCADET_SIMULATIONTYPES_HPP_ +#endif // LIBCADET_SIMULATIONTYPES_HPP_ diff --git a/src/libcadet/SimulatorImpl.cpp b/src/libcadet/SimulatorImpl.cpp index 7ed710052..4f9a8ab88 100644 --- a/src/libcadet/SimulatorImpl.cpp +++ b/src/libcadet/SimulatorImpl.cpp @@ -33,221 +33,229 @@ #include "Logging.hpp" #ifdef CADET_PARALLELIZE - #include +#include - #define TBB_PREVIEW_GLOBAL_CONTROL 1 - #include +#define TBB_PREVIEW_GLOBAL_CONTROL 1 +#include - #ifdef CADET_TBB_GLOBALCTRL - #include - #endif +#ifdef CADET_TBB_GLOBALCTRL +#include +#endif #endif namespace { - template - const std::vector convertNVectorToStdVectorPtrs(N_Vector* vec, unsigned int numVec) - { - std::vector sensState(numVec, nullptr); - for (unsigned int i = 0; i < numVec; ++i) - sensState[i] = NVEC_DATA(vec[i]); +template const std::vector convertNVectorToStdVectorPtrs(N_Vector* vec, unsigned int numVec) +{ + std::vector sensState(numVec, nullptr); + for (unsigned int i = 0; i < numVec; ++i) + sensState[i] = NVEC_DATA(vec[i]); - return sensState; - } + return sensState; +} - const std::vector convertNVectorToStdVectorPtrs(unsigned int& len, N_Vector* vec, unsigned int numVec) +const std::vector convertNVectorToStdVectorPtrs(unsigned int& len, N_Vector* vec, unsigned int numVec) +{ + if (!vec || (numVec == 0)) { - if (!vec || (numVec == 0)) - { - return std::vector(0, nullptr); - } - - len = NVEC_LENGTH(vec[0]); - return convertNVectorToStdVectorPtrs(vec, numVec); + return std::vector(0, nullptr); } - const std::vector convertNVectorToStdVectorConstPtrs(unsigned int& len, N_Vector* vec, unsigned int numVec) - { - if (!vec || (numVec == 0)) - { - return std::vector(0, nullptr); - } + len = NVEC_LENGTH(vec[0]); + return convertNVectorToStdVectorPtrs(vec, numVec); +} - len = NVEC_LENGTH(vec[0]); - return convertNVectorToStdVectorPtrs(vec, numVec); +const std::vector convertNVectorToStdVectorConstPtrs(unsigned int& len, N_Vector* vec, + unsigned int numVec) +{ + if (!vec || (numVec == 0)) + { + return std::vector(0, nullptr); } - /** - * @brief Checks whether a given parameter @p id corresponds to a SECTION_TIMES parameter - * @param [in] id Parameter id to be checked - * @param [in] nSectionTimes Number of sections - * @return @c true if the given id corresponds to a SECTION_TIMES parameter, otherwise @c false - */ - inline bool isSectionTimeParameter(const cadet::ParameterId& id, unsigned int nSectionTimes) - { - return ((id.name == cadet::hashString("SECTION_TIMES")) && (id.section < nSectionTimes) && (id.component == cadet::CompIndep) && - (id.particleType == cadet::ParTypeIndep) && (id.boundState == cadet::BoundStateIndep) && (id.reaction == cadet::ReactionIndep) && + len = NVEC_LENGTH(vec[0]); + return convertNVectorToStdVectorPtrs(vec, numVec); +} + +/** + * @brief Checks whether a given parameter @p id corresponds to a SECTION_TIMES parameter + * @param [in] id Parameter id to be checked + * @param [in] nSectionTimes Number of sections + * @return @c true if the given id corresponds to a SECTION_TIMES parameter, otherwise @c false + */ +inline bool isSectionTimeParameter(const cadet::ParameterId& id, unsigned int nSectionTimes) +{ + return ((id.name == cadet::hashString("SECTION_TIMES")) && (id.section < nSectionTimes) && + (id.component == cadet::CompIndep) && (id.particleType == cadet::ParTypeIndep) && + (id.boundState == cadet::BoundStateIndep) && (id.reaction == cadet::ReactionIndep) && (id.unitOperation == cadet::UnitOpIndep)); - } +} - /** - * @brief Determines the method of consistent initialization for the transition into the given section - * @param [in] mode Consistent initialization mode set by user - * @param [in] curSec Index of current section - * @return One of @c Full, @c Lean, @c None - */ - inline cadet::ConsistentInitialization currentConsistentInitMode(cadet::ConsistentInitialization mode, unsigned int curSec) +/** + * @brief Determines the method of consistent initialization for the transition into the given section + * @param [in] mode Consistent initialization mode set by user + * @param [in] curSec Index of current section + * @return One of @c Full, @c Lean, @c None + */ +inline cadet::ConsistentInitialization currentConsistentInitMode(cadet::ConsistentInitialization mode, + unsigned int curSec) +{ + switch (mode) { - switch (mode) - { - case cadet::ConsistentInitialization::None: - return cadet::ConsistentInitialization::None; + case cadet::ConsistentInitialization::None: + return cadet::ConsistentInitialization::None; - case cadet::ConsistentInitialization::Full: - return cadet::ConsistentInitialization::Full; + case cadet::ConsistentInitialization::Full: + return cadet::ConsistentInitialization::Full; - case cadet::ConsistentInitialization::FullFirstOnly: - if (curSec == 0) - return cadet::ConsistentInitialization::Full; - else - return cadet::ConsistentInitialization::None; + case cadet::ConsistentInitialization::FullFirstOnly: + if (curSec == 0) + return cadet::ConsistentInitialization::Full; + else + return cadet::ConsistentInitialization::None; - case cadet::ConsistentInitialization::Lean: - return cadet::ConsistentInitialization::Lean; + case cadet::ConsistentInitialization::Lean: + return cadet::ConsistentInitialization::Lean; - case cadet::ConsistentInitialization::LeanFirstOnly: - if (curSec == 0) - return cadet::ConsistentInitialization::Lean; - else - return cadet::ConsistentInitialization::None; + case cadet::ConsistentInitialization::LeanFirstOnly: + if (curSec == 0) + return cadet::ConsistentInitialization::Lean; + else + return cadet::ConsistentInitialization::None; - case cadet::ConsistentInitialization::FullOnceThenLean: - if (curSec == 0) - return cadet::ConsistentInitialization::Full; - else - return cadet::ConsistentInitialization::Lean; + case cadet::ConsistentInitialization::FullOnceThenLean: + if (curSec == 0) + return cadet::ConsistentInitialization::Full; + else + return cadet::ConsistentInitialization::Lean; - case cadet::ConsistentInitialization::NoneOnceThenFull: - if (curSec == 0) - return cadet::ConsistentInitialization::None; - else - return cadet::ConsistentInitialization::Full; + case cadet::ConsistentInitialization::NoneOnceThenFull: + if (curSec == 0) + return cadet::ConsistentInitialization::None; + else + return cadet::ConsistentInitialization::Full; - case cadet::ConsistentInitialization::NoneOnceThenLean: - if (curSec == 0) - return cadet::ConsistentInitialization::None; - else - return cadet::ConsistentInitialization::Lean; - } - return cadet::ConsistentInitialization::None; + case cadet::ConsistentInitialization::NoneOnceThenLean: + if (curSec == 0) + return cadet::ConsistentInitialization::None; + else + return cadet::ConsistentInitialization::Lean; } + return cadet::ConsistentInitialization::None; +} - inline bool hasNaN(double const* const y, unsigned int size) +inline bool hasNaN(double const* const y, unsigned int size) +{ + for (unsigned int i = 0; i < size; ++i) { - for (unsigned int i = 0; i < size; ++i) - { - if (std::isnan(y[i])) - return true; - } - return false; + if (std::isnan(y[i])) + return true; } + return false; +} - inline bool hasNaN(const N_Vector p) - { - return hasNaN(NVEC_DATA(p), NVEC_LENGTH(p)); - } +inline bool hasNaN(const N_Vector p) +{ + return hasNaN(NVEC_DATA(p), NVEC_LENGTH(p)); +} - inline std::string getIDAReturnFlagName(int solverFlag) - { - char const* const retFlagName = IDAGetReturnFlagName(solverFlag); - const std::string flagName = retFlagName; - std::free(const_cast(retFlagName)); +inline std::string getIDAReturnFlagName(int solverFlag) +{ + char const* const retFlagName = IDAGetReturnFlagName(solverFlag); + const std::string flagName = retFlagName; + std::free(const_cast(retFlagName)); - return flagName; - } + return flagName; } +} // namespace namespace cadet { - namespace log - { - inline std::ostream& operator<<(std::ostream& os, const N_Vector& nv) - { - double const* const ptrY = NVEC_DATA(nv); - os << "["; - for (int i = 0; i < NVEC_LENGTH(nv)-1; ++i) - os << ptrY[i] << ","; - os << ptrY[NVEC_LENGTH(nv)-1] << "]"; - return os; - } - } +namespace log +{ +inline std::ostream& operator<<(std::ostream& os, const N_Vector& nv) +{ + double const* const ptrY = NVEC_DATA(nv); + os << "["; + for (int i = 0; i < NVEC_LENGTH(nv) - 1; ++i) + os << ptrY[i] << ","; + os << ptrY[NVEC_LENGTH(nv) - 1] << "]"; + return os; +} +} // namespace log - /** - * @brief IDAS error handler function - * @details Handles errors reported by the IDAS solver. See section 4.6.2 of the IDAS manual for details. - */ - void idasErrorHandler(int error_code, const char* module, const char* function, char* msg, void* eh_data) - { -// cadet::Simulator* const sim = static_cast(eh_data); +/** + * @brief IDAS error handler function + * @details Handles errors reported by the IDAS solver. See section 4.6.2 of the IDAS manual for details. + */ +void idasErrorHandler(int error_code, const char* module, const char* function, char* msg, void* eh_data) +{ + // cadet::Simulator* const sim = static_cast(eh_data); - std::ostringstream oss; - oss << "In function '" << function << "' of module '" << module << "', error code '" << getIDAReturnFlagName(error_code) << "':\n" << msg; + std::ostringstream oss; + oss << "In function '" << function << "' of module '" << module << "', error code '" + << getIDAReturnFlagName(error_code) << "':\n" + << msg; - // @todo Find an error handling system and put it here - if (error_code < 0) - { - // Error - LOG(Error) << oss.str(); - } - else - { - // Warning - LOG(Warning) << oss.str(); - } + // @todo Find an error handling system and put it here + if (error_code < 0) + { + // Error + LOG(Error) << oss.str(); } - - /** - * @brief IDAS wrapper function to call the model's residual() method - */ - int residualDaeWrapper(double t, N_Vector y, N_Vector yDot, N_Vector res, void* userData) + else { - cadet::Simulator* const sim = static_cast(userData); - const unsigned int secIdx = sim->getCurrentSection(t); + // Warning + LOG(Warning) << oss.str(); + } +} - LOG(Trace) << "==> Residual at t = " << t << " sec = " << secIdx; +/** + * @brief IDAS wrapper function to call the model's residual() method + */ +int residualDaeWrapper(double t, N_Vector y, N_Vector yDot, N_Vector res, void* userData) +{ + cadet::Simulator* const sim = static_cast(userData); + const unsigned int secIdx = sim->getCurrentSection(t); - if (sim->_modifiedNewton) - return sim->_model->residual(cadet::SimulationTime{t, secIdx}, cadet::ConstSimulationState{NVEC_DATA(y), NVEC_DATA(yDot)}, NVEC_DATA(res)); + LOG(Trace) << "==> Residual at t = " << t << " sec = " << secIdx; - return sim->_model->residualWithJacobian(cadet::SimulationTime{t, secIdx}, cadet::ConstSimulationState{NVEC_DATA(y), NVEC_DATA(yDot)}, NVEC_DATA(res), - cadet::AdJacobianParams{sim->_vecADres, sim->_vecADy, sim->numSensitivityAdDirections()}); - } + if (sim->_modifiedNewton) + return sim->_model->residual(cadet::SimulationTime{t, secIdx}, + cadet::ConstSimulationState{NVEC_DATA(y), NVEC_DATA(yDot)}, NVEC_DATA(res)); - int jacobianUpdateWrapper(IDAMem IDA_mem, N_Vector y, N_Vector yDot, N_Vector res, N_Vector tempv1, N_Vector tempv2, N_Vector tempv3) - { - cadet::Simulator* const sim = static_cast(IDA_mem->ida_lmem); - const double t = IDA_mem->ida_tn; - const unsigned int secIdx = sim->getCurrentSection(t); + return sim->_model->residualWithJacobian( + cadet::SimulationTime{t, secIdx}, cadet::ConstSimulationState{NVEC_DATA(y), NVEC_DATA(yDot)}, NVEC_DATA(res), + cadet::AdJacobianParams{sim->_vecADres, sim->_vecADy, sim->numSensitivityAdDirections()}); +} - LOG(Trace) << "==> Jacobian at t = " << t; +int jacobianUpdateWrapper(IDAMem IDA_mem, N_Vector y, N_Vector yDot, N_Vector res, N_Vector tempv1, N_Vector tempv2, + N_Vector tempv3) +{ + cadet::Simulator* const sim = static_cast(IDA_mem->ida_lmem); + const double t = IDA_mem->ida_tn; + const unsigned int secIdx = sim->getCurrentSection(t); - return sim->_model->jacobian(cadet::SimulationTime{ t, secIdx }, cadet::ConstSimulationState{ NVEC_DATA(y), NVEC_DATA(yDot) }, NVEC_DATA(tempv1), - cadet::AdJacobianParams{ sim->_vecADres, sim->_vecADy, sim->numSensitivityAdDirections() }); - } + LOG(Trace) << "==> Jacobian at t = " << t; - /** - * @brief Change the error weights in the state vector - * @details This sets the error weight to 0 for the network coupling equations, duplicated inlets - * and inlet and outlet state vector entries. Those entries are all solved exactly and - * without this the solver takes more steps and smaller steps for some simulations. - * The problem is the largest error is usually on the first and last column cells in the GRM - * and since the algebraic systems are solved exactly they end up duplicating those errors exactly. - * In many cases this leads to a system that would have passed error control failing due to the error - * doubleing or more in size. - * @param [in] y Current state vector - * @param [out] ewt Weight vector - * @param [in] user_data User data originally supplied to IDAS - */ + return sim->_model->jacobian( + cadet::SimulationTime{t, secIdx}, cadet::ConstSimulationState{NVEC_DATA(y), NVEC_DATA(yDot)}, NVEC_DATA(tempv1), + cadet::AdJacobianParams{sim->_vecADres, sim->_vecADy, sim->numSensitivityAdDirections()}); +} + +/** + * @brief Change the error weights in the state vector + * @details This sets the error weight to 0 for the network coupling equations, duplicated inlets + * and inlet and outlet state vector entries. Those entries are all solved exactly and + * without this the solver takes more steps and smaller steps for some simulations. + * The problem is the largest error is usually on the first and last column cells in the GRM + * and since the algebraic systems are solved exactly they end up duplicating those errors exactly. + * In many cases this leads to a system that would have passed error control failing due to the error + * doubleing or more in size. + * @param [in] y Current state vector + * @param [out] ewt Weight vector + * @param [in] user_data User data originally supplied to IDAS + */ /* int weightWrapper(N_Vector y, N_Vector ewt, void *user_data) { @@ -315,295 +323,329 @@ namespace cadet } */ - /** - * @brief IDAS wrapper function to call the model's linearSolve() method - */ - int linearSolveWrapper(IDAMem IDA_mem, N_Vector rhs, N_Vector weight, N_Vector y, N_Vector yDot, N_Vector res) - { - cadet::Simulator* const sim = static_cast(IDA_mem->ida_lmem); - const double t = IDA_mem->ida_tn; - const double alpha = IDA_mem->ida_cj; - const double tol = IDA_mem->ida_epsNewt; +/** + * @brief IDAS wrapper function to call the model's linearSolve() method + */ +int linearSolveWrapper(IDAMem IDA_mem, N_Vector rhs, N_Vector weight, N_Vector y, N_Vector yDot, N_Vector res) +{ + cadet::Simulator* const sim = static_cast(IDA_mem->ida_lmem); + const double t = IDA_mem->ida_tn; + const double alpha = IDA_mem->ida_cj; + const double tol = IDA_mem->ida_epsNewt; - LOG(Trace) << "==> Solve at t = " << t << " alpha = " << alpha << " tol = " << tol; + LOG(Trace) << "==> Solve at t = " << t << " alpha = " << alpha << " tol = " << tol; - return sim->_model->linearSolve(t, alpha, tol, NVEC_DATA(rhs), NVEC_DATA(weight), cadet::ConstSimulationState{NVEC_DATA(y), NVEC_DATA(yDot)}); - } + return sim->_model->linearSolve(t, alpha, tol, NVEC_DATA(rhs), NVEC_DATA(weight), + cadet::ConstSimulationState{NVEC_DATA(y), NVEC_DATA(yDot)}); +} - /** - * @brief IDAS wrapper function to call the model's residualSensFwd() method +/** + * @brief IDAS wrapper function to call the model's residualSensFwd() method + */ +int residualSensWrapper(int ns, double t, N_Vector y, N_Vector yDot, N_Vector res, N_Vector* yS, N_Vector* ySDot, + N_Vector* resS, void* userData, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) +{ + cadet::Simulator* const sim = static_cast(userData); + const std::vector sensY = convertNVectorToStdVectorPtrs(yS, ns); + const std::vector sensYdot = convertNVectorToStdVectorPtrs(ySDot, ns); + std::vector sensRes = convertNVectorToStdVectorPtrs(resS, ns); + const unsigned int secIdx = sim->getCurrentSection(t); + + LOG(Trace) << "==> Residual SENS at t = " << t << " sec = " << secIdx; + + /* + reinterpret_cast(sim->_model)->genJacobian(t, secIdx, NVEC_DATA(y), + NVEC_DATA(yDot)); reinterpret_cast(sim->_model)->genJacobian(ns, t, NVEC_DATA(y), + NVEC_DATA(yDot), NVEC_DATA(res), sensY, sensYdot, sensRes, sim->_vecADres, NVEC_DATA(tmp1), NVEC_DATA(tmp2), + NVEC_DATA(tmp3)); */ - int residualSensWrapper(int ns, double t, N_Vector y, N_Vector yDot, N_Vector res, - N_Vector* yS, N_Vector* ySDot, N_Vector* resS, - void *userData, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) + + if (sim->_modifiedNewton) { - cadet::Simulator* const sim = static_cast(userData); - const std::vector sensY = convertNVectorToStdVectorPtrs(yS, ns); - const std::vector sensYdot = convertNVectorToStdVectorPtrs(ySDot, ns); - std::vector sensRes = convertNVectorToStdVectorPtrs(resS, ns); - const unsigned int secIdx = sim->getCurrentSection(t); + return sim->_model->residualSensFwdWithJacobian( + ns, cadet::SimulationTime{t, secIdx}, cadet::ConstSimulationState{NVEC_DATA(y), NVEC_DATA(yDot)}, + NVEC_DATA(res), sensY, sensYdot, sensRes, + cadet::AdJacobianParams{sim->_vecADres, sim->_vecADy, sim->numSensitivityAdDirections()}, NVEC_DATA(tmp1), + NVEC_DATA(tmp2), NVEC_DATA(tmp3)); + } - LOG(Trace) << "==> Residual SENS at t = " << t << " sec = " << secIdx; + return sim->_model->residualSensFwd( + ns, cadet::SimulationTime{t, secIdx}, cadet::ConstSimulationState{NVEC_DATA(y), NVEC_DATA(yDot)}, + NVEC_DATA(res), sensY, sensYdot, sensRes, sim->_vecADres, NVEC_DATA(tmp1), NVEC_DATA(tmp2), NVEC_DATA(tmp3)); +} -/* - reinterpret_cast(sim->_model)->genJacobian(t, secIdx, NVEC_DATA(y), NVEC_DATA(yDot)); - reinterpret_cast(sim->_model)->genJacobian(ns, t, NVEC_DATA(y), NVEC_DATA(yDot), NVEC_DATA(res), - sensY, sensYdot, sensRes, sim->_vecADres, NVEC_DATA(tmp1), NVEC_DATA(tmp2), NVEC_DATA(tmp3)); -*/ +Simulator::Simulator() + : _model(nullptr), _solRecorder(nullptr), _idaMemBlock(nullptr), _vecStateY(nullptr), _vecStateYdot(nullptr), + _vecFwdYs(nullptr), _vecFwdYsDot(nullptr), _relTolS(1.0e-9), _absTol(1, 1.0e-12), _relTol(1.0e-9), + _initStepSize(1, 1.0e-6), _maxSteps(10000), _maxStepSize(0.0), _nThreads(0), _sensErrorTestEnabled(true), + _maxNewtonIter(4), _maxErrorTestFail(10), _maxConvTestFail(10), _maxNewtonIterSens(4), _curSec(0), + _skipConsistencyStateY(false), _skipConsistencySensitivity(false), + _consistentInitMode(ConsistentInitialization::Full), _consistentInitModeSens(ConsistentInitialization::Full), + _vecADres(nullptr), _vecADy(nullptr), _lastIntTime(0.0), _notification(nullptr) +{ +#if defined(ACTIVE_SFAD) || defined(ACTIVE_SETFAD) + LOG(Debug) << "Resetting AD directions from " << ad::getDirections() << " to default " << ad::getMaxDirections(); + ad::setDirections(ad::getMaxDirections()); +#endif +} - if (sim->_modifiedNewton) - { - return sim->_model->residualSensFwdWithJacobian(ns, cadet::SimulationTime{t, secIdx}, cadet::ConstSimulationState{NVEC_DATA(y), NVEC_DATA(yDot)}, NVEC_DATA(res), - sensY, sensYdot, sensRes, cadet::AdJacobianParams{sim->_vecADres, sim->_vecADy, sim->numSensitivityAdDirections()}, NVEC_DATA(tmp1), NVEC_DATA(tmp2), NVEC_DATA(tmp3)); - } +Simulator::~Simulator() CADET_NOEXCEPT +{ + clearModel(); +} - return sim->_model->residualSensFwd(ns, cadet::SimulationTime{t, secIdx}, cadet::ConstSimulationState{NVEC_DATA(y), NVEC_DATA(yDot)}, NVEC_DATA(res), - sensY, sensYdot, sensRes, sim->_vecADres, NVEC_DATA(tmp1), NVEC_DATA(tmp2), NVEC_DATA(tmp3)); - } +void Simulator::clearModel() CADET_NOEXCEPT +{ + delete[] _vecADy; + delete[] _vecADres; - Simulator::Simulator() : _model(nullptr), _solRecorder(nullptr), _idaMemBlock(nullptr), _vecStateY(nullptr), - _vecStateYdot(nullptr), _vecFwdYs(nullptr), _vecFwdYsDot(nullptr), - _relTolS(1.0e-9), _absTol(1, 1.0e-12), _relTol(1.0e-9), _initStepSize(1, 1.0e-6), _maxSteps(10000), _maxStepSize(0.0), - _nThreads(0), _sensErrorTestEnabled(true), _maxNewtonIter(4), _maxErrorTestFail(10), _maxConvTestFail(10), - _maxNewtonIterSens(4), _curSec(0), _skipConsistencyStateY(false), _skipConsistencySensitivity(false), - _consistentInitMode(ConsistentInitialization::Full), _consistentInitModeSens(ConsistentInitialization::Full), - _vecADres(nullptr), _vecADy(nullptr), _lastIntTime(0.0), _notification(nullptr) + if ((_sensitiveParams.slices() > 0) && _vecFwdYs) { -#if defined(ACTIVE_SFAD) || defined(ACTIVE_SETFAD) - LOG(Debug) << "Resetting AD directions from " << ad::getDirections() << " to default " << ad::getMaxDirections(); - ad::setDirections(ad::getMaxDirections()); -#endif + NVec_DestroyArray(_vecFwdYs, _sensitiveParams.slices()); + NVec_DestroyArray(_vecFwdYsDot, _sensitiveParams.slices()); } + _sensitiveParams.clear(); - Simulator::~Simulator() CADET_NOEXCEPT - { - clearModel(); - } + if (_vecStateYdot) + NVec_Destroy(_vecStateYdot); + if (_vecStateY) + NVec_Destroy(_vecStateY); - void Simulator::clearModel() CADET_NOEXCEPT - { - delete[] _vecADy; - delete[] _vecADres; + if (_idaMemBlock) + IDAFree(&_idaMemBlock); +} - if ((_sensitiveParams.slices() > 0) && _vecFwdYs) - { - NVec_DestroyArray(_vecFwdYs, _sensitiveParams.slices()); - NVec_DestroyArray(_vecFwdYsDot, _sensitiveParams.slices()); - } - _sensitiveParams.clear(); +void Simulator::initializeModel(IModelSystem& model) +{ + // Clean up + clearModel(); - if (_vecStateYdot) - NVec_Destroy(_vecStateYdot); - if (_vecStateY) - NVec_Destroy(_vecStateY); + // Require ISimulatableModel descendant + _model = reinterpret_cast(&model); - if (_idaMemBlock) - IDAFree(&_idaMemBlock); - } + // Allocate and initialize state vectors + const unsigned int nDOFs = _model->numDofs(); + _vecStateY = NVec_New(nDOFs); + _vecStateYdot = NVec_New(nDOFs); - void Simulator::initializeModel(IModelSystem& model) + // Propagate section times if available + if (_sectionTimes.size() > 0) { - // Clean up - clearModel(); - - // Require ISimulatableModel descendant - _model = reinterpret_cast(&model); + bool* const secCont = new bool[_sectionContinuity.size()]; + std::copy(_sectionContinuity.begin(), _sectionContinuity.end(), secCont); - // Allocate and initialize state vectors - const unsigned int nDOFs = _model->numDofs(); - _vecStateY = NVec_New(nDOFs); - _vecStateYdot = NVec_New(nDOFs); + // Convert from active to double + double* const secTimes = new double[_sectionTimes.size()]; + for (std::size_t i = 0; i < _sectionTimes.size(); ++i) + secTimes[i] = static_cast(_sectionTimes[i]); - // Propagate section times if available - if (_sectionTimes.size() > 0) - { - bool* const secCont = new bool[_sectionContinuity.size()]; - std::copy(_sectionContinuity.begin(), _sectionContinuity.end(), secCont); + _model->setSectionTimes(secTimes, secCont, _sectionTimes.size() - 1); - // Convert from active to double - double* const secTimes = new double[_sectionTimes.size()]; - for (std::size_t i = 0; i < _sectionTimes.size(); ++i) - secTimes[i] = static_cast(_sectionTimes[i]); + delete[] secTimes; + delete[] secCont; + } - _model->setSectionTimes(secTimes, secCont, _sectionTimes.size() - 1); + // Initialize with all zeros, correct initial conditions will be set later + NVec_Const(0.0, _vecStateY); + NVec_Const(0.0, _vecStateYdot); - delete[] secTimes; - delete[] secCont; - } + // Create IDAS internal memory + _idaMemBlock = IDACreate(); - // Initialize with all zeros, correct initial conditions will be set later - NVec_Const(0.0, _vecStateY); - NVec_Const(0.0, _vecStateYdot); + // IDAS Step 4.1: Specify error handler function + IDASetErrHandlerFn(_idaMemBlock, &idasErrorHandler, this); - // Create IDAS internal memory - _idaMemBlock = IDACreate(); + // IDAS Step 5: Initialize the solver + _model->applyInitialCondition(SimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}); - // IDAS Step 4.1: Specify error handler function - IDASetErrHandlerFn(_idaMemBlock, &idasErrorHandler, this); + // Use 0.0 as beginning of simulation time if we haven't set section times yet + if (_sectionTimes.size() > 0) + IDAInit(_idaMemBlock, &residualDaeWrapper, static_cast(_sectionTimes[0]), _vecStateY, _vecStateYdot); + else + IDAInit(_idaMemBlock, &residualDaeWrapper, 0.0, _vecStateY, _vecStateYdot); - // IDAS Step 5: Initialize the solver - _model->applyInitialCondition(SimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}); + // IDAS Step 6: Specify integration tolerances (S: scalar; V: array) + updateMainErrorTolerances(); - // Use 0.0 as beginning of simulation time if we haven't set section times yet - if (_sectionTimes.size() > 0) - IDAInit(_idaMemBlock, &residualDaeWrapper, static_cast(_sectionTimes[0]), _vecStateY, _vecStateYdot); - else - IDAInit(_idaMemBlock, &residualDaeWrapper, 0.0, _vecStateY, _vecStateYdot); + // IDAS Step 7.1: Set optional inputs - // IDAS Step 6: Specify integration tolerances (S: scalar; V: array) - updateMainErrorTolerances(); + // Set time integrator parameters + IDASetMaxNumSteps(_idaMemBlock, _maxSteps); + IDASetMaxStep(_idaMemBlock, _maxStepSize); + IDASetMaxNonlinIters(_idaMemBlock, _maxNewtonIter); + IDASetMaxErrTestFails(_idaMemBlock, _maxErrorTestFail); + IDASetMaxConvFails(_idaMemBlock, _maxConvTestFail); + IDASetSensMaxNonlinIters(_idaMemBlock, _maxNewtonIterSens); - // IDAS Step 7.1: Set optional inputs + // Specify the linear solver. + IDAMem IDA_mem = static_cast(_idaMemBlock); - // Set time integrator parameters - IDASetMaxNumSteps(_idaMemBlock, _maxSteps); - IDASetMaxStep(_idaMemBlock, _maxStepSize); - IDASetMaxNonlinIters(_idaMemBlock, _maxNewtonIter); - IDASetMaxErrTestFails(_idaMemBlock, _maxErrorTestFail); - IDASetMaxConvFails(_idaMemBlock, _maxConvTestFail); - IDASetSensMaxNonlinIters(_idaMemBlock, _maxNewtonIterSens); - - // Specify the linear solver. - IDAMem IDA_mem = static_cast(_idaMemBlock); - - IDA_mem->ida_lsolve = &linearSolveWrapper; - IDA_mem->ida_lmem = this; - IDA_mem->ida_linit = nullptr; - IDA_mem->ida_lsetup = _modifiedNewton ? &jacobianUpdateWrapper : nullptr; - IDA_mem->ida_lperf = nullptr; - IDA_mem->ida_lfree = nullptr; + IDA_mem->ida_lsolve = &linearSolveWrapper; + IDA_mem->ida_lmem = this; + IDA_mem->ida_linit = nullptr; + IDA_mem->ida_lsetup = _modifiedNewton ? &jacobianUpdateWrapper : nullptr; + IDA_mem->ida_lperf = nullptr; + IDA_mem->ida_lfree = nullptr; // IDA_mem->ida_efun = &weightWrapper; // IDA_mem->ida_user_efun = 1; #if CADET_SUNDIALS_IFACE <= 2 - IDA_mem->ida_setupNonNull = false; + IDA_mem->ida_setupNonNull = false; #endif - // Attach user data structure - IDASetUserData(_idaMemBlock, this); + // Attach user data structure + IDASetUserData(_idaMemBlock, this); - // Allocate memory for AD if required - if (_model->usesAD()) - { - _vecADres = new active[nDOFs]; - _vecADy = new active[nDOFs]; - } + // Allocate memory for AD if required + if (_model->usesAD()) + { + _vecADres = new active[nDOFs]; + _vecADy = new active[nDOFs]; } +} + +void Simulator::updateMainErrorTolerances() +{ + if (!_idaMemBlock) + return; - void Simulator::updateMainErrorTolerances() + if (_absTol.size() > 1) { - if (!_idaMemBlock) + if (!_model) return; - if (_absTol.size() > 1) + N_Vector absTolTemp = NVec_New(_model->numDofs()); + const unsigned int pureDofs = _model->numPureDofs(); + + // Check whether user has given us full absolute error for all (pure) DOFs + if (_absTol.size() >= pureDofs) { - if (!_model) - return; + // Copy error tolerances for pure data + std::copy(_absTol.data(), _absTol.data() + pureDofs, NVEC_DATA(absTolTemp)); - N_Vector absTolTemp = NVec_New(_model->numDofs()); - const unsigned int pureDofs = _model->numPureDofs(); + // Calculate error tolerances for coupling DOFs and append them + const std::vector addAbsErrTol = + _model->calculateErrorTolsForAdditionalDofs(_absTol.data(), _absTol.size()); + std::copy(addAbsErrTol.data(), addAbsErrTol.data() + addAbsErrTol.size(), NVEC_DATA(absTolTemp) + pureDofs); + } + else + { + // We've received an expandable error specification + _model->expandErrorTol(_absTol.data(), _absTol.size(), NVEC_DATA(absTolTemp)); + } - // Check whether user has given us full absolute error for all (pure) DOFs - if (_absTol.size() >= pureDofs) - { - // Copy error tolerances for pure data - std::copy(_absTol.data(), _absTol.data() + pureDofs, NVEC_DATA(absTolTemp)); + IDASVtolerances(_idaMemBlock, _relTol, absTolTemp); + NVec_Destroy(absTolTemp); + } + else + IDASStolerances(_idaMemBlock, _relTol, _absTol[0]); +} - // Calculate error tolerances for coupling DOFs and append them - const std::vector addAbsErrTol = _model->calculateErrorTolsForAdditionalDofs(_absTol.data(), _absTol.size()); - std::copy(addAbsErrTol.data(), addAbsErrTol.data() + addAbsErrTol.size(), NVEC_DATA(absTolTemp) + pureDofs); - } - else - { - // We've received an expandable error specification - _model->expandErrorTol(_absTol.data(), _absTol.size(), NVEC_DATA(absTolTemp)); - } +void Simulator::preFwdSensInit(unsigned int nSens) +{ + // Turn off solution of sensitivity systems (this will be overridden by a call to IDASensInit below) + // In fact, this has only an effect, if at first a computation with sensitivities is performed and then + // (without clearing and reallocating internal memory by cs_free/cs_malloc) another computation without + // sensitivities is started. + IDASensToggleOff(_idaMemBlock); - IDASVtolerances(_idaMemBlock, _relTol, absTolTemp); - NVec_Destroy(absTolTemp); - } - else - IDASStolerances(_idaMemBlock, _relTol, _absTol[0]); + if (_vecFwdYs) + { + NVec_DestroyArray(_vecFwdYs, nSens); + NVec_DestroyArray(_vecFwdYsDot, nSens); + _vecFwdYs = nullptr; + _vecFwdYsDot = nullptr; } - void Simulator::preFwdSensInit(unsigned int nSens) + // Allocate sensitivity state vectors + if (nSens > 0) { - // Turn off solution of sensitivity systems (this will be overridden by a call to IDASensInit below) - // In fact, this has only an effect, if at first a computation with sensitivities is performed and then - // (without clearing and reallocating internal memory by cs_free/cs_malloc) another computation without - // sensitivities is started. - IDASensToggleOff(_idaMemBlock); + _vecFwdYs = NVec_CloneArray(nSens, _vecStateY); + _vecFwdYsDot = NVec_CloneArray(nSens, _vecStateYdot); - if (_vecFwdYs) - { - NVec_DestroyArray(_vecFwdYs, nSens); - NVec_DestroyArray(_vecFwdYsDot, nSens); - _vecFwdYs = nullptr; - _vecFwdYsDot = nullptr; - } + // Allocate memory for AD if not already done + if (!_vecADres) + _vecADres = new active[_model->numDofs()]; + } +} - // Allocate sensitivity state vectors - if (nSens > 0) - { - _vecFwdYs = NVec_CloneArray(nSens, _vecStateY); - _vecFwdYsDot = NVec_CloneArray(nSens, _vecStateYdot); +void Simulator::postFwdSensInit(unsigned int nSens) +{ + // Initialize IDA sensitivity computation + // TODO: Use IDASensReInit if this is not the first time sensitivities are activated + IDASensInit(_idaMemBlock, nSens, IDA_STAGGERED, &cadet::residualSensWrapper, _vecFwdYs, _vecFwdYsDot); - // Allocate memory for AD if not already done - if (!_vecADres) - _vecADres = new active[_model->numDofs()]; - } - } + // Set sensitivity integration tolerances + IDASensSStolerances(_idaMemBlock, _relTolS, _absTolS.data()); - void Simulator::postFwdSensInit(unsigned int nSens) - { - // Initialize IDA sensitivity computation - // TODO: Use IDASensReInit if this is not the first time sensitivities are activated - IDASensInit(_idaMemBlock, nSens, IDA_STAGGERED, &cadet::residualSensWrapper, _vecFwdYs, _vecFwdYsDot); + // Activate sensitivity error control + IDASetSensErrCon(_idaMemBlock, _sensErrorTestEnabled); +} - // Set sensitivity integration tolerances - IDASensSStolerances(_idaMemBlock, _relTolS, _absTolS.data()); +void Simulator::initializeFwdSensitivities() +{ + const unsigned int nSens = _sensitiveParams.slices(); + preFwdSensInit(nSens); - // Activate sensitivity error control - IDASetSensErrCon(_idaMemBlock, _sensErrorTestEnabled); - } + if (nSens == 0) + return; - void Simulator::initializeFwdSensitivities() + for (unsigned int dir = 0; dir < nSens; ++dir) { - const unsigned int nSens = _sensitiveParams.slices(); - preFwdSensInit(nSens); + // Initialize sensitivity vectors with 0.0 + NVec_Const(0.0, _vecFwdYs[dir]); + NVec_Const(0.0, _vecFwdYsDot[dir]); + } - if (nSens == 0) - return; + // Apply initial values due to sensitivites with respect to initial conditions + _model->initializeSensitivityStates(convertNVectorToStdVectorPtrs(_vecFwdYs, nSens)); - for (unsigned int dir = 0; dir < nSens; ++dir) - { - // Initialize sensitivity vectors with 0.0 - NVec_Const(0.0, _vecFwdYs[dir]); - NVec_Const(0.0, _vecFwdYsDot[dir]); - } + // Compute consistent initial conditions for sensitivity systems later + _skipConsistencySensitivity = false; - // Apply initial values due to sensitivites with respect to initial conditions - _model->initializeSensitivityStates(convertNVectorToStdVectorPtrs(_vecFwdYs, nSens)); + postFwdSensInit(nSens); +} - // Compute consistent initial conditions for sensitivity systems later - _skipConsistencySensitivity = false; +void Simulator::initializeFwdSensitivities(double const* const* const initSens, double const* const* const initSensDot) +{ + const unsigned int nSens = _sensitiveParams.slices(); + preFwdSensInit(nSens); - postFwdSensInit(nSens); - } + if (nSens == 0) + return; + + if (!initSens) + throw std::invalid_argument("Pointer to initial sensitivities is NULL"); - void Simulator::initializeFwdSensitivities(double const * const* const initSens, double const * const* const initSensDot) + if (!initSensDot) + throw std::invalid_argument("Pointer to initial sensitivity time derivatives is NULL"); + + for (unsigned int dir = 0; dir < nSens; ++dir) { - const unsigned int nSens = _sensitiveParams.slices(); - preFwdSensInit(nSens); + double* const yS = NVEC_DATA(_vecFwdYs[dir]); + double const* const src = initSens[dir]; + double* const ySdot = NVEC_DATA(_vecFwdYsDot[dir]); + double const* const srcDot = initSensDot[dir]; - if (nSens == 0) - return; + // Initialize sensitivity vectors with given data + std::copy(src, src + NVEC_LENGTH(_vecFwdYs[dir]), yS); + std::copy(srcDot, srcDot + NVEC_LENGTH(_vecFwdYsDot[dir]), ySdot); + } + + // Don't assume that consistent values were given + _skipConsistencySensitivity = false; - if (!initSens) - throw std::invalid_argument("Pointer to initial sensitivities is NULL"); + postFwdSensInit(nSens); +} - if (!initSensDot) - throw std::invalid_argument("Pointer to initial sensitivity time derivatives is NULL"); +void Simulator::applyInitialConditionFwdSensitivities(double const* const* const initSens, + double const* const* const initSensDot) +{ + const unsigned int nSens = _sensitiveParams.slices(); + if (nSens == 0) + return; + if (initSens && initSensDot) + { for (unsigned int dir = 0; dir < nSens; ++dir) { double* const yS = NVEC_DATA(_vecFwdYs[dir]); @@ -615,230 +657,244 @@ namespace cadet std::copy(src, src + NVEC_LENGTH(_vecFwdYs[dir]), yS); std::copy(srcDot, srcDot + NVEC_LENGTH(_vecFwdYsDot[dir]), ySdot); } - - // Don't assume that consistent values were given - _skipConsistencySensitivity = false; - - postFwdSensInit(nSens); } - - void Simulator::applyInitialConditionFwdSensitivities(double const * const* const initSens, double const * const* const initSensDot) + else { - const unsigned int nSens = _sensitiveParams.slices(); - if (nSens == 0) - return; - - if (initSens && initSensDot) - { - for (unsigned int dir = 0; dir < nSens; ++dir) - { - double* const yS = NVEC_DATA(_vecFwdYs[dir]); - double const* const src = initSens[dir]; - double* const ySdot = NVEC_DATA(_vecFwdYsDot[dir]); - double const* const srcDot = initSensDot[dir]; - - // Initialize sensitivity vectors with given data - std::copy(src, src + NVEC_LENGTH(_vecFwdYs[dir]), yS); - std::copy(srcDot, srcDot + NVEC_LENGTH(_vecFwdYsDot[dir]), ySdot); - } - } - else + for (unsigned int dir = 0; dir < nSens; ++dir) { - for (unsigned int dir = 0; dir < nSens; ++dir) - { - // Initialize sensitivity vectors with 0.0 - NVec_Const(0.0, _vecFwdYs[dir]); - NVec_Const(0.0, _vecFwdYsDot[dir]); - } - - // Apply initial values due to sensitivites with respect to initial conditions - _model->initializeSensitivityStates(convertNVectorToStdVectorPtrs(_vecFwdYs, nSens)); + // Initialize sensitivity vectors with 0.0 + NVec_Const(0.0, _vecFwdYs[dir]); + NVec_Const(0.0, _vecFwdYsDot[dir]); } - // Don't assume that consistent values were given - _skipConsistencySensitivity = false; + // Apply initial values due to sensitivites with respect to initial conditions + _model->initializeSensitivityStates(convertNVectorToStdVectorPtrs(_vecFwdYs, nSens)); } - void Simulator::applyInitialCondition() - { - _model->applyInitialCondition(SimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}); - IDAReInit(_idaMemBlock, static_cast(_sectionTimes[0]), _vecStateY, _vecStateYdot); + // Don't assume that consistent values were given + _skipConsistencySensitivity = false; +} - // Better check for consistency - _skipConsistencyStateY = false; - } +void Simulator::applyInitialCondition() +{ + _model->applyInitialCondition(SimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}); + IDAReInit(_idaMemBlock, static_cast(_sectionTimes[0]), _vecStateY, _vecStateYdot); - void Simulator::setInitialCondition(IParameterProvider& paramProvider) - { - _model->readInitialCondition(paramProvider); - } + // Better check for consistency + _skipConsistencyStateY = false; +} - void Simulator::applyInitialCondition(double const* const initState) - { - // Copy initial state - double* const y = NVEC_DATA(_vecStateY); - std::copy(initState, initState + NVEC_LENGTH(_vecStateY), y); +void Simulator::setInitialCondition(IParameterProvider& paramProvider) +{ + _model->readInitialCondition(paramProvider); +} - IDAReInit(_idaMemBlock, static_cast(_sectionTimes[0]), _vecStateY, _vecStateYdot); +void Simulator::applyInitialCondition(double const* const initState) +{ + // Copy initial state + double* const y = NVEC_DATA(_vecStateY); + std::copy(initState, initState + NVEC_LENGTH(_vecStateY), y); - // We need to compute matching yDot for consistency - _skipConsistencyStateY = false; - } + IDAReInit(_idaMemBlock, static_cast(_sectionTimes[0]), _vecStateY, _vecStateYdot); - void Simulator::applyInitialCondition(double const* const initState, double const* const initStateDot) - { - // Copy initial state - double* const y = NVEC_DATA(_vecStateY); - std::copy(initState, initState + NVEC_LENGTH(_vecStateY), y); + // We need to compute matching yDot for consistency + _skipConsistencyStateY = false; +} - // Copy initial time derivative state - double* const yDot = NVEC_DATA(_vecStateYdot); - std::copy(initStateDot, initStateDot + NVEC_LENGTH(_vecStateY), yDot); +void Simulator::applyInitialCondition(double const* const initState, double const* const initStateDot) +{ + // Copy initial state + double* const y = NVEC_DATA(_vecStateY); + std::copy(initState, initState + NVEC_LENGTH(_vecStateY), y); - IDAReInit(_idaMemBlock, static_cast(_sectionTimes[0]), _vecStateY, _vecStateYdot); + // Copy initial time derivative state + double* const yDot = NVEC_DATA(_vecStateYdot); + std::copy(initStateDot, initStateDot + NVEC_LENGTH(_vecStateY), yDot); - // Do not assume that the initial state is consistent - _skipConsistencyStateY = false; - } + IDAReInit(_idaMemBlock, static_cast(_sectionTimes[0]), _vecStateY, _vecStateYdot); - void Simulator::skipConsistentInitialization() - { - _skipConsistencyStateY = true; - _skipConsistencySensitivity = true; - } + // Do not assume that the initial state is consistent + _skipConsistencyStateY = false; +} - void Simulator::setConsistentInitialization(ConsistentInitialization ci) - { - _consistentInitMode = ci; - } +void Simulator::skipConsistentInitialization() +{ + _skipConsistencyStateY = true; + _skipConsistencySensitivity = true; +} - void Simulator::setConsistentInitializationSens(ConsistentInitialization ci) - { - _consistentInitModeSens = ci; - } +void Simulator::setConsistentInitialization(ConsistentInitialization ci) +{ + _consistentInitMode = ci; +} - std::unordered_map Simulator::getAllParameterValues() const - { - std::unordered_map data; - if (_model) - data = _model->getAllParameterValues(); +void Simulator::setConsistentInitializationSens(ConsistentInitialization ci) +{ + _consistentInitModeSens = ci; +} - // Add section times - const StringHash secTimesName = hashString("SECTION_TIMES"); - for (std::size_t i = 0; i < _sectionTimes.size(); ++i) - data[makeParamId(secTimesName, UnitOpIndep, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, i)] = static_cast(_sectionTimes[i]); +std::unordered_map Simulator::getAllParameterValues() const +{ + std::unordered_map data; + if (_model) + data = _model->getAllParameterValues(); - return data; - } + // Add section times + const StringHash secTimesName = hashString("SECTION_TIMES"); + for (std::size_t i = 0; i < _sectionTimes.size(); ++i) + data[makeParamId(secTimesName, UnitOpIndep, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, i)] = + static_cast(_sectionTimes[i]); - bool Simulator::hasParameter(const ParameterId& pId) const - { - if (isSectionTimeParameter(pId, _sectionTimes.size())) - return true; + return data; +} - if (_model) - return _model->hasParameter(pId); +bool Simulator::hasParameter(const ParameterId& pId) const +{ + if (isSectionTimeParameter(pId, _sectionTimes.size())) + return true; - return false; - } + if (_model) + return _model->hasParameter(pId); - void Simulator::setSensitiveParameter(const ParameterId& id) - { - setSensitiveParameter(id, 1.0e-5); - } + return false; +} - void Simulator::setSensitiveParameter(const ParameterId& id, double absTolS) - { - setSensitiveParameter(&id, nullptr, 1, absTolS); - } +void Simulator::setSensitiveParameter(const ParameterId& id) +{ + setSensitiveParameter(id, 1.0e-5); +} + +void Simulator::setSensitiveParameter(const ParameterId& id, double absTolS) +{ + setSensitiveParameter(&id, nullptr, 1, absTolS); +} - void Simulator::setSensitiveParameter(ParameterId const* ids, double const* diffFactors, unsigned int numParams, double absTolS) +void Simulator::setSensitiveParameter(ParameterId const* ids, double const* diffFactors, unsigned int numParams, + double absTolS) +{ + // Set AD directions + const unsigned int adDir = numSensitivityAdDirections(); + for (unsigned int i = 0; i < numParams; ++i) { - // Set AD directions - const unsigned int adDir = numSensitivityAdDirections(); - for (unsigned int i = 0; i < numParams; ++i) - { - double localDiffFactor = 1.0; - if (diffFactors) - localDiffFactor = diffFactors[i]; + double localDiffFactor = 1.0; + if (diffFactors) + localDiffFactor = diffFactors[i]; - bool paramFound = setSectionTimesSensitive(ids[i], adDir, localDiffFactor); - paramFound = _model->setSensitiveParameter(ids[i], adDir, localDiffFactor) || paramFound; + bool paramFound = setSectionTimesSensitive(ids[i], adDir, localDiffFactor); + paramFound = _model->setSensitiveParameter(ids[i], adDir, localDiffFactor) || paramFound; - if (!paramFound) - { - LOG(Warning) << "Warning: Unkown parameter " << ids[i] << " in parameter join was ignored"; - } + if (!paramFound) + { + LOG(Warning) << "Warning: Unkown parameter " << ids[i] << " in parameter join was ignored"; } + } - _sensitiveParams.pushBackSlice(ids, numParams); - _absTolS.push_back(absTolS); + _sensitiveParams.pushBackSlice(ids, numParams); + _absTolS.push_back(absTolS); - if (diffFactors) - _sensitiveParamsFactor.insert(_sensitiveParamsFactor.end(), diffFactors, diffFactors + numParams); - else - _sensitiveParamsFactor.insert(_sensitiveParamsFactor.end(), numParams, 1.0); - } + if (diffFactors) + _sensitiveParamsFactor.insert(_sensitiveParamsFactor.end(), diffFactors, diffFactors + numParams); + else + _sensitiveParamsFactor.insert(_sensitiveParamsFactor.end(), numParams, 1.0); +} - void Simulator::setSensitiveParameter(ParameterId const* ids, unsigned int numParams, double absTolS) - { - setSensitiveParameter(ids, nullptr, numParams, absTolS); - } +void Simulator::setSensitiveParameter(ParameterId const* ids, unsigned int numParams, double absTolS) +{ + setSensitiveParameter(ids, nullptr, numParams, absTolS); +} + +void Simulator::resetSensParams() +{ + using std::to_string; - void Simulator::resetSensParams() + unsigned int globalIdx = 0; + for (unsigned int i = 0; i < _sensitiveParams.slices(); ++i) { - using std::to_string; + ParameterId const* const ids = _sensitiveParams[i]; - unsigned int globalIdx = 0; - for (unsigned int i = 0; i < _sensitiveParams.slices(); ++i) + for (unsigned int j = 0; j < _sensitiveParams.sliceSize(i); ++j, ++globalIdx) { - ParameterId const* const ids = _sensitiveParams[i]; - - for (unsigned int j = 0; j < _sensitiveParams.sliceSize(i); ++j, ++globalIdx) - { - bool paramFound = setSectionTimesSensitive(ids[j], i, _sensitiveParamsFactor[globalIdx]); - paramFound = _model->setSensitiveParameter(ids[j], i, _sensitiveParamsFactor[globalIdx]) || paramFound; + bool paramFound = setSectionTimesSensitive(ids[j], i, _sensitiveParamsFactor[globalIdx]); + paramFound = _model->setSensitiveParameter(ids[j], i, _sensitiveParamsFactor[globalIdx]) || paramFound; - if (!paramFound) - throw InvalidParameterException("Sensitive parameter " + to_string(ids[j]) + " disappeared"); - } + if (!paramFound) + throw InvalidParameterException("Sensitive parameter " + to_string(ids[j]) + " disappeared"); } } +} - bool Simulator::setSectionTimesSensitive(const ParameterId& id, unsigned int adDirection, double adValue) +bool Simulator::setSectionTimesSensitive(const ParameterId& id, unsigned int adDirection, double adValue) +{ + if (isSectionTimeParameter(id, _sectionTimes.size())) { - if (isSectionTimeParameter(id, _sectionTimes.size())) - { - // Correct adValue - LOG(Debug) << "Found parameter " << id << " in SECTION_TIMES: Dir " << adDirection << " is set to " - << adValue << " [" << static_cast(_sectionTimes[id.section]) << " < " << _sectionTimes.size() << "]"; - _sectionTimes[id.section].setADValue(adDirection, adValue); + // Correct adValue + LOG(Debug) << "Found parameter " << id << " in SECTION_TIMES: Dir " << adDirection << " is set to " << adValue + << " [" << static_cast(_sectionTimes[id.section]) << " < " << _sectionTimes.size() << "]"; + _sectionTimes[id.section].setADValue(adDirection, adValue); - return true; - } - return false; + return true; } + return false; +} - void Simulator::clearSensParams() - { - _sensitiveParams.clear(); - _sensitiveParamsFactor.clear(); - _absTolS.clear(); +void Simulator::clearSensParams() +{ + _sensitiveParams.clear(); + _sensitiveParamsFactor.clear(); + _absTolS.clear(); - _model->clearSensParams(); - for (std::size_t i = 0; i < _sectionTimes.size(); ++i) - _sectionTimes[i].setADValue(0.0); + _model->clearSensParams(); + for (std::size_t i = 0; i < _sectionTimes.size(); ++i) + _sectionTimes[i].setADValue(0.0); - initializeFwdSensitivities(); + initializeFwdSensitivities(); +} + +unsigned int Simulator::numSensParams() const CADET_NOEXCEPT +{ + return _sensitiveParams.slices(); +} + +void Simulator::setSensitiveParameterValue(const ParameterId& id, double value) +{ + if (isSectionTimeParameter(id, _sectionTimes.size())) + { + _sectionTimes[id.section].setValue(value); + // Do not exit here, since model can also contain instances of SECTION_TIMES } - unsigned int Simulator::numSensParams() const CADET_NOEXCEPT + if (!_model) + return; + + util::SlicedVector::size_type temp; + util::SlicedVector::size_type linearIndex; + util::SlicedVector::size_type idxSlice = _sensitiveParams.findElementAndSlice(id, temp, linearIndex); + if (idxSlice < _sensitiveParams.slices()) { - return _sensitiveParams.slices(); + ParameterId const* const fusedIds = _sensitiveParams[idxSlice]; + const util::SlicedVector::size_type sliceOffset = _sensitiveParams.sliceOffset(idxSlice); + + // Noramlize parameter value + value /= _sensitiveParamsFactor[linearIndex]; + + // Take care of linear factors + // @todo add constant offset to linear combination + for (unsigned int i = 0; i < _sensitiveParams.sliceSize(idxSlice); ++i) + _model->setSensitiveParameterValue(fusedIds[i], _sensitiveParamsFactor[sliceOffset + i] * value); } +} + +void Simulator::setSensitiveParameterValue(unsigned int idx, double value) +{ + if (idx >= _sensitiveParams.slices()) + return; - void Simulator::setSensitiveParameterValue(const ParameterId& id, double value) + ParameterId const* const paramIds = _sensitiveParams[idx]; + const util::SlicedVector::size_type sliceOffset = _sensitiveParams.sliceOffset(idx); + + for (unsigned int i = 0; i < _sensitiveParams.sliceSize(idx); ++i) { + const ParameterId& id = paramIds[i]; if (isSectionTimeParameter(id, _sectionTimes.size())) { _sectionTimes[id.section].setValue(value); @@ -846,905 +902,920 @@ namespace cadet } if (!_model) - return; - - util::SlicedVector::size_type temp; - util::SlicedVector::size_type linearIndex; - util::SlicedVector::size_type idxSlice = _sensitiveParams.findElementAndSlice(id, temp, linearIndex); - if (idxSlice < _sensitiveParams.slices()) - { - ParameterId const* const fusedIds = _sensitiveParams[idxSlice]; - const util::SlicedVector::size_type sliceOffset = _sensitiveParams.sliceOffset(idxSlice); - - // Noramlize parameter value - value /= _sensitiveParamsFactor[linearIndex]; + continue; - // Take care of linear factors - // @todo add constant offset to linear combination - for (unsigned int i = 0; i < _sensitiveParams.sliceSize(idxSlice); ++i) - _model->setSensitiveParameterValue(fusedIds[i], _sensitiveParamsFactor[sliceOffset + i] * value); - } + // Take care of linear factors + // @todo add constant offset to linear combination + _model->setSensitiveParameterValue(id, _sensitiveParamsFactor[sliceOffset + i] * value); } +} - void Simulator::setSensitiveParameterValue(unsigned int idx, double value) - { - if (idx >= _sensitiveParams.slices()) - return; +void Simulator::setSensitiveParameterFactors(unsigned int idx, double const* factors) +{ + if (idx >= _sensitiveParams.slices()) + return; - ParameterId const* const paramIds = _sensitiveParams[idx]; - const util::SlicedVector::size_type sliceOffset = _sensitiveParams.sliceOffset(idx); + ParameterId const* const paramIds = _sensitiveParams[idx]; + const util::SlicedVector::size_type sliceOffset = _sensitiveParams.sliceOffset(idx); - for (unsigned int i = 0; i < _sensitiveParams.sliceSize(idx); ++i) - { - const ParameterId& id = paramIds[i]; - if (isSectionTimeParameter(id, _sectionTimes.size())) - { - _sectionTimes[id.section].setValue(value); - // Do not exit here, since model can also contain instances of SECTION_TIMES - } + for (unsigned int i = 0; i < _sensitiveParams.sliceSize(idx); ++i) + { + // Update the linear factor + _sensitiveParamsFactor[sliceOffset + i] = factors[i]; - if (!_model) - continue; + const ParameterId& id = paramIds[i]; + setSectionTimesSensitive(id, i, factors[i]); - // Take care of linear factors - // @todo add constant offset to linear combination - _model->setSensitiveParameterValue(id, _sensitiveParamsFactor[sliceOffset + i] * value); - } + if (_model) + _model->setSensitiveParameter(id, i, factors[i]); } +} - void Simulator::setSensitiveParameterFactors(unsigned int idx, double const* factors) +void Simulator::setParameterValue(const ParameterId& id, double value) +{ + if (isSectionTimeParameter(id, _sectionTimes.size())) { - if (idx >= _sensitiveParams.slices()) - return; + _sectionTimes[id.section].setValue(value); + // Do not exit here, since model can also contain instances of SECTION_TIMES + } - ParameterId const* const paramIds = _sensitiveParams[idx]; - const util::SlicedVector::size_type sliceOffset = _sensitiveParams.sliceOffset(idx); + if (_model) + _model->setParameter(id, value); +} - for (unsigned int i = 0; i < _sensitiveParams.sliceSize(idx); ++i) - { - // Update the linear factor - _sensitiveParamsFactor[sliceOffset + i] = factors[i]; +void Simulator::setSolutionTimes(const std::vector& solutionTimes) +{ + _solutionTimes = solutionTimes; +} - const ParameterId& id = paramIds[i]; - setSectionTimesSensitive(id, i, factors[i]); +const std::vector& Simulator::getSolutionTimes() const +{ + return _solutionTimes; +} - if (_model) - _model->setSensitiveParameter(id, i, factors[i]); - } - } +void Simulator::setSectionTimes(const std::vector& sectionTimes) +{ + setSectionTimes(sectionTimes, std::vector(sectionTimes.size() - 1, false)); +} - void Simulator::setParameterValue(const ParameterId& id, double value) - { - if (isSectionTimeParameter(id, _sectionTimes.size())) - { - _sectionTimes[id.section].setValue(value); - // Do not exit here, since model can also contain instances of SECTION_TIMES - } +void Simulator::setSectionTimes(const std::vector& sectionTimes, const std::vector& sectionContinuity) +{ + // Ensure that at least one section is defined + if (sectionTimes.size() < 2) + throw std::invalid_argument("At least one section has to be specified!"); - if (_model) - _model->setParameter(id, value); - } + // Ensure that all section start times are smaller than their end times + for (std::size_t i = 0; i < sectionTimes.size() - 1; ++i) + if (sectionTimes[i] > sectionTimes[i + 1]) + throw InvalidParameterException( + "The end time of each section must be greater than its start time (failed for section " + + std::to_string(i) + ")!"); - void Simulator::setSolutionTimes(const std::vector& solutionTimes) + // Send section times to models + if (_model) { - _solutionTimes = solutionTimes; + bool* const secCont = new bool[sectionContinuity.size()]; + std::copy(sectionContinuity.begin(), sectionContinuity.end(), secCont); + _model->setSectionTimes(sectionTimes.data(), secCont, sectionTimes.size() - 1); + delete[] secCont; } - const std::vector& Simulator::getSolutionTimes() const - { - return _solutionTimes; - } + // Copy section times into AD (active) data type + _sectionTimes.clear(); + _sectionTimes.reserve(sectionTimes.size()); + for (std::size_t i = 0; i < sectionTimes.size(); ++i) + _sectionTimes.push_back(sectionTimes[i]); - void Simulator::setSectionTimes(const std::vector& sectionTimes) - { - setSectionTimes(sectionTimes, std::vector(sectionTimes.size() - 1, false)); - } + _sectionContinuity = sectionContinuity; - void Simulator::setSectionTimes(const std::vector& sectionTimes, const std::vector& sectionContinuity) + // Set AD sensitivities + unsigned int globalIdx = 0; + for (unsigned int i = 0; i < _sensitiveParams.slices(); ++i) { - // Ensure that at least one section is defined - if (sectionTimes.size() < 2) - throw std::invalid_argument("At least one section has to be specified!"); - - // Ensure that all section start times are smaller than their end times - for (std::size_t i = 0; i < sectionTimes.size() - 1; ++i) - if (sectionTimes[i] > sectionTimes[i + 1]) - throw InvalidParameterException("The end time of each section must be greater than its start time (failed for section " + std::to_string(i) + ")!"); + ParameterId const* const ids = _sensitiveParams[i]; - // Send section times to models - if (_model) + for (unsigned int j = 0; j < _sensitiveParams.sliceSize(i); ++j, ++globalIdx) { - bool* const secCont = new bool[sectionContinuity.size()]; - std::copy(sectionContinuity.begin(), sectionContinuity.end(), secCont); - _model->setSectionTimes(sectionTimes.data(), secCont, sectionTimes.size() - 1); - delete[] secCont; - } - - // Copy section times into AD (active) data type - _sectionTimes.clear(); - _sectionTimes.reserve(sectionTimes.size()); - for (std::size_t i = 0; i < sectionTimes.size(); ++i) - _sectionTimes.push_back(sectionTimes[i]); - - _sectionContinuity = sectionContinuity; - - // Set AD sensitivities - unsigned int globalIdx = 0; - for (unsigned int i = 0; i < _sensitiveParams.slices(); ++i) - { - ParameterId const* const ids = _sensitiveParams[i]; - - for (unsigned int j = 0; j < _sensitiveParams.sliceSize(i); ++j, ++globalIdx) - { - setSectionTimesSensitive(ids[j], i, _sensitiveParamsFactor[globalIdx]); - } + setSectionTimesSensitive(ids[j], i, _sensitiveParamsFactor[globalIdx]); } } +} - void Simulator::setSolutionRecorder(ISolutionRecorder* recorder) +void Simulator::setSolutionRecorder(ISolutionRecorder* recorder) +{ + _solRecorder = recorder; + if (_solRecorder) { - _solRecorder = recorder; - if (_solRecorder) - { - _solRecorder->prepare(NVEC_LENGTH(_vecStateY), _sensitiveParams.slices(), _solutionTimes.size()); - _model->reportSolutionStructure(*_solRecorder); - } + _solRecorder->prepare(NVEC_LENGTH(_vecStateY), _sensitiveParams.slices(), _solutionTimes.size()); + _model->reportSolutionStructure(*_solRecorder); } +} - void Simulator::integrate() - { - // In this function the model is integrated by IDAS from the SUNDIALS package. - // The authors of IDAS recommend to restart the time integrator when a discontinuity - // is encountered (see https://computation.llnl.gov/casc/sundials/support/notes.html#disc). - // The sectionTime (together with the sectionContinuity) array indicates such - // discontinuitites and the solver is restarted accordingly. This also requires - // the computation of consistent initial values for each restart. - - // This sets up the tbb thread limiter - // TBB can use up to _nThreads but it may use fewer +void Simulator::integrate() +{ + // In this function the model is integrated by IDAS from the SUNDIALS package. + // The authors of IDAS recommend to restart the time integrator when a discontinuity + // is encountered (see https://computation.llnl.gov/casc/sundials/support/notes.html#disc). + // The sectionTime (together with the sectionContinuity) array indicates such + // discontinuitites and the solver is restarted accordingly. This also requires + // the computation of consistent initial values for each restart. + + // This sets up the tbb thread limiter + // TBB can use up to _nThreads but it may use fewer #ifdef CADET_PARALLELIZE - #ifdef CADET_TBB_GLOBALCTRL - tbb::global_control tbbGlobalControl(tbb::global_control::max_allowed_parallelism, (_nThreads > 0) ? _nThreads : tbb::this_task_arena::max_concurrency()); - #else - tbb::task_scheduler_init init(tbb::task_scheduler_init::deferred); - if (_nThreads > 0) - init.initialize(_nThreads); - else - init.initialize(tbb::task_scheduler_init::default_num_threads()); - #endif - _model->setupParallelization(tbb::this_task_arena::max_concurrency()); +#ifdef CADET_TBB_GLOBALCTRL + tbb::global_control tbbGlobalControl(tbb::global_control::max_allowed_parallelism, + (_nThreads > 0) ? _nThreads : tbb::this_task_arena::max_concurrency()); +#else + tbb::task_scheduler_init init(tbb::task_scheduler_init::deferred); + if (_nThreads > 0) + init.initialize(_nThreads); + else + init.initialize(tbb::task_scheduler_init::default_num_threads()); +#endif + _model->setupParallelization(tbb::this_task_arena::max_concurrency()); #else - _model->setupParallelization(1); + _model->setupParallelization(1); #endif - // Set number of threads in SUNDIALS OpenMP-enabled implementation + // Set number of threads in SUNDIALS OpenMP-enabled implementation #ifdef CADET_SUNDIALS_OPENMP - if (_vecStateY) - NVec_SetThreads(_vecStateY, _nThreads); - if (_vecStateYdot) - NVec_SetThreads(_vecStateYdot, _nThreads); + if (_vecStateY) + NVec_SetThreads(_vecStateY, _nThreads); + if (_vecStateYdot) + NVec_SetThreads(_vecStateYdot, _nThreads); - for (unsigned int i = 0; i < _sensitiveParams.slices(); ++i) - { - NVec_SetThreads(_vecFwdYs[i], _nThreads); - NVec_SetThreads(_vecFwdYsDot[i], _nThreads); - } + for (unsigned int i = 0; i < _sensitiveParams.slices(); ++i) + { + NVec_SetThreads(_vecFwdYs[i], _nThreads); + NVec_SetThreads(_vecFwdYsDot[i], _nThreads); + } #endif - // Set number of AD directions - // @todo This is problematic if multiple Simulators are run concurrently! + // Set number of AD directions + // @todo This is problematic if multiple Simulators are run concurrently! #if defined(ACTIVE_SFAD) || defined(ACTIVE_SETFAD) - LOG(Debug) << "Setting AD directions from " << ad::getDirections() << " to " << numSensitivityAdDirections() + _model->requiredADdirs(); - if (numSensitivityAdDirections() + _model->requiredADdirs() > ad::getMaxDirections()) - throw InvalidParameterException("Requested " + std::to_string(numSensitivityAdDirections() + _model->requiredADdirs()) + " AD directions, but only " - + std::to_string(ad::getMaxDirections()) + " are supported"); - - ad::setDirections(numSensitivityAdDirections() + _model->requiredADdirs()); + LOG(Debug) << "Setting AD directions from " << ad::getDirections() << " to " + << numSensitivityAdDirections() + _model->requiredADdirs(); + if (numSensitivityAdDirections() + _model->requiredADdirs() > ad::getMaxDirections()) + throw InvalidParameterException( + "Requested " + std::to_string(numSensitivityAdDirections() + _model->requiredADdirs()) + + " AD directions, but only " + std::to_string(ad::getMaxDirections()) + " are supported"); + + ad::setDirections(numSensitivityAdDirections() + _model->requiredADdirs()); #endif - if (_notification) - _notification->timeIntegrationStart(); + if (_notification) + _notification->timeIntegrationStart(); - _timerIntegration.start(); + _timerIntegration.start(); - // Setup AD vectors by model - _model->prepareADvectors(AdJacobianParams{_vecADres, _vecADy, numSensitivityAdDirections()}); + // Setup AD vectors by model + _model->prepareADvectors(AdJacobianParams{_vecADres, _vecADy, numSensitivityAdDirections()}); - std::vector::const_iterator it; - double tOut = 0.0; + std::vector::const_iterator it; + double tOut = 0.0; - const bool writeAtUserTimes = _solutionTimes.size() > 0; - const bool wantSensitivities = _sensitiveParams.slices() > 0; + const bool writeAtUserTimes = _solutionTimes.size() > 0; + const bool wantSensitivities = _sensitiveParams.slices() > 0; - LOG(Debug) << "#MaxNewton: " << _maxNewtonIter << ", #MaxErrTestFail: " << _maxErrorTestFail << ", #MaxConvTestFail: " << _maxConvTestFail; - if (wantSensitivities) - { - LOG(Debug) << "Sensitvities in error test: " << _sensErrorTestEnabled << ", #MaxNewtonSens: " << _maxNewtonIterSens; - } + LOG(Debug) << "#MaxNewton: " << _maxNewtonIter << ", #MaxErrTestFail: " << _maxErrorTestFail + << ", #MaxConvTestFail: " << _maxConvTestFail; + if (wantSensitivities) + { + LOG(Debug) << "Sensitvities in error test: " << _sensErrorTestEnabled + << ", #MaxNewtonSens: " << _maxNewtonIterSens; + } - if (_solRecorder) - { - _solRecorder->notifyIntegrationStart(NVEC_LENGTH(_vecStateY), _sensitiveParams.slices(), _solutionTimes.size()); - _model->reportSolutionStructure(*_solRecorder); - } + if (_solRecorder) + { + _solRecorder->notifyIntegrationStart(NVEC_LENGTH(_vecStateY), _sensitiveParams.slices(), _solutionTimes.size()); + _model->reportSolutionStructure(*_solRecorder); + } - // Decide whether to use user specified solution output times (IDA_NORMAL) - // or internal integrator steps (IDA_ONE_STEP) - int idaTask = IDA_ONE_STEP; - if (writeAtUserTimes) - { - idaTask = IDA_NORMAL; - } + // Decide whether to use user specified solution output times (IDA_NORMAL) + // or internal integrator steps (IDA_ONE_STEP) + int idaTask = IDA_ONE_STEP; + if (writeAtUserTimes) + { + idaTask = IDA_NORMAL; + } - LOG(Debug) << "Integration span: [" << static_cast(_sectionTimes[0]) << ", " << static_cast(_sectionTimes.back()) << "] sections"; + LOG(Debug) << "Integration span: [" << static_cast(_sectionTimes[0]) << ", " + << static_cast(_sectionTimes.back()) << "] sections"; - if (writeAtUserTimes) - { - LOG(Debug) << "Solution time span: [" << _solutionTimes[0] << ", " << _solutionTimes.back() << "]"; - } + if (writeAtUserTimes) + { + LOG(Debug) << "Solution time span: [" << _solutionTimes[0] << ", " << _solutionTimes.back() << "]"; + } + + double curT = static_cast(_sectionTimes[0]); + _curSec = 0; + const double tEnd = writeAtUserTimes ? _solutionTimes.back() : static_cast(_sectionTimes.back()); + while (curT < tEnd) + { + // Get smallest index with t_i >= curT (t_i being a _sectionTimes element) + // This will return i if curT == _sectionTimes[i], which effectively advances + // the index if required + _curSec = getNextSection(curT, _curSec); + const double startTime = static_cast(_sectionTimes[_curSec]); - double curT = static_cast(_sectionTimes[0]); - _curSec = 0; - const double tEnd = writeAtUserTimes ? _solutionTimes.back() : static_cast(_sectionTimes.back()); - while (curT < tEnd) + // Determine continuous time slice + unsigned int skip = 1; // Always finish the current section + for (std::size_t i = _curSec; i < _sectionTimes.size() - 2; ++i) { - // Get smallest index with t_i >= curT (t_i being a _sectionTimes element) - // This will return i if curT == _sectionTimes[i], which effectively advances - // the index if required - _curSec = getNextSection(curT, _curSec); - const double startTime = static_cast(_sectionTimes[_curSec]); - - // Determine continuous time slice - unsigned int skip = 1; // Always finish the current section - for (std::size_t i = _curSec; i < _sectionTimes.size() - 2; ++i) - { - if (!_sectionContinuity[i]) - break; + if (!_sectionContinuity[i]) + break; - // This is a continuous section transition, so we don't need to - // restart the integrator and just integrate for a longer time - ++skip; - } + // This is a continuous section transition, so we don't need to + // restart the integrator and just integrate for a longer time + ++skip; + } - const double endTime = writeAtUserTimes ? std::min(static_cast(_sectionTimes[_curSec + skip]), tEnd) : static_cast(_sectionTimes[_curSec + skip]); - curT = startTime; + const double endTime = writeAtUserTimes ? std::min(static_cast(_sectionTimes[_curSec + skip]), tEnd) + : static_cast(_sectionTimes[_curSec + skip]); + curT = startTime; - LOG(Debug) << " ###### SECTION " << _curSec << " from " << startTime << " to " << endTime; + LOG(Debug) << " ###### SECTION " << _curSec << " from " << startTime << " to " << endTime; - // IDAS Step 7.3: Set the initial step size - const double stepSize = _initStepSize.size() > 1 ? _initStepSize[_curSec] : _initStepSize[0]; - IDASetInitStep(_idaMemBlock, stepSize); + // IDAS Step 7.3: Set the initial step size + const double stepSize = _initStepSize.size() > 1 ? _initStepSize[_curSec] : _initStepSize[0]; + IDASetInitStep(_idaMemBlock, stepSize); - // IDAS Step 7.4: Set the stop time - IDASetStopTime(_idaMemBlock, endTime); + // IDAS Step 7.4: Set the stop time + IDASetStopTime(_idaMemBlock, endTime); - // Update Jacobian - _model->notifyDiscontinuousSectionTransition(curT, _curSec, ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, AdJacobianParams{_vecADres, _vecADy, numSensitivityAdDirections()}); + // Update Jacobian + _model->notifyDiscontinuousSectionTransition( + curT, _curSec, ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, + AdJacobianParams{_vecADres, _vecADy, numSensitivityAdDirections()}); - // Compute consistent initial values - LOG(Debug) << "---====--- CONSISTENCY ---====--- "; - const double consPrev = _model->residualNorm(SimulationTime{curT, _curSec}, ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}); - LOG(Debug) << " ==========> Consistency error prev: " << consPrev; + // Compute consistent initial values + LOG(Debug) << "---====--- CONSISTENCY ---====--- "; + const double consPrev = _model->residualNorm( + SimulationTime{curT, _curSec}, ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}); + LOG(Debug) << " ==========> Consistency error prev: " << consPrev; - if (!_skipConsistencyStateY && (_consistentInitMode != ConsistentInitialization::None)) + if (!_skipConsistencyStateY && (_consistentInitMode != ConsistentInitialization::None)) + { + const ConsistentInitialization mode = currentConsistentInitMode(_consistentInitMode, _curSec); + if (mode == ConsistentInitialization::Full) { - const ConsistentInitialization mode = currentConsistentInitMode(_consistentInitMode, _curSec); - if (mode == ConsistentInitialization::Full) - { - _model->consistentInitialConditions(SimulationTime{curT, _curSec}, SimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, - AdJacobianParams{_vecADres, _vecADy, numSensitivityAdDirections()}, _algTol); - - const double consPost = _model->residualNorm(SimulationTime{curT, _curSec}, ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}); - LOG(Debug) << " ==========> Consistency error post Full: " << consPost; - } - else if (mode == ConsistentInitialization::Lean) - { - _model->leanConsistentInitialConditions(SimulationTime{curT, _curSec}, SimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, - AdJacobianParams{_vecADres, _vecADy, numSensitivityAdDirections()}, _algTol); - - const double consPost = _model->residualNorm(SimulationTime{curT, _curSec}, ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}); - LOG(Debug) << " ==========> Consistency error post Lean: " << consPost; - } - else - { - LOG(Debug) << " ==========> Consistent initialization NOT performed (mode " << to_string(_consistentInitMode) << ")"; - } - - LOG(Debug) << "y = " << log::VectorPtr(NVEC_DATA(_vecStateY), _model->numDofs()) << ";"; - LOG(Debug) << "yDot = " << log::VectorPtr(NVEC_DATA(_vecStateYdot), _model->numDofs()) << ";"; - LOG(Debug) << "Contains NaN: y = " << hasNaN(_vecStateY) << " yDot = " << hasNaN(_vecStateYdot); + _model->consistentInitialConditions( + SimulationTime{curT, _curSec}, SimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, + AdJacobianParams{_vecADres, _vecADy, numSensitivityAdDirections()}, _algTol); + + const double consPost = + _model->residualNorm(SimulationTime{curT, _curSec}, + ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}); + LOG(Debug) << " ==========> Consistency error post Full: " << consPost; } - _skipConsistencyStateY = false; - - if (wantSensitivities && !_skipConsistencySensitivity && (_consistentInitModeSens != ConsistentInitialization::None)) + else if (mode == ConsistentInitialization::Lean) { -#ifdef CADET_DEBUG - const std::vector sensYdbg = convertNVectorToStdVectorPtrs(_vecFwdYs, _sensitiveParams.slices()); - const std::vector sensYdotDbg = convertNVectorToStdVectorPtrs(_vecFwdYsDot, _sensitiveParams.slices()); + _model->leanConsistentInitialConditions( + SimulationTime{curT, _curSec}, SimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, + AdJacobianParams{_vecADres, _vecADy, numSensitivityAdDirections()}, _algTol); + + const double consPost = + _model->residualNorm(SimulationTime{curT, _curSec}, + ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}); + LOG(Debug) << " ==========> Consistency error post Lean: " << consPost; + } + else + { + LOG(Debug) << " ==========> Consistent initialization NOT performed (mode " + << to_string(_consistentInitMode) << ")"; + } - std::vector norms(_sensitiveParams.slices(), 0.0); - std::vector temp(_model->numDofs(), 0.0); - _model->residualSensFwdNorm(_sensitiveParams.slices(), SimulationTime{curT, _curSec}, ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, - sensYdbg, sensYdotDbg, norms.data(), _vecADres, temp.data()); + LOG(Debug) << "y = " << log::VectorPtr(NVEC_DATA(_vecStateY), _model->numDofs()) << ";"; + LOG(Debug) << "yDot = " << log::VectorPtr(NVEC_DATA(_vecStateYdot), _model->numDofs()) << ";"; + LOG(Debug) << "Contains NaN: y = " << hasNaN(_vecStateY) << " yDot = " << hasNaN(_vecStateYdot); + } + _skipConsistencyStateY = false; - LOG(Debug) << " ==========> Sens consistency error prev: " << norms; + if (wantSensitivities && !_skipConsistencySensitivity && + (_consistentInitModeSens != ConsistentInitialization::None)) + { +#ifdef CADET_DEBUG + const std::vector sensYdbg = + convertNVectorToStdVectorPtrs(_vecFwdYs, _sensitiveParams.slices()); + const std::vector sensYdotDbg = + convertNVectorToStdVectorPtrs(_vecFwdYsDot, _sensitiveParams.slices()); + + std::vector norms(_sensitiveParams.slices(), 0.0); + std::vector temp(_model->numDofs(), 0.0); + _model->residualSensFwdNorm(_sensitiveParams.slices(), SimulationTime{curT, _curSec}, + ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, sensYdbg, + sensYdotDbg, norms.data(), _vecADres, temp.data()); + + LOG(Debug) << " ==========> Sens consistency error prev: " << norms; #endif - const ConsistentInitialization mode = currentConsistentInitMode(_consistentInitModeSens, _curSec); - if (mode == ConsistentInitialization::Full) - { - // Compute consistent initial conditions for sensitivity subsystems - std::vector sensY = convertNVectorToStdVectorPtrs(_vecFwdYs, _sensitiveParams.slices()); - std::vector sensYdot = convertNVectorToStdVectorPtrs(_vecFwdYsDot, _sensitiveParams.slices()); - _model->consistentInitialSensitivity(SimulationTime{curT, _curSec}, ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, sensY, sensYdot, _vecADres, _vecADy); + const ConsistentInitialization mode = currentConsistentInitMode(_consistentInitModeSens, _curSec); + if (mode == ConsistentInitialization::Full) + { + // Compute consistent initial conditions for sensitivity subsystems + std::vector sensY = + convertNVectorToStdVectorPtrs(_vecFwdYs, _sensitiveParams.slices()); + std::vector sensYdot = + convertNVectorToStdVectorPtrs(_vecFwdYsDot, _sensitiveParams.slices()); + _model->consistentInitialSensitivity( + SimulationTime{curT, _curSec}, + ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, sensY, sensYdot, _vecADres, + _vecADy); #ifdef CADET_DEBUG - _model->residualSensFwdNorm(_sensitiveParams.slices(), SimulationTime{curT, _curSec}, ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, - sensYdbg, sensYdotDbg, norms.data(), _vecADres, temp.data()); + _model->residualSensFwdNorm(_sensitiveParams.slices(), SimulationTime{curT, _curSec}, + ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, + sensYdbg, sensYdotDbg, norms.data(), _vecADres, temp.data()); - LOG(Debug) << " ==========> Sens consistency error post Full: " << norms; + LOG(Debug) << " ==========> Sens consistency error post Full: " << norms; #endif - } - else if (mode == ConsistentInitialization::Lean) - { - // Compute consistent initial conditions for sensitivity subsystems - std::vector sensY = convertNVectorToStdVectorPtrs(_vecFwdYs, _sensitiveParams.slices()); - std::vector sensYdot = convertNVectorToStdVectorPtrs(_vecFwdYsDot, _sensitiveParams.slices()); - _model->leanConsistentInitialSensitivity(SimulationTime{curT, _curSec}, ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, sensY, sensYdot, _vecADres, _vecADy); + } + else if (mode == ConsistentInitialization::Lean) + { + // Compute consistent initial conditions for sensitivity subsystems + std::vector sensY = + convertNVectorToStdVectorPtrs(_vecFwdYs, _sensitiveParams.slices()); + std::vector sensYdot = + convertNVectorToStdVectorPtrs(_vecFwdYsDot, _sensitiveParams.slices()); + _model->leanConsistentInitialSensitivity( + SimulationTime{curT, _curSec}, + ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, sensY, sensYdot, _vecADres, + _vecADy); #ifdef CADET_DEBUG - _model->residualSensFwdNorm(_sensitiveParams.slices(), SimulationTime{curT, _curSec}, ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, - sensYdbg, sensYdotDbg, norms.data(), _vecADres, temp.data()); + _model->residualSensFwdNorm(_sensitiveParams.slices(), SimulationTime{curT, _curSec}, + ConstSimulationState{NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)}, + sensYdbg, sensYdotDbg, norms.data(), _vecADres, temp.data()); - LOG(Debug) << " ==========> Sens consistency error post Lean: " << norms; + LOG(Debug) << " ==========> Sens consistency error post Lean: " << norms; #endif - } - else - { - LOG(Debug) << " ==========> Sens consistent initialization NOT performed (mode " << to_string(_consistentInitModeSens) << ")"; - } + } + else + { + LOG(Debug) << " ==========> Sens consistent initialization NOT performed (mode " + << to_string(_consistentInitModeSens) << ")"; + } #ifdef CADET_DEBUG - for (std::size_t j = 0; j < norms.size(); ++j) - { - LOG(Debug) << "sensY[" << j << "] = " << log::VectorPtr(NVEC_DATA(_vecFwdYs[j]), _model->numDofs()) << ";"; - LOG(Debug) << "sensYdot[" << j << "] = " << log::VectorPtr(NVEC_DATA(_vecFwdYsDot[j]), _model->numDofs()) << ";"; - LOG(Debug) << "Contains NaN: sensY[" << j << "] = " << hasNaN(_vecFwdYs[j]) << " sensYdot[" << j << "] = " << hasNaN(_vecFwdYsDot[j]); - } -#endif + for (std::size_t j = 0; j < norms.size(); ++j) + { + LOG(Debug) << "sensY[" << j + << "] = " << log::VectorPtr(NVEC_DATA(_vecFwdYs[j]), _model->numDofs()) << ";"; + LOG(Debug) << "sensYdot[" << j + << "] = " << log::VectorPtr(NVEC_DATA(_vecFwdYsDot[j]), _model->numDofs()) << ";"; + LOG(Debug) << "Contains NaN: sensY[" << j << "] = " << hasNaN(_vecFwdYs[j]) << " sensYdot[" << j + << "] = " << hasNaN(_vecFwdYsDot[j]); } - _skipConsistencySensitivity = false; +#endif + } + _skipConsistencySensitivity = false; - // Notify user and check for user abort - if (_notification) + // Notify user and check for user abort + if (_notification) + { + const double progress = + (curT - static_cast(_sectionTimes[0])) / (tEnd - static_cast(_sectionTimes[0])); + if (!_notification->timeIntegrationSection(_curSec, curT, NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot), + progress)) { - const double progress = (curT - static_cast(_sectionTimes[0])) / (tEnd - static_cast(_sectionTimes[0])); - if (!_notification->timeIntegrationSection(_curSec, curT, NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot), progress)) - { - _lastIntTime = _timerIntegration.stop(); - return; - } + _lastIntTime = _timerIntegration.stop(); + return; } + } - // IDAS Step 5.2: Re-initialization of the solver - IDAReInit(_idaMemBlock, startTime, _vecStateY, _vecStateYdot); - if (wantSensitivities) - IDASensReInit(_idaMemBlock, IDA_STAGGERED, _vecFwdYs, _vecFwdYsDot); - - // Inititalize the IDA solver flag - int solverFlag = IDA_SUCCESS; + // IDAS Step 5.2: Re-initialization of the solver + IDAReInit(_idaMemBlock, startTime, _vecStateY, _vecStateYdot); + if (wantSensitivities) + IDASensReInit(_idaMemBlock, IDA_STAGGERED, _vecFwdYs, _vecFwdYsDot); - if (writeAtUserTimes) - { - // Write initial conditions only if desired by user - if (_curSec == 0 && _solutionTimes.front() == curT) - writeSolution(curT); + // Inititalize the IDA solver flag + int solverFlag = IDA_SUCCESS; - // Initialize iterator and forward it to the first solution time that lies inside the current section - it = _solutionTimes.begin(); - while ((*it) <= startTime) ++it; - } - else - { - // Always write initial conditions if solutions are written at integration times - if (_curSec == 0) writeSolution(curT); + if (writeAtUserTimes) + { + // Write initial conditions only if desired by user + if (_curSec == 0 && _solutionTimes.front() == curT) + writeSolution(curT); + + // Initialize iterator and forward it to the first solution time that lies inside the current section + it = _solutionTimes.begin(); + while ((*it) <= startTime) + ++it; + } + else + { + // Always write initial conditions if solutions are written at integration times + if (_curSec == 0) + writeSolution(curT); - // Here tOut - only during the first call to IDASolve - specifies the direction - // and rough scale of the independent variable, see IDAS Guide p.33 - tOut = endTime; - } + // Here tOut - only during the first call to IDASolve - specifies the direction + // and rough scale of the independent variable, see IDAS Guide p.33 + tOut = endTime; + } - // Main loop which integrates the system until reaching the end time of the current section - // or until an error occures - while ((solverFlag == IDA_SUCCESS) || (solverFlag == IDA_ROOT_RETURN)) + // Main loop which integrates the system until reaching the end time of the current section + // or until an error occures + while ((solverFlag == IDA_SUCCESS) || (solverFlag == IDA_ROOT_RETURN)) + { + // Update tOut if we write solutions at user specified times + if (writeAtUserTimes) { - // Update tOut if we write solutions at user specified times - if (writeAtUserTimes) - { - // Check if user specified times are sufficiently long. - // otherwise integrate till IDA_TSTOP_RETURN - if (it == _solutionTimes.end()) - break; - else - tOut = *it; - } + // Check if user specified times are sufficiently long. + // otherwise integrate till IDA_TSTOP_RETURN + if (it == _solutionTimes.end()) + break; + else + tOut = *it; + } - // IDA Step 11: Advance solution in time - solverFlag = IDASolve(_idaMemBlock, tOut, &curT, _vecStateY, _vecStateYdot, idaTask); - LOG(Debug) << "Solve from " << curT << " to " << tOut << " => " - << (solverFlag == IDA_SUCCESS ? "IDA_SUCCESS" : "") << (solverFlag == IDA_TSTOP_RETURN ? "IDA_TSTOP_RETURN" : ""); + // IDA Step 11: Advance solution in time + solverFlag = IDASolve(_idaMemBlock, tOut, &curT, _vecStateY, _vecStateYdot, idaTask); + LOG(Debug) << "Solve from " << curT << " to " << tOut << " => " + << (solverFlag == IDA_SUCCESS ? "IDA_SUCCESS" : "") + << (solverFlag == IDA_TSTOP_RETURN ? "IDA_TSTOP_RETURN" : ""); #ifdef CADET_DEBUG - { - long nTimeSteps = 0; - IDAGetNumSteps(_idaMemBlock, &nTimeSteps); + { + long nTimeSteps = 0; + IDAGetNumSteps(_idaMemBlock, &nTimeSteps); - double curStepSize = 0.0; - IDAGetCurrentStep(_idaMemBlock, &curStepSize); + double curStepSize = 0.0; + IDAGetCurrentStep(_idaMemBlock, &curStepSize); - double lastStepSize = 0.0; - IDAGetLastStep(_idaMemBlock, &lastStepSize); + double lastStepSize = 0.0; + IDAGetLastStep(_idaMemBlock, &lastStepSize); - long nResEvals = 0; - IDAGetNumResEvals(_idaMemBlock, &nResEvals); + long nResEvals = 0; + IDAGetNumResEvals(_idaMemBlock, &nResEvals); - long nErrTestFail = 0; - IDAGetNumErrTestFails(_idaMemBlock, &nErrTestFail); + long nErrTestFail = 0; + IDAGetNumErrTestFails(_idaMemBlock, &nErrTestFail); - long nNonLin = 0; - IDAGetNumNonlinSolvIters(_idaMemBlock, &nNonLin); + long nNonLin = 0; + IDAGetNumNonlinSolvIters(_idaMemBlock, &nNonLin); - long nConvFail = 0; - IDAGetNumNonlinSolvConvFails(_idaMemBlock, &nConvFail); + long nConvFail = 0; + IDAGetNumNonlinSolvConvFails(_idaMemBlock, &nConvFail); - LOG(Debug) << "=== #Steps: " << nTimeSteps << "\n=== #Residual evals: " << nResEvals << "\n=== #Error test fails: " << nErrTestFail - << "\n=== #Newton iters: " << nNonLin << "\n=== #Conv test fails: " << nConvFail << "\n=== Last step size: " << lastStepSize - << "\n=== Next step size: " << curStepSize; + LOG(Debug) << "=== #Steps: " << nTimeSteps << "\n=== #Residual evals: " << nResEvals + << "\n=== #Error test fails: " << nErrTestFail << "\n=== #Newton iters: " << nNonLin + << "\n=== #Conv test fails: " << nConvFail << "\n=== Last step size: " << lastStepSize + << "\n=== Next step size: " << curStepSize; - if (wantSensitivities) - { - long nSensResEvals = 0; - IDAGetSensNumResEvals(_idaMemBlock, &nSensResEvals); + if (wantSensitivities) + { + long nSensResEvals = 0; + IDAGetSensNumResEvals(_idaMemBlock, &nSensResEvals); - long nSensErrTestFails = 0; - IDAGetSensNumErrTestFails(_idaMemBlock, &nSensErrTestFails); + long nSensErrTestFails = 0; + IDAGetSensNumErrTestFails(_idaMemBlock, &nSensErrTestFails); - long nSensNonLin = 0; - IDAGetSensNumNonlinSolvIters(_idaMemBlock, &nSensNonLin); + long nSensNonLin = 0; + IDAGetSensNumNonlinSolvIters(_idaMemBlock, &nSensNonLin); - long nSensConvFail = 0; - IDAGetSensNumNonlinSolvConvFails(_idaMemBlock, &nSensConvFail); + long nSensConvFail = 0; + IDAGetSensNumNonlinSolvConvFails(_idaMemBlock, &nSensConvFail); - LOG(Debug) << "=== #Sens residual evals: " << nSensResEvals << "\n=== #Sens error test fails: " << nSensErrTestFails - << "\n=== #Sens Newton iters: " << nSensNonLin << "\n=== #Sens conv test fails: " << nSensConvFail; - } + LOG(Debug) << "=== #Sens residual evals: " << nSensResEvals + << "\n=== #Sens error test fails: " << nSensErrTestFails + << "\n=== #Sens Newton iters: " << nSensNonLin + << "\n=== #Sens conv test fails: " << nSensConvFail; } - #endif - switch (solverFlag) - { - case IDA_SUCCESS: - // tOut was reached - - // Extract sensitivity information from IDA (required for consistent initialization - // and output of sensitivities) - if (wantSensitivities) - { - IDAGetSens(_idaMemBlock, &curT, _vecFwdYs); - IDAGetSensDky(_idaMemBlock, curT, 1, _vecFwdYsDot); - } - writeSolution(curT); - - if (writeAtUserTimes) - ++it; + } +#endif + switch (solverFlag) + { + case IDA_SUCCESS: + // tOut was reached - // Notify user and check for user abort - if (_notification) - { - const double progress = (curT - static_cast(_sectionTimes[0])) / (tEnd - static_cast(_sectionTimes[0])); - if (!_notification->timeIntegrationStep(_curSec, curT, NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot), progress)) - { - _lastIntTime = _timerIntegration.stop(); - return; - } - } - break; - case IDA_ROOT_RETURN: - // A root was found - // Eventually call some routine - break; - case IDA_TSTOP_RETURN: - // Extract sensitivity information from IDA (required for consistent initialization - // and output of sensitivities) - if (wantSensitivities) - { - IDAGetSens(_idaMemBlock, &curT, _vecFwdYs); - IDAGetSensDky(_idaMemBlock, curT, 1, _vecFwdYsDot); - } + // Extract sensitivity information from IDA (required for consistent initialization + // and output of sensitivities) + if (wantSensitivities) + { + IDAGetSens(_idaMemBlock, &curT, _vecFwdYs); + IDAGetSensDky(_idaMemBlock, curT, 1, _vecFwdYsDot); + } + writeSolution(curT); - // Section end time was reached (in previous step) - if (!writeAtUserTimes && (endTime == static_cast(_sectionTimes.back()))) - { - // Write a solution for the ultimate endTime in the last section, - // when we write at integration times. - writeSolution(curT); - } + if (writeAtUserTimes) + ++it; - // Notify user and check for user abort - if (_notification) + // Notify user and check for user abort + if (_notification) + { + const double progress = + (curT - static_cast(_sectionTimes[0])) / (tEnd - static_cast(_sectionTimes[0])); + if (!_notification->timeIntegrationStep(_curSec, curT, NVEC_DATA(_vecStateY), + NVEC_DATA(_vecStateYdot), progress)) { - const double progress = (curT - static_cast(_sectionTimes[0])) / (tEnd - static_cast(_sectionTimes[0])); - if (!_notification->timeIntegrationStep(_curSec, curT, NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot), progress)) - { - _lastIntTime = _timerIntegration.stop(); - return; - } + _lastIntTime = _timerIntegration.stop(); + return; } - break; - default: - _lastIntTime = _timerIntegration.stop(); + } + break; + case IDA_ROOT_RETURN: + // A root was found + // Eventually call some routine + break; + case IDA_TSTOP_RETURN: + // Extract sensitivity information from IDA (required for consistent initialization + // and output of sensitivities) + if (wantSensitivities) + { + IDAGetSens(_idaMemBlock, &curT, _vecFwdYs); + IDAGetSensDky(_idaMemBlock, curT, 1, _vecFwdYsDot); + } - // An error occured - const std::string errorFlag = getIDAReturnFlagName(solverFlag); - LOG(Error) << "IDASolve returned " << errorFlag << " at t = " << curT; + // Section end time was reached (in previous step) + if (!writeAtUserTimes && (endTime == static_cast(_sectionTimes.back()))) + { + // Write a solution for the ultimate endTime in the last section, + // when we write at integration times. + writeSolution(curT); + } - if (_notification) + // Notify user and check for user abort + if (_notification) + { + const double progress = + (curT - static_cast(_sectionTimes[0])) / (tEnd - static_cast(_sectionTimes[0])); + if (!_notification->timeIntegrationStep(_curSec, curT, NVEC_DATA(_vecStateY), + NVEC_DATA(_vecStateYdot), progress)) { - const double progress = (curT - static_cast(_sectionTimes[0])) / (tEnd - static_cast(_sectionTimes[0])); - _notification->timeIntegrationError(errorFlag.c_str(), _curSec, curT, progress); + _lastIntTime = _timerIntegration.stop(); + return; } + } + break; + default: + _lastIntTime = _timerIntegration.stop(); - throw IntegrationException(std::string("Error in IDASolve: ") + errorFlag + std::string(" at t = ") + std::to_string(curT)); //todo might not be necessary - break; - } // switch - - } // while + // An error occured + const std::string errorFlag = getIDAReturnFlagName(solverFlag); + LOG(Error) << "IDASolve returned " << errorFlag << " at t = " << curT; - } // for (_sec ...) + if (_notification) + { + const double progress = + (curT - static_cast(_sectionTimes[0])) / (tEnd - static_cast(_sectionTimes[0])); + _notification->timeIntegrationError(errorFlag.c_str(), _curSec, curT, progress); + } - _lastIntTime = _timerIntegration.stop(); + throw IntegrationException(std::string("Error in IDASolve: ") + errorFlag + std::string(" at t = ") + + std::to_string(curT)); // todo might not be necessary + break; + } // switch - if (_notification) - _notification->timeIntegrationEnd(); - } + } // while - double const* Simulator::getLastSolution(unsigned int& len) const - { - len = NVEC_LENGTH(_vecStateY); - return NVEC_DATA(_vecStateY); - } + } // for (_sec ...) - double const* Simulator::getLastSolutionDerivative(unsigned int& len) const - { - len = NVEC_LENGTH(_vecStateYdot); - return NVEC_DATA(_vecStateYdot); - } + _lastIntTime = _timerIntegration.stop(); - std::vector Simulator::getLastSensitivities(unsigned int& len) const - { - return convertNVectorToStdVectorConstPtrs(len, _vecFwdYs, _sensitiveParams.slices()); - } - - std::vector Simulator::getLastSensitivityDerivatives(unsigned int& len) const - { - return convertNVectorToStdVectorConstPtrs(len, _vecFwdYsDot, _sensitiveParams.slices()); - } - - void Simulator::configureTimeIntegrator(double relTol, double absTol, double initStepSize, unsigned int maxSteps, double maxStepSize) - { - _absTol.clear(); - _absTol.push_back(absTol); + if (_notification) + _notification->timeIntegrationEnd(); +} - _relTol = relTol; - _maxSteps = maxSteps; - _maxStepSize = maxStepSize; +double const* Simulator::getLastSolution(unsigned int& len) const +{ + len = NVEC_LENGTH(_vecStateY); + return NVEC_DATA(_vecStateY); +} - _initStepSize.clear(); - _initStepSize.push_back(initStepSize); - } +double const* Simulator::getLastSolutionDerivative(unsigned int& len) const +{ + len = NVEC_LENGTH(_vecStateYdot); + return NVEC_DATA(_vecStateYdot); +} - void Simulator::configureTimeIntegrator(double relTol, double absTol, const std::vector& initStepSizes, unsigned int maxSteps, double maxStepSize) - { - _absTol.clear(); - _absTol.push_back(absTol); +std::vector Simulator::getLastSensitivities(unsigned int& len) const +{ + return convertNVectorToStdVectorConstPtrs(len, _vecFwdYs, _sensitiveParams.slices()); +} - _relTol = relTol; - _maxSteps = maxSteps; - _maxStepSize = maxStepSize; - _initStepSize = initStepSizes; - } +std::vector Simulator::getLastSensitivityDerivatives(unsigned int& len) const +{ + return convertNVectorToStdVectorConstPtrs(len, _vecFwdYsDot, _sensitiveParams.slices()); +} - void Simulator::configure(IParameterProvider& paramProvider) - { - paramProvider.pushScope("time_integrator"); +void Simulator::configureTimeIntegrator(double relTol, double absTol, double initStepSize, unsigned int maxSteps, + double maxStepSize) +{ + _absTol.clear(); + _absTol.push_back(absTol); - if (paramProvider.exists("USE_MODIFIED_NEWTON")) - _modifiedNewton = paramProvider.getBool("USE_MODIFIED_NEWTON"); - else - _modifiedNewton = false; + _relTol = relTol; + _maxSteps = maxSteps; + _maxStepSize = maxStepSize; - _absTol.clear(); - if (paramProvider.isArray("ABSTOL")) - _absTol = paramProvider.getDoubleArray("ABSTOL"); - else - _absTol.push_back(paramProvider.getDouble("ABSTOL")); + _initStepSize.clear(); + _initStepSize.push_back(initStepSize); +} - _relTol = paramProvider.getDouble("RELTOL"); - _algTol = paramProvider.getDouble("ALGTOL"); - _maxSteps = paramProvider.getInt("MAX_STEPS"); +void Simulator::configureTimeIntegrator(double relTol, double absTol, const std::vector& initStepSizes, + unsigned int maxSteps, double maxStepSize) +{ + _absTol.clear(); + _absTol.push_back(absTol); - if (paramProvider.exists("MAX_STEP_SIZE")) - _maxStepSize = paramProvider.getDouble("MAX_STEP_SIZE"); + _relTol = relTol; + _maxSteps = maxSteps; + _maxStepSize = maxStepSize; + _initStepSize = initStepSizes; +} - _initStepSize.clear(); - if (paramProvider.isArray("INIT_STEP_SIZE")) - _initStepSize = paramProvider.getDoubleArray("INIT_STEP_SIZE"); - else - _initStepSize.push_back(paramProvider.getDouble("INIT_STEP_SIZE")); +void Simulator::configure(IParameterProvider& paramProvider) +{ + paramProvider.pushScope("time_integrator"); - if (paramProvider.exists("RELTOL_SENS")) - _relTolS = paramProvider.getDouble("RELTOL_SENS"); - else - _relTolS = _relTol; + if (paramProvider.exists("USE_MODIFIED_NEWTON")) + _modifiedNewton = paramProvider.getBool("USE_MODIFIED_NEWTON"); + else + _modifiedNewton = false; - if (paramProvider.exists("ERRORTEST_SENS")) - _sensErrorTestEnabled = paramProvider.getBool("ERRORTEST_SENS"); + _absTol.clear(); + if (paramProvider.isArray("ABSTOL")) + _absTol = paramProvider.getDoubleArray("ABSTOL"); + else + _absTol.push_back(paramProvider.getDouble("ABSTOL")); - if (paramProvider.exists("MAX_NEWTON_ITER")) - _maxNewtonIter = paramProvider.getInt("MAX_NEWTON_ITER"); + _relTol = paramProvider.getDouble("RELTOL"); + _algTol = paramProvider.getDouble("ALGTOL"); + _maxSteps = paramProvider.getInt("MAX_STEPS"); - if (paramProvider.exists("MAX_ERRTEST_FAIL")) - _maxErrorTestFail = paramProvider.getInt("MAX_ERRTEST_FAIL"); + if (paramProvider.exists("MAX_STEP_SIZE")) + _maxStepSize = paramProvider.getDouble("MAX_STEP_SIZE"); - if (paramProvider.exists("MAX_CONVTEST_FAIL")) - _maxConvTestFail = paramProvider.getInt("MAX_CONVTEST_FAIL"); + _initStepSize.clear(); + if (paramProvider.isArray("INIT_STEP_SIZE")) + _initStepSize = paramProvider.getDoubleArray("INIT_STEP_SIZE"); + else + _initStepSize.push_back(paramProvider.getDouble("INIT_STEP_SIZE")); - if (paramProvider.exists("MAX_NEWTON_ITER_SENS")) - _maxNewtonIterSens = paramProvider.getInt("MAX_NEWTON_ITER_SENS"); + if (paramProvider.exists("RELTOL_SENS")) + _relTolS = paramProvider.getDouble("RELTOL_SENS"); + else + _relTolS = _relTol; - paramProvider.popScope(); + if (paramProvider.exists("ERRORTEST_SENS")) + _sensErrorTestEnabled = paramProvider.getBool("ERRORTEST_SENS"); - if (paramProvider.exists("NTHREADS")) - { - // Ensure numThreads >= 0 - const int numThreads = paramProvider.getInt("NTHREADS"); - _nThreads = std::max(numThreads, 0); - } - else - _nThreads = 0; + if (paramProvider.exists("MAX_NEWTON_ITER")) + _maxNewtonIter = paramProvider.getInt("MAX_NEWTON_ITER"); - _solutionTimes.clear(); - if (paramProvider.exists("USER_SOLUTION_TIMES")) - _solutionTimes = paramProvider.getDoubleArray("USER_SOLUTION_TIMES"); + if (paramProvider.exists("MAX_ERRTEST_FAIL")) + _maxErrorTestFail = paramProvider.getInt("MAX_ERRTEST_FAIL"); - if (paramProvider.exists("CONSISTENT_INIT_MODE")) - _consistentInitMode = toConsistentInitialization(paramProvider.getInt("CONSISTENT_INIT_MODE")); + if (paramProvider.exists("MAX_CONVTEST_FAIL")) + _maxConvTestFail = paramProvider.getInt("MAX_CONVTEST_FAIL"); - if (paramProvider.exists("CONSISTENT_INIT_MODE_SENS")) - _consistentInitModeSens = toConsistentInitialization(paramProvider.getInt("CONSISTENT_INIT_MODE_SENS")); + if (paramProvider.exists("MAX_NEWTON_ITER_SENS")) + _maxNewtonIterSens = paramProvider.getInt("MAX_NEWTON_ITER_SENS"); - // @todo: Read more configuration values - } + paramProvider.popScope(); - void Simulator::reconfigure(IParameterProvider& paramProvider) + if (paramProvider.exists("NTHREADS")) { - configure(paramProvider); + // Ensure numThreads >= 0 + const int numThreads = paramProvider.getInt("NTHREADS"); + _nThreads = std::max(numThreads, 0); } + else + _nThreads = 0; - void Simulator::setSensitivityErrorTolerance(double relTol, double const* absTol) - { - _relTolS = relTol; - _absTolS.clear(); + _solutionTimes.clear(); + if (paramProvider.exists("USER_SOLUTION_TIMES")) + _solutionTimes = paramProvider.getDoubleArray("USER_SOLUTION_TIMES"); - const unsigned int nSens = _sensitiveParams.slices(); - if (_idaMemBlock && (nSens > 0) && _vecFwdYs && absTol) - { - _absTolS.insert(_absTolS.end(), absTol, absTol + nSens); + if (paramProvider.exists("CONSISTENT_INIT_MODE")) + _consistentInitMode = toConsistentInitialization(paramProvider.getInt("CONSISTENT_INIT_MODE")); - // Set sensitivity integration tolerances - IDASensSStolerances(_idaMemBlock, _relTolS, _absTolS.data()); - } - } + if (paramProvider.exists("CONSISTENT_INIT_MODE_SENS")) + _consistentInitModeSens = toConsistentInitialization(paramProvider.getInt("CONSISTENT_INIT_MODE_SENS")); - void Simulator::setRelativeErrorToleranceSens(double relTol) - { - _relTolS = relTol; - if (_idaMemBlock && (_sensitiveParams.slices() > 0) && _vecFwdYs) - { - // Set sensitivity integration tolerances - IDASensSStolerances(_idaMemBlock, _relTolS, _absTolS.data()); - } - } + // @todo: Read more configuration values +} - void Simulator::setRelativeErrorTolerance(double relTol) - { - _relTol = relTol; - updateMainErrorTolerances(); - } +void Simulator::reconfigure(IParameterProvider& paramProvider) +{ + configure(paramProvider); +} - void Simulator::setAbsoluteErrorTolerance(double absTol) - { - _absTol.clear(); - _absTol.push_back(absTol); - updateMainErrorTolerances(); - } +void Simulator::setSensitivityErrorTolerance(double relTol, double const* absTol) +{ + _relTolS = relTol; + _absTolS.clear(); - void Simulator::setAbsoluteErrorTolerance(const std::vector& absTol) + const unsigned int nSens = _sensitiveParams.slices(); + if (_idaMemBlock && (nSens > 0) && _vecFwdYs && absTol) { - _absTol = absTol; - updateMainErrorTolerances(); - } + _absTolS.insert(_absTolS.end(), absTol, absTol + nSens); - void Simulator::setInitialStepSize(double stepSize) - { - _initStepSize.clear(); - _initStepSize.push_back(stepSize); + // Set sensitivity integration tolerances + IDASensSStolerances(_idaMemBlock, _relTolS, _absTolS.data()); } +} - void Simulator::setInitialStepSize(const std::vector& stepSize) +void Simulator::setRelativeErrorToleranceSens(double relTol) +{ + _relTolS = relTol; + if (_idaMemBlock && (_sensitiveParams.slices() > 0) && _vecFwdYs) { - _initStepSize = stepSize; + // Set sensitivity integration tolerances + IDASensSStolerances(_idaMemBlock, _relTolS, _absTolS.data()); } +} - void Simulator::setMaximumSteps(unsigned int maxSteps) - { - _maxSteps = maxSteps; - if (_idaMemBlock) - IDASetMaxNumSteps(_idaMemBlock, _maxSteps); - } +void Simulator::setRelativeErrorTolerance(double relTol) +{ + _relTol = relTol; + updateMainErrorTolerances(); +} - void Simulator::setMaximumStepSize(double maxStepSize) - { - _maxStepSize = std::max(0.0, maxStepSize); - if (_idaMemBlock) - IDASetMaxStep(_idaMemBlock, _maxStepSize); - } +void Simulator::setAbsoluteErrorTolerance(double absTol) +{ + _absTol.clear(); + _absTol.push_back(absTol); + updateMainErrorTolerances(); +} - void Simulator::setSensitivityErrorControl(bool enabled) - { - _sensErrorTestEnabled = enabled; - if (_idaMemBlock && (_sensitiveParams.slices() > 0) && _vecFwdYs) - IDASetSensErrCon(_idaMemBlock, enabled); - } +void Simulator::setAbsoluteErrorTolerance(const std::vector& absTol) +{ + _absTol = absTol; + updateMainErrorTolerances(); +} - void Simulator::setMaxNewtonIteration(unsigned int nIter) - { - _maxNewtonIter = nIter; - if (_idaMemBlock) - IDASetMaxNonlinIters(_idaMemBlock, nIter); - } +void Simulator::setInitialStepSize(double stepSize) +{ + _initStepSize.clear(); + _initStepSize.push_back(stepSize); +} - void Simulator::setMaxErrorTestFails(unsigned int nFails) - { - _maxErrorTestFail = nFails; - if (_idaMemBlock) - IDASetMaxErrTestFails(_idaMemBlock, nFails); - } +void Simulator::setInitialStepSize(const std::vector& stepSize) +{ + _initStepSize = stepSize; +} - void Simulator::setMaxConvergenceFails(unsigned int nFails) - { - _maxConvTestFail = nFails; - if (_idaMemBlock) - IDASetMaxConvFails(_idaMemBlock, nFails); - } +void Simulator::setMaximumSteps(unsigned int maxSteps) +{ + _maxSteps = maxSteps; + if (_idaMemBlock) + IDASetMaxNumSteps(_idaMemBlock, _maxSteps); +} - void Simulator::setMaxSensNewtonIteration(unsigned int nIter) - { - _maxNewtonIterSens = nIter; - if (_idaMemBlock && (_sensitiveParams.slices() > 0) && _vecFwdYs) - IDASetSensMaxNonlinIters(_idaMemBlock, nIter); - } +void Simulator::setMaximumStepSize(double maxStepSize) +{ + _maxStepSize = std::max(0.0, maxStepSize); + if (_idaMemBlock) + IDASetMaxStep(_idaMemBlock, _maxStepSize); +} +void Simulator::setSensitivityErrorControl(bool enabled) +{ + _sensErrorTestEnabled = enabled; + if (_idaMemBlock && (_sensitiveParams.slices() > 0) && _vecFwdYs) + IDASetSensErrCon(_idaMemBlock, enabled); +} +void Simulator::setMaxNewtonIteration(unsigned int nIter) +{ + _maxNewtonIter = nIter; + if (_idaMemBlock) + IDASetMaxNonlinIters(_idaMemBlock, nIter); +} - bool Simulator::reconfigureModel(IParameterProvider& paramProvider) - { - if (!_model) - return false; +void Simulator::setMaxErrorTestFails(unsigned int nFails) +{ + _maxErrorTestFail = nFails; + if (_idaMemBlock) + IDASetMaxErrTestFails(_idaMemBlock, nFails); +} - // Reconfigure the model - const bool success = _model->configure(paramProvider); +void Simulator::setMaxConvergenceFails(unsigned int nFails) +{ + _maxConvTestFail = nFails; + if (_idaMemBlock) + IDASetMaxConvFails(_idaMemBlock, nFails); +} - // Set all AD directions for parameter sensitivities again - resetSensParams(); +void Simulator::setMaxSensNewtonIteration(unsigned int nIter) +{ + _maxNewtonIterSens = nIter; + if (_idaMemBlock && (_sensitiveParams.slices() > 0) && _vecFwdYs) + IDASetSensMaxNonlinIters(_idaMemBlock, nIter); +} - return success; - } +bool Simulator::reconfigureModel(IParameterProvider& paramProvider) +{ + if (!_model) + return false; - bool Simulator::reconfigureModel(IParameterProvider& paramProvider, unsigned int unitOpIdx) - { - if (!_model) - return false; + // Reconfigure the model + const bool success = _model->configure(paramProvider); - // Reconfigure the model - const bool success = _model->configureModel(paramProvider, unitOpIdx); + // Set all AD directions for parameter sensitivities again + resetSensParams(); - // Set all AD directions for parameter sensitivities again - resetSensParams(); + return success; +} - return success; - } +bool Simulator::reconfigureModel(IParameterProvider& paramProvider, unsigned int unitOpIdx) +{ + if (!_model) + return false; - void Simulator::writeSolution(double t) - { - if (!_solRecorder) - return; + // Reconfigure the model + const bool success = _model->configureModel(paramProvider, unitOpIdx); - _solRecorder->beginTimestep(t); + // Set all AD directions for parameter sensitivities again + resetSensParams(); - _solRecorder->beginSolution(); - _model->reportSolution(*_solRecorder, NVEC_DATA(_vecStateY)); - _solRecorder->endSolution(); + return success; +} - _solRecorder->beginSolutionDerivative(); - _model->reportSolution(*_solRecorder, NVEC_DATA(_vecStateYdot)); - _solRecorder->endSolutionDerivative(); +void Simulator::writeSolution(double t) +{ + if (!_solRecorder) + return; - for (unsigned int i = 0; i < _sensitiveParams.slices(); ++i) - { - _solRecorder->beginSensitivity(*_sensitiveParams[i], i); - _model->reportSolution(*_solRecorder, NVEC_DATA(_vecFwdYs[i])); - _solRecorder->endSensitivity(*_sensitiveParams[i], i); + _solRecorder->beginTimestep(t); - _solRecorder->beginSensitivityDerivative(*_sensitiveParams[i], i); - _model->reportSolution(*_solRecorder, NVEC_DATA(_vecFwdYsDot[i])); - _solRecorder->endSensitivityDerivative(*_sensitiveParams[i], i); - } + _solRecorder->beginSolution(); + _model->reportSolution(*_solRecorder, NVEC_DATA(_vecStateY)); + _solRecorder->endSolution(); - _solRecorder->endTimestep(); - } + _solRecorder->beginSolutionDerivative(); + _model->reportSolution(*_solRecorder, NVEC_DATA(_vecStateYdot)); + _solRecorder->endSolutionDerivative(); - unsigned int Simulator::getNextSection(double t, unsigned int startIdx) const + for (unsigned int i = 0; i < _sensitiveParams.slices(); ++i) { - if (t < _sectionTimes[startIdx]) - return -1; + _solRecorder->beginSensitivity(*_sensitiveParams[i], i); + _model->reportSolution(*_solRecorder, NVEC_DATA(_vecFwdYs[i])); + _solRecorder->endSensitivity(*_sensitiveParams[i], i); - for (std::size_t i = startIdx; i < _sectionTimes.size() - 1; ++i) - { - if (_sectionTimes[i] >= t) - return i; - } - - return -1; + _solRecorder->beginSensitivityDerivative(*_sensitiveParams[i], i); + _model->reportSolution(*_solRecorder, NVEC_DATA(_vecFwdYsDot[i])); + _solRecorder->endSensitivityDerivative(*_sensitiveParams[i], i); } - unsigned int Simulator::getCurrentSection(double t) const - { - //TODO: Use binary search - - for (std::size_t i = _curSec; i < _sectionTimes.size() - 1; ++i) - { - if ((t >= _sectionTimes[i]) && (t <= _sectionTimes[i+1])) - return i; - } + _solRecorder->endTimestep(); +} +unsigned int Simulator::getNextSection(double t, unsigned int startIdx) const +{ + if (t < _sectionTimes[startIdx]) return -1; - } - IModelSystem* Simulator::model() CADET_NOEXCEPT + for (std::size_t i = startIdx; i < _sectionTimes.size() - 1; ++i) { - return _model; + if (_sectionTimes[i] >= t) + return i; } - IModelSystem const* Simulator::model() const CADET_NOEXCEPT - { - return _model; - } + return -1; +} - unsigned int Simulator::numDofs() const CADET_NOEXCEPT - { - if (_model) - return _model->numDofs(); - return 0; - } +unsigned int Simulator::getCurrentSection(double t) const +{ + // TODO: Use binary search - void Simulator::setNumThreads(unsigned int nThreads) CADET_NOEXCEPT + for (std::size_t i = _curSec; i < _sectionTimes.size() - 1; ++i) { - _nThreads = nThreads; + if ((t >= _sectionTimes[i]) && (t <= _sectionTimes[i + 1])) + return i; } - void Simulator::setNotificationCallback(INotificationCallback* nc) CADET_NOEXCEPT - { - _notification = nc; - } + return -1; +} + +IModelSystem* Simulator::model() CADET_NOEXCEPT +{ + return _model; +} + +IModelSystem const* Simulator::model() const CADET_NOEXCEPT +{ + return _model; +} + +unsigned int Simulator::numDofs() const CADET_NOEXCEPT +{ + if (_model) + return _model->numDofs(); + return 0; +} + +void Simulator::setNumThreads(unsigned int nThreads) CADET_NOEXCEPT +{ + _nThreads = nThreads; +} + +void Simulator::setNotificationCallback(INotificationCallback* nc) CADET_NOEXCEPT +{ + _notification = nc; +} } // namespace cadet diff --git a/src/libcadet/SimulatorImpl.hpp b/src/libcadet/SimulatorImpl.hpp index c730b8ec0..23b3dca28 100644 --- a/src/libcadet/SimulatorImpl.hpp +++ b/src/libcadet/SimulatorImpl.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Simulator implementation. */ @@ -36,25 +36,24 @@ int residualDaeWrapper(double t, N_Vector y, N_Vector yDot, N_Vector res, void* int linearSolveWrapper(IDAMem IDA_mem, N_Vector rhs, N_Vector weight, N_Vector yCur, N_Vector yDotCur, N_Vector resCur); -int jacobianUpdateWrapper(IDAMem IDA_mem, N_Vector y, N_Vector yDot, N_Vector res, N_Vector tempv1, N_Vector tempv2, N_Vector tempv3); +int jacobianUpdateWrapper(IDAMem IDA_mem, N_Vector y, N_Vector yDot, N_Vector res, N_Vector tempv1, N_Vector tempv2, + N_Vector tempv3); -int residualSensWrapper(int ns, double t, N_Vector y, N_Vector yDot, N_Vector res, - N_Vector* yS, N_Vector* ySDot, N_Vector* resS, - void *userData, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3); +int residualSensWrapper(int ns, double t, N_Vector y, N_Vector yDot, N_Vector res, N_Vector* yS, N_Vector* ySDot, + N_Vector* resS, void* userData, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3); -//int weightWrapper(N_Vector y, N_Vector ewt, void *user_data); +// int weightWrapper(N_Vector y, N_Vector ewt, void *user_data); class ISimulatableModel; /** * @brief Provides functionality to simulate a model using a time integrator - * @details This class is responsible for managing the time integration process + * @details This class is responsible for managing the time integration process * and holds all memory associated with that (e.g., state vectors). */ class Simulator : public ISimulator { public: - Simulator(); virtual ~Simulator() CADET_NOEXCEPT; @@ -68,7 +67,8 @@ class Simulator : public ISimulator virtual void setSensitiveParameterValue(const ParameterId& id, double value); virtual void setSensitiveParameterValue(unsigned int idx, double value); virtual void setSensitiveParameterFactors(unsigned int idx, double const* factors); - virtual void setSensitiveParameter(ParameterId const* ids, double const* diffFactors, unsigned int numParams, double absTolS); + virtual void setSensitiveParameter(ParameterId const* ids, double const* diffFactors, unsigned int numParams, + double absTolS); virtual void setSensitiveParameter(ParameterId const* ids, unsigned int numParams, double absTolS); virtual void setParameterValue(const ParameterId& id, double value); @@ -83,13 +83,15 @@ class Simulator : public ISimulator virtual void setInitialCondition(IParameterProvider& paramProvider); virtual void applyInitialCondition(double const* const initState); virtual void applyInitialCondition(double const* const initState, double const* const initStateDot); - virtual void applyInitialConditionFwdSensitivities(double const * const* const initSens, double const * const* const initSensDot); + virtual void applyInitialConditionFwdSensitivities(double const* const* const initSens, + double const* const* const initSensDot); virtual void skipConsistentInitialization(); virtual void setConsistentInitialization(ConsistentInitialization ci); virtual void setConsistentInitializationSens(ConsistentInitialization ci); virtual void initializeFwdSensitivities(); - virtual void initializeFwdSensitivities(double const * const* const initSens, double const * const* const initSensDot); + virtual void initializeFwdSensitivities(double const* const* const initSens, + double const* const* const initSensDot); virtual void setSolutionRecorder(ISolutionRecorder* recorder); @@ -103,14 +105,19 @@ class Simulator : public ISimulator virtual void configure(IParameterProvider& paramProvider); virtual void reconfigure(IParameterProvider& paramProvider); - virtual void configureTimeIntegrator(double relTol, double absTol, double initStepSize, unsigned int maxSteps, double maxStepSize); - virtual void configureTimeIntegrator(double relTol, double absTol, const std::vector& initStepSizes, unsigned int maxSteps, double maxStepSize); + virtual void configureTimeIntegrator(double relTol, double absTol, double initStepSize, unsigned int maxSteps, + double maxStepSize); + virtual void configureTimeIntegrator(double relTol, double absTol, const std::vector& initStepSizes, + unsigned int maxSteps, double maxStepSize); virtual void setSensitivityErrorTolerance(double relTol, double const* absTol); virtual void setRelativeErrorTolerance(double relTol); virtual void setAbsoluteErrorTolerance(double absTol); virtual void setAbsoluteErrorTolerance(const std::vector& absTol); - virtual void setAlgebraicErrorTolerance(double algTol) CADET_NOEXCEPT { _algTol = algTol; } + virtual void setAlgebraicErrorTolerance(double algTol) CADET_NOEXCEPT + { + _algTol = algTol; + } virtual void setInitialStepSize(double stepSize); virtual void setInitialStepSize(const std::vector& stepSize); virtual void setMaximumSteps(unsigned int maxSteps); @@ -131,12 +138,18 @@ class Simulator : public ISimulator virtual unsigned int numDofs() const CADET_NOEXCEPT; virtual void setNumThreads(unsigned int nThreads) CADET_NOEXCEPT; - virtual double lastSimulationDuration() const CADET_NOEXCEPT { return _lastIntTime; } - virtual double totalSimulationDuration() const CADET_NOEXCEPT { return _timerIntegration.totalElapsedTime(); } + virtual double lastSimulationDuration() const CADET_NOEXCEPT + { + return _lastIntTime; + } + virtual double totalSimulationDuration() const CADET_NOEXCEPT + { + return _timerIntegration.totalElapsedTime(); + } virtual void setNotificationCallback(INotificationCallback* nc) CADET_NOEXCEPT; -protected: +protected: /** * @brief Clears memory used for time integration of a model */ @@ -150,7 +163,7 @@ class Simulator : public ISimulator /** * @brief Computes the index of the next section from the given time @p t - * @details Returns the lowest index @c i with @f$ t_i \geq t @f$, where + * @details Returns the lowest index @c i with @f$ t_i \geq t @f$, where * @f$ t_i @f$ is an element of @c _sectionTimes. * @param [in] t Current time * @param [in] startIdx Index of the first section the search should begin with @@ -179,7 +192,7 @@ class Simulator : public ISimulator /** * @brief Sets up IDAS for computing sensitivities * @details Main objective is handing the initial values of the sensitivity subsystems over to IDAS - * + * * @param [in] nSens Number of sensitivities */ void postFwdSensInit(unsigned int nSens); @@ -188,13 +201,17 @@ class Simulator : public ISimulator * @brief Returns the number of AD directions that are assigned to a parameter sensitivity * @return Total number of AD directions used for parameter sensitivities */ - inline unsigned int numSensitivityAdDirections() const { return _sensitiveParams.slices(); } + inline unsigned int numSensitivityAdDirections() const + { + return _sensitiveParams.slices(); + } /** * @brief Sets the SECTION_TIMES parameter sensitive that matches the given parameter @p id * @param [in] id Parameter Id of the sensitive parameter * @param [in] adDirection AD direction assigned to this parameter - * @param [in,out] adValue On entry value of the derivative in the given direction, on exit the (possibly corrected) value + * @param [in,out] adValue On entry value of the derivative in the given direction, on exit the (possibly corrected) + * value * @return @c true if the given parameter matches a SECTION_TIMES parameter, otherwise @c false */ bool setSectionTimesSensitive(const ParameterId& id, unsigned int adDirection, double adValue); @@ -217,15 +234,17 @@ class Simulator : public ISimulator friend int ::cadet::residualDaeWrapper(double t, N_Vector y, N_Vector yDot, N_Vector res, void* userData); - friend int ::cadet::linearSolveWrapper(IDAMem IDA_mem, N_Vector rhs, N_Vector weight, N_Vector yCur, N_Vector yDotCur, N_Vector resCur); + friend int ::cadet::linearSolveWrapper(IDAMem IDA_mem, N_Vector rhs, N_Vector weight, N_Vector yCur, + N_Vector yDotCur, N_Vector resCur); - friend int ::cadet::jacobianUpdateWrapper(IDAMem IDA_mem, N_Vector y, N_Vector yDot, N_Vector res, N_Vector tempv1, N_Vector tempv2, N_Vector tempv3); + friend int ::cadet::jacobianUpdateWrapper(IDAMem IDA_mem, N_Vector y, N_Vector yDot, N_Vector res, N_Vector tempv1, + N_Vector tempv2, N_Vector tempv3); -// friend int ::cadet::weightWrapper(N_Vector y, N_Vector ewt, void *user_data); + // friend int ::cadet::weightWrapper(N_Vector y, N_Vector ewt, void *user_data); - friend int ::cadet::residualSensWrapper(int ns, double t, N_Vector y, N_Vector yDot, N_Vector res, - N_Vector* yS, N_Vector* ySDot, N_Vector* resS, - void *userData, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3); + friend int ::cadet::residualSensWrapper(int ns, double t, N_Vector y, N_Vector yDot, N_Vector res, N_Vector* yS, + N_Vector* ySDot, N_Vector* resS, void* userData, N_Vector tmp1, + N_Vector tmp2, N_Vector tmp3); ISimulatableModel* _model; //!< Simulated model, not owned by the Simulator @@ -235,58 +254,62 @@ class Simulator : public ISimulator /** * @brief Determines whether the transition from section i to section i+1 is continuous. - * @details The solver will be reset only at discontinuous transitions. The i-th element - * corresponds to the transition from _sectionTimes[i+1] to _sectionTimes[i+2]. + * @details The solver will be reset only at discontinuous transitions. The i-th element + * corresponds to the transition from _sectionTimes[i+1] to _sectionTimes[i+2]. * Therefore size = nsec - 1. */ std::vector _sectionContinuity; - std::vector _solutionTimes; //!< Contains the time transformed user specified times for writing solutions to the output + std::vector + _solutionTimes; //!< Contains the time transformed user specified times for writing solutions to the output - N_Vector _vecStateY; //!< IDAS state vector - N_Vector _vecStateYdot; //!< IDAS state vector time derivative - N_Vector* _vecFwdYs; //!< IDAS sensitivities vector - N_Vector* _vecFwdYsDot; //!< IDAS sensitivities vector time derivative + N_Vector _vecStateY; //!< IDAS state vector + N_Vector _vecStateYdot; //!< IDAS state vector time derivative + N_Vector* _vecFwdYs; //!< IDAS sensitivities vector + N_Vector* _vecFwdYsDot; //!< IDAS sensitivities vector time derivative util::SlicedVector _sensitiveParams; //!< Stores (fused) sensitive parameters std::vector _sensitiveParamsFactor; //!< Stores the factors of the linear sensitive parameter combinations std::vector _sectionTimes; //!< Stores the AD variables used for SECTION_TIMES parameter derivatives - - double _relTolS; //!< Relative tolerance for forward sensitivity systems in the time integration + + double _relTolS; //!< Relative tolerance for forward sensitivity systems in the time integration std::vector _absTolS; //!< Absolute tolerances for forward sensitivity systems in the time integration std::vector _absTol; //!< Absolute tolerance for the original system in the time integration - double _relTol; //!< Relative tolerance for the original system in the time integration - double _algTol; //!< Tolerance for the solution of algebraic equations in the consistent initialization + double _relTol; //!< Relative tolerance for the original system in the time integration + double _algTol; //!< Tolerance for the solution of algebraic equations in the consistent initialization std::vector _initStepSize; //!< Initial step size for the time integrator - unsigned int _maxSteps; //!< Maximum number of time integration steps - double _maxStepSize; //!< Maximum time step size + unsigned int _maxSteps; //!< Maximum number of time integration steps + double _maxStepSize; //!< Maximum time step size unsigned int _nThreads; //!< Maximum number of threads CADET is allowed to use 0, disables maximum setting bool _modifiedNewton; //!< Determines whether modified or full Newton method is used - bool _sensErrorTestEnabled; //!< Determines whether forward sensitivity systems participate in the local time integration error test - unsigned int _maxNewtonIter; //!< Maximum number of Newton iterations for original DAE system - unsigned int _maxErrorTestFail; //!< Maximum number of local time integration error test failures - unsigned int _maxConvTestFail; //!< Maximum number of Newton iteration failures + bool _sensErrorTestEnabled; //!< Determines whether forward sensitivity systems participate in the local time + //!< integration error test + unsigned int _maxNewtonIter; //!< Maximum number of Newton iterations for original DAE system + unsigned int _maxErrorTestFail; //!< Maximum number of local time integration error test failures + unsigned int _maxConvTestFail; //!< Maximum number of Newton iteration failures unsigned int _maxNewtonIterSens; //!< Maximum number of Newton iterations for forward sensitivity systems SectionIdx _curSec; //!< Index of the current section - bool _skipConsistencyStateY; //!< Flag that determines whether the consistent initialization is skipped - bool _skipConsistencySensitivity; //!< Flag that determines whether the consistent initialization of the sensitivity systems is skipped + bool _skipConsistencyStateY; //!< Flag that determines whether the consistent initialization is skipped + bool _skipConsistencySensitivity; //!< Flag that determines whether the consistent initialization of the sensitivity + //!< systems is skipped ConsistentInitialization _consistentInitMode; //!< Mode that determines consistent initialization behavior - ConsistentInitialization _consistentInitModeSens; //!< Mode that determines consistent initialization behavior of the sensitivity systems + ConsistentInitialization + _consistentInitModeSens; //!< Mode that determines consistent initialization behavior of the sensitivity systems active* _vecADres; //!< Vector of AD datatypes for holding the residual - active* _vecADy; //!< Vector of AD datatypes for holding the state vector + active* _vecADy; //!< Vector of AD datatypes for holding the state vector Timer _timerIntegration; //!< Timer measuring the duration of the call to integrate() - double _lastIntTime; //!< Last simulation duration + double _lastIntTime; //!< Last simulation duration INotificationCallback* _notification; //!< Callback handler for notifications }; } // namespace cadet -#endif // LIBCADET_SIMULATOR_IMPL_HPP_ +#endif // LIBCADET_SIMULATOR_IMPL_HPP_ diff --git a/src/libcadet/SlicedVector.hpp b/src/libcadet/SlicedVector.hpp index 8934f4762..fcc1f2fac 100644 --- a/src/libcadet/SlicedVector.hpp +++ b/src/libcadet/SlicedVector.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines a SlicedVector which replaces vectors of vectors */ @@ -36,8 +36,7 @@ namespace util * and end of each slice are saved in a separate vector. * @tparam T Type of the saved data */ -template -class SlicedVector +template class SlicedVector { public: typedef typename std::vector::size_type size_type; @@ -45,8 +44,12 @@ class SlicedVector /** * @brief Creates an empty SlicedVector */ - SlicedVector() : _index(1, 0) { } - ~SlicedVector() CADET_NOEXCEPT { } + SlicedVector() : _index(1, 0) + { + } + ~SlicedVector() CADET_NOEXCEPT + { + } // Default copy and move mechanisms SlicedVector(const SlicedVector& cpy) = default; @@ -64,8 +67,11 @@ class SlicedVector * @brief Checks whether this SlicedVector is empty * @return @c true if it is empty, otherwise @c false */ - inline bool empty() const { return _index.size() == 1; } - + inline bool empty() const + { + return _index.size() == 1; + } + /** * @brief Clears the SlicedVector and deletes all stored items */ @@ -98,7 +104,7 @@ class SlicedVector } /** - * @brief Appends a given slice + * @brief Appends a given slice * @param [in] slice Slice to append */ inline void pushBackSlice(const std::vector& slice) @@ -108,7 +114,7 @@ class SlicedVector } /** - * @brief Appends a given slice + * @brief Appends a given slice * @param [in] data Pointer to the first element of the slice * @param [in] size Size of the slice */ @@ -125,7 +131,7 @@ class SlicedVector inline void pushBackInLastSlice(const T& value) { cadet_assert(!empty()); - + // Increase end index of last slice ++_index.back(); _values.push_back(value); @@ -168,21 +174,30 @@ class SlicedVector * @brief Returns the number of slices * @return Number of slices */ - inline size_type slices() const CADET_NOEXCEPT { return _index.size() - 1; } + inline size_type slices() const CADET_NOEXCEPT + { + return _index.size() - 1; + } /** * @brief Returns a pointer to the first element of a given slice * @param [in] idxSlice Index of the slice * @return Pointer to the first element of the given slice */ - inline T const* operator[](size_type idxSlice) const { return _values.data() + _index[idxSlice]; } + inline T const* operator[](size_type idxSlice) const + { + return _values.data() + _index[idxSlice]; + } /** * @brief Returns a pointer to the first element of a given slice * @param [in] idxSlice Index of the slice * @return Pointer to the first element of the given slice */ - inline T* operator[](size_type idxSlice) { return _values.data() + _index[idxSlice]; } + inline T* operator[](size_type idxSlice) + { + return _values.data() + _index[idxSlice]; + } /** * @brief Returns the element at the given position in the given slice @@ -190,7 +205,10 @@ class SlicedVector * @param [in] idxElem Index of the element within the slice * @return Element at the given index of the given slice */ - inline const T& operator()(size_type idxSlice, size_type idxElem) const { return _values[_index[idxSlice] + idxElem]; } + inline const T& operator()(size_type idxSlice, size_type idxElem) const + { + return _values[_index[idxSlice] + idxElem]; + } /** * @brief Returns the element at the given position in the given slice @@ -198,21 +216,30 @@ class SlicedVector * @param [in] idxElem Index of the element within the slice * @return Element at the given index of the given slice */ - inline T& operator()(size_type idxSlice, size_type idxElem) { return _values[_index[idxSlice] + idxElem]; } + inline T& operator()(size_type idxSlice, size_type idxElem) + { + return _values[_index[idxSlice] + idxElem]; + } /** * @brief Returns the element at the given linear index * @param [in] idx Linear index of the requested element * @return Element at the given linear index */ - inline T& native(size_type idx) { return _values[idx]; } + inline T& native(size_type idx) + { + return _values[idx]; + } /** * @brief Returns the element at the given linear index * @param [in] idx Linear index of the requested element * @return Element at the given linear index */ - inline const T& native(size_type idx) const { return _values[idx]; } + inline const T& native(size_type idx) const + { + return _values[idx]; + } /** * @brief Returns the element at the given position in the given slice @@ -220,7 +247,10 @@ class SlicedVector * @param [in] idxElem Index of the element within the slice * @return Element at the given index of the given slice */ - inline const T& at(size_type idxSlice, size_type idxElem) const { return _values[_index[idxSlice] + idxElem]; } + inline const T& at(size_type idxSlice, size_type idxElem) const + { + return _values[_index[idxSlice] + idxElem]; + } /** * @brief Returns the element at the given position in the given slice @@ -228,32 +258,41 @@ class SlicedVector * @param [in] idxElem Index of the element within the slice * @return Element at the given index of the given slice */ - inline T& at(size_type idxSlice, size_type idxElem) { return _values[_index[idxSlice] + idxElem]; } + inline T& at(size_type idxSlice, size_type idxElem) + { + return _values[_index[idxSlice] + idxElem]; + } /** * @brief Returns a pointer to the first element of a given slice * @param [in] idxSlice Index of the slice * @return Pointer to the first element of the given slice */ - inline T const* at(size_type idxSlice) const { return _values.data() + _index[idxSlice]; } + inline T const* at(size_type idxSlice) const + { + return _values.data() + _index[idxSlice]; + } /** * @brief Returns a pointer to the first element of a given slice * @param [in] idxSlice Index of the slice * @return Pointer to the first element of the given slice */ - inline T* at(size_type idxSlice) { return _values.data() + _index[idxSlice]; } + inline T* at(size_type idxSlice) + { + return _values.data() + _index[idxSlice]; + } /** * @brief Returns a pointer to the first element of the last slice * @return Pointer to the first element of the last slice */ - inline T const* back() const + inline T const* back() const { cadet_assert(!empty()); return &_values[_index[_index.size() - 2]]; } - + inline T* back() { cadet_assert(!empty()); @@ -265,26 +304,32 @@ class SlicedVector * @param [in] idxSlice Index of the slice * @return Number of elmenets in the slice */ - inline size_type sliceSize(size_type idxSlice) const { return _index[idxSlice + 1] - _index[idxSlice]; } + inline size_type sliceSize(size_type idxSlice) const + { + return _index[idxSlice + 1] - _index[idxSlice]; + } /** * @brief Returns the total number of stored items * @details All items in every slice are counted. * @return Total number of stored items */ - inline size_type size() const CADET_NOEXCEPT { return _values.size(); } + inline size_type size() const CADET_NOEXCEPT + { + return _values.size(); + } /** * @brief Finds the slice that contains a given item * @param [in] value Item to be found - * @return Index of the slice that contains the requested @p value, + * @return Index of the slice that contains the requested @p value, * or a value larger than slices() if @p value was not found */ inline size_type findSlice(const T& value) const { for (size_type i = 0; i < slices(); ++i) { - for (size_type j = _index[i]; j < _index[i+1]; ++j) + for (size_type j = _index[i]; j < _index[i + 1]; ++j) { if (_values[j] == value) { @@ -306,11 +351,11 @@ class SlicedVector { for (size_type i = 0; i < slices(); ++i) { - for (size_type j = _index[i]; j < _index[i+1]; ++j) + for (size_type j = _index[i]; j < _index[i + 1]; ++j) { if (_values[j] == value) { - len = _index[i+1] - _index[i]; + len = _index[i + 1] - _index[i]; return &_values[_index[i]]; } } @@ -323,14 +368,14 @@ class SlicedVector * @param [in] value Item to be found * @param [out] index Index of the item in the slice, if found * @param [out] linearIndex Index of the item in the linearized array, if found - * @return Index of the slice that contains the requested @p value, + * @return Index of the slice that contains the requested @p value, * or a value larger than slices() if @p value was not found */ inline size_type findElementAndSlice(const T& value, size_type& index, size_type& linearIndex) const { for (size_type i = 0; i < slices(); ++i) { - for (size_type j = _index[i]; j < _index[i+1]; ++j) + for (size_type j = _index[i]; j < _index[i + 1]; ++j) { if (_values[j] == value) { @@ -364,7 +409,10 @@ class SlicedVector * @param [in] idx Index of the slice * @return Offset of the given slice in the linearized storage */ - inline size_type sliceOffset(size_type idx) const { return _index[idx]; } + inline size_type sliceOffset(size_type idx) const + { + return _index[idx]; + } /** * @brief Reserves memory without initializing it @@ -426,11 +474,11 @@ class SlicedVector } protected: - std::vector _values; //!< Holds all values in a linearized fashion + std::vector _values; //!< Holds all values in a linearized fashion std::vector _index; //!< Holds starting indices of slices }; } // namespace util } // namespace cadet -#endif // LIBCADET_SLICEDVECTOR_HPP_ +#endif // LIBCADET_SLICEDVECTOR_HPP_ diff --git a/src/libcadet/Stencil.hpp b/src/libcadet/Stencil.hpp index 06159c2fd..0ea493546 100644 --- a/src/libcadet/Stencil.hpp +++ b/src/libcadet/Stencil.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides several numerical stencil implementations. */ @@ -24,186 +24,218 @@ namespace cadet { +/** + * @brief A stencil that works on the original state vector + * @details Supports strides on the state vector for non-consecutive ordering. + * Stencils are supposed to be a lightweight view into the state vector providing easy access to + * cells of one single component. + * @tparam T Underlying state element type + */ +template class StridedStencil +{ +public: /** - * @brief A stencil that works on the original state vector - * @details Supports strides on the state vector for non-consecutive ordering. - * Stencils are supposed to be a lightweight view into the state vector providing easy access to - * cells of one single component. - * @tparam T Underlying state element type + * @brief Creates a StridedStencil on the given array + * @param [in] data Pointer to original state vector + * @param [in] stride Stride in the state vector (i.e., number of elements between consecutive data items) */ - template - class StridedStencil - { - public: - /** - * @brief Creates a StridedStencil on the given array - * @param [in] data Pointer to original state vector - * @param [in] stride Stride in the state vector (i.e., number of elements between consecutive data items) - */ - StridedStencil(T const* const data, const unsigned int stride) : _data(data), _stride(stride) { } - - inline const T& operator[](const int idx) const { return _data[idx * static_cast(_stride)]; } - - /** - * @brief Advances the stencil to the next cell - */ - inline void advance() CADET_NOEXCEPT - { - _data += _stride; - } - - inline void advance(const T&) CADET_NOEXCEPT - { - advance(); - } - - inline StridedStencil& operator++() - { - advance(); - return *this; - } - - protected: - T const* _data; - const unsigned int _stride; - }; + StridedStencil(T const* const data, const unsigned int stride) : _data(data), _stride(stride) + { + } + inline const T& operator[](const int idx) const + { + return _data[idx * static_cast(_stride)]; + } /** - * @brief A stencil that caches its values into a separate array for spatial locality in memory - * @details Stencils are supposed to be a lightweight view into the state vector providing easy - * access to cells of one single component. The stencil size has to be odd. - * @tparam T Underlying state element type - * @tparam MemoryPoolType Memory pool used to allocate the cache of the stencil + * @brief Advances the stencil to the next cell */ - template - class CachingStencil - { - public: - - /** - * @brief Creates a CachingStencil with given size - * @details The @p size has to be odd. - * @param [in] size Size of the stencil - * @param [in] memPool Memory pool used for the stencil (e.g., ArrayPool) - */ - CachingStencil(const unsigned int size, MemoryPoolType& memPool) : _cache(memPool.template create(size)), _size(size), _offset(0u), _memPool(memPool) { } - CachingStencil(const unsigned int size, MemoryPoolType& memPool, unsigned int offset) : _cache(memPool.template create(size) + offset), _size(size), _offset(offset), _memPool(memPool) { } - CachingStencil(const std::initializer_list vals, MemoryPoolType& memPool) : CachingStencil(vals.size(), memPool) { initialize(vals); } - CachingStencil(const std::initializer_list vals, MemoryPoolType& memPool, unsigned int offset) : CachingStencil(vals.size(), memPool, offset) { initialize(vals); } - ~CachingStencil() CADET_NOEXCEPT { _memPool.template destroy(); } - - inline const T& operator[](const int idx) const - { - cadet_assert(idx >= -static_cast(_offset)); - cadet_assert(idx < static_cast(_size - _offset)); - return _cache[idx]; - } - inline T& operator[](const int idx) - { - cadet_assert(idx >= -static_cast(_offset)); - cadet_assert(idx < static_cast(_size - _offset)); - return _cache[idx]; - } - - inline const T& native(const unsigned int i) const - { - cadet_assert(i < _size); - return _cache[static_cast(i) - _offset]; - } - - inline void initialize(const std::initializer_list vals) - { - cadet_assert(vals.size() <= _size); - - T const* ptr = vals.begin(); - - for (int i = 0; i < static_cast(_size); ++i, ++ptr) - _cache[i - static_cast(_offset)] = *ptr; - } - - /** - * @brief Advances the stencil to the next cell - * @param val New volume average added to the stencil - */ - inline void advance(const T& val) CADET_NOEXCEPT - { - // Rotate cache backwards, that is, assign ..., _cache[0] = _cache[1], _cache[1] = _cache[2] - for (int i = -static_cast(_offset); i < static_cast(_size) - 1 - static_cast(_offset); ++i) - _cache[i] = _cache[i+1]; - - // Update cache - _cache[static_cast(_size) - static_cast(_offset) - 1] = val; - } - - protected: - T* const _cache; //!< Cache for stencil elements - const unsigned int _size; //!< Size of the stencil - const unsigned int _offset; //!< Offset of the pointer in the stencil - MemoryPoolType& _memPool; //!< Memory pool for the stencil - }; + inline void advance() CADET_NOEXCEPT + { + _data += _stride; + } + + inline void advance(const T&) CADET_NOEXCEPT + { + advance(); + } + + inline StridedStencil& operator++() + { + advance(); + return *this; + } +protected: + T const* _data; + const unsigned int _stride; +}; +/** + * @brief A stencil that caches its values into a separate array for spatial locality in memory + * @details Stencils are supposed to be a lightweight view into the state vector providing easy + * access to cells of one single component. The stencil size has to be odd. + * @tparam T Underlying state element type + * @tparam MemoryPoolType Memory pool used to allocate the cache of the stencil + */ +template class CachingStencil +{ +public: /** - * @brief Specialization of the CachingStencil for doubles - * @todo Is this specialization of the CachingStencil necessary? + * @brief Creates a CachingStencil with given size + * @details The @p size has to be odd. + * @param [in] size Size of the stencil + * @param [in] memPool Memory pool used for the stencil (e.g., ArrayPool) */ - template - class CachingStencil - { - public: - CachingStencil(const unsigned int size, MemoryPoolType& memPool) : _cache(memPool.template create(size)), _size(size), _offset(0u), _memPool(memPool) { } - CachingStencil(const unsigned int size, MemoryPoolType& memPool, unsigned int offset) : _cache(memPool.template create(size) + offset), _size(size), _offset(offset), _memPool(memPool) { } - CachingStencil(const std::initializer_list vals, MemoryPoolType& memPool) : CachingStencil(vals.size(), memPool) { initialize(vals); } - CachingStencil(const std::initializer_list vals, MemoryPoolType& memPool, unsigned int offset) : CachingStencil(vals.size(), memPool, offset) { initialize(vals); } - ~CachingStencil() CADET_NOEXCEPT { _memPool.template destroy(); } - - inline double operator[](const int idx) const - { - cadet_assert(idx >= -static_cast(_offset)); - cadet_assert(idx < static_cast(_size - _offset)); - return _cache[idx]; - } - inline double& operator[](const int idx) - { - cadet_assert(idx >= -static_cast(_offset)); - cadet_assert(idx < static_cast(_size - _offset)); - return _cache[idx]; - } - - inline double native(const unsigned int i) const - { - cadet_assert(i < _size); - return _cache[static_cast(i) - static_cast(_offset)]; - } - - inline void advance(const double val) CADET_NOEXCEPT - { - // Rotate cache backwards, that is, assign ..., _cache[0] = _cache[1], _cache[1] = _cache[2] - for (int i = -static_cast(_offset); i < static_cast(_size) - 1 - static_cast(_offset); ++i) - _cache[i] = _cache[i+1]; - - // Update cache - _cache[static_cast(_size) - static_cast(_offset) - 1] = val; - } - - inline void initialize(const std::initializer_list vals) - { - cadet_assert(vals.size() <= _size); - - double const* ptr = vals.begin(); - - for (int i = 0; i < static_cast(_size); ++i, ++ptr) - _cache[i - static_cast(_offset)] = *ptr; - } - - protected: - double* const _cache; - const unsigned int _size; - const unsigned int _offset; - MemoryPoolType& _memPool; - }; + CachingStencil(const unsigned int size, MemoryPoolType& memPool) + : _cache(memPool.template create(size)), _size(size), _offset(0u), _memPool(memPool) + { + } + CachingStencil(const unsigned int size, MemoryPoolType& memPool, unsigned int offset) + : _cache(memPool.template create(size) + offset), _size(size), _offset(offset), _memPool(memPool) + { + } + CachingStencil(const std::initializer_list vals, MemoryPoolType& memPool) : CachingStencil(vals.size(), memPool) + { + initialize(vals); + } + CachingStencil(const std::initializer_list vals, MemoryPoolType& memPool, unsigned int offset) + : CachingStencil(vals.size(), memPool, offset) + { + initialize(vals); + } + ~CachingStencil() CADET_NOEXCEPT + { + _memPool.template destroy(); + } + + inline const T& operator[](const int idx) const + { + cadet_assert(idx >= -static_cast(_offset)); + cadet_assert(idx < static_cast(_size - _offset)); + return _cache[idx]; + } + inline T& operator[](const int idx) + { + cadet_assert(idx >= -static_cast(_offset)); + cadet_assert(idx < static_cast(_size - _offset)); + return _cache[idx]; + } + + inline const T& native(const unsigned int i) const + { + cadet_assert(i < _size); + return _cache[static_cast(i) - _offset]; + } + + inline void initialize(const std::initializer_list vals) + { + cadet_assert(vals.size() <= _size); + + T const* ptr = vals.begin(); + + for (int i = 0; i < static_cast(_size); ++i, ++ptr) + _cache[i - static_cast(_offset)] = *ptr; + } + + /** + * @brief Advances the stencil to the next cell + * @param val New volume average added to the stencil + */ + inline void advance(const T& val) CADET_NOEXCEPT + { + // Rotate cache backwards, that is, assign ..., _cache[0] = _cache[1], _cache[1] = _cache[2] + for (int i = -static_cast(_offset); i < static_cast(_size) - 1 - static_cast(_offset); ++i) + _cache[i] = _cache[i + 1]; + + // Update cache + _cache[static_cast(_size) - static_cast(_offset) - 1] = val; + } + +protected: + T* const _cache; //!< Cache for stencil elements + const unsigned int _size; //!< Size of the stencil + const unsigned int _offset; //!< Offset of the pointer in the stencil + MemoryPoolType& _memPool; //!< Memory pool for the stencil +}; + +/** + * @brief Specialization of the CachingStencil for doubles + * @todo Is this specialization of the CachingStencil necessary? + */ +template class CachingStencil +{ +public: + CachingStencil(const unsigned int size, MemoryPoolType& memPool) + : _cache(memPool.template create(size)), _size(size), _offset(0u), _memPool(memPool) + { + } + CachingStencil(const unsigned int size, MemoryPoolType& memPool, unsigned int offset) + : _cache(memPool.template create(size) + offset), _size(size), _offset(offset), _memPool(memPool) + { + } + CachingStencil(const std::initializer_list vals, MemoryPoolType& memPool) + : CachingStencil(vals.size(), memPool) + { + initialize(vals); + } + CachingStencil(const std::initializer_list vals, MemoryPoolType& memPool, unsigned int offset) + : CachingStencil(vals.size(), memPool, offset) + { + initialize(vals); + } + ~CachingStencil() CADET_NOEXCEPT + { + _memPool.template destroy(); + } + + inline double operator[](const int idx) const + { + cadet_assert(idx >= -static_cast(_offset)); + cadet_assert(idx < static_cast(_size - _offset)); + return _cache[idx]; + } + inline double& operator[](const int idx) + { + cadet_assert(idx >= -static_cast(_offset)); + cadet_assert(idx < static_cast(_size - _offset)); + return _cache[idx]; + } + + inline double native(const unsigned int i) const + { + cadet_assert(i < _size); + return _cache[static_cast(i) - static_cast(_offset)]; + } + + inline void advance(const double val) CADET_NOEXCEPT + { + // Rotate cache backwards, that is, assign ..., _cache[0] = _cache[1], _cache[1] = _cache[2] + for (int i = -static_cast(_offset); i < static_cast(_size) - 1 - static_cast(_offset); ++i) + _cache[i] = _cache[i + 1]; + + // Update cache + _cache[static_cast(_size) - static_cast(_offset) - 1] = val; + } + + inline void initialize(const std::initializer_list vals) + { + cadet_assert(vals.size() <= _size); + + double const* ptr = vals.begin(); + + for (int i = 0; i < static_cast(_size); ++i, ++ptr) + _cache[i - static_cast(_offset)] = *ptr; + } + +protected: + double* const _cache; + const unsigned int _size; + const unsigned int _offset; + MemoryPoolType& _memPool; +}; } // namespace cadet -#endif // LIBCADET_STENCIL_HPP_ +#endif // LIBCADET_STENCIL_HPP_ diff --git a/src/libcadet/SundialsVector.hpp b/src/libcadet/SundialsVector.hpp index be9e05540..6e2ba46a5 100644 --- a/src/libcadet/SundialsVector.hpp +++ b/src/libcadet/SundialsVector.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Handles different NVector implementations of SUNDIALS and provides uniform access to them. * Note that, according to the SUNDIALS manual, the OpenMP implementation only improves performance * for state vectors of 100.000 entries or more because of threading overhead. @@ -21,32 +21,32 @@ #define LIBCADET_SUNDIALSVECTOR_HPP_ #ifdef CADET_SUNDIALS_OPENMP - #include - #include - - #define NVEC_DATA(x) NV_DATA_OMP(x) - #define NVEC_LENGTH(x) NV_LENGTH_OMP(x) - - #define NVec_New(x) N_VNew_OpenMP(x, omp_get_max_threads()) - #define NVec_Destroy N_VDestroy_OpenMP - #define NVec_DestroyArray N_VDestroyVectorArray_OpenMP - #define NVec_CloneArray N_VCloneVectorArray_OpenMP - #define NVec_NewEmpty(x) N_VNewEmpty_OpenMP(x, omp_get_max_threads()) - #define NVec_SetThreads(x, nThreads) NV_NUM_THREADS_OMP(x) = nThreads +#include +#include + +#define NVEC_DATA(x) NV_DATA_OMP(x) +#define NVEC_LENGTH(x) NV_LENGTH_OMP(x) + +#define NVec_New(x) N_VNew_OpenMP(x, omp_get_max_threads()) +#define NVec_Destroy N_VDestroy_OpenMP +#define NVec_DestroyArray N_VDestroyVectorArray_OpenMP +#define NVec_CloneArray N_VCloneVectorArray_OpenMP +#define NVec_NewEmpty(x) N_VNewEmpty_OpenMP(x, omp_get_max_threads()) +#define NVec_SetThreads(x, nThreads) NV_NUM_THREADS_OMP(x) = nThreads #else - #include +#include - #define NVEC_DATA(x) NV_DATA_S(x) - #define NVEC_LENGTH(x) NV_LENGTH_S(x) +#define NVEC_DATA(x) NV_DATA_S(x) +#define NVEC_LENGTH(x) NV_LENGTH_S(x) - #define NVec_New(x) N_VNew_Serial(x) - #define NVec_Destroy N_VDestroy_Serial - #define NVec_DestroyArray N_VDestroyVectorArray_Serial - #define NVec_CloneArray N_VCloneVectorArray_Serial - #define NVec_NewEmpty N_VNewEmpty_Serial - #define NVec_SetThreads(x, nThreads) +#define NVec_New(x) N_VNew_Serial(x) +#define NVec_Destroy N_VDestroy_Serial +#define NVec_DestroyArray N_VDestroyVectorArray_Serial +#define NVec_CloneArray N_VCloneVectorArray_Serial +#define NVec_NewEmpty N_VNewEmpty_Serial +#define NVec_SetThreads(x, nThreads) #endif #define NVec_Const N_VConst -#endif // LIBCADET_SUNDIALSVECTOR_HPP_ +#endif // LIBCADET_SUNDIALSVECTOR_HPP_ diff --git a/src/libcadet/VersionInfo.cpp.in b/src/libcadet/VersionInfo.cpp.in index e8a346d32..b415a3ed6 100644 --- a/src/libcadet/VersionInfo.cpp.in +++ b/src/libcadet/VersionInfo.cpp.in @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -67,7 +67,7 @@ namespace cadet extern "C" { - + CADET_API const char* cdtGetLibraryVersion() { return cadet::getLibraryVersion(); @@ -82,7 +82,7 @@ extern "C" { return cadet::getLibraryBranchRefspec(); } - + CADET_API const char* cdtGetLibraryDependencyVersions() { return cadet::getLibraryDependencyVersions(); diff --git a/src/libcadet/Weno.cpp b/src/libcadet/Weno.cpp index 5e2fd1d09..50777e16a 100644 --- a/src/libcadet/Weno.cpp +++ b/src/libcadet/Weno.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -16,25 +16,23 @@ namespace cadet { // Initialization of static WENO coefficients -const double Weno::_wenoD2[2] = { 2.0/3.0, 1.0/3.0 }; +const double Weno::_wenoD2[2] = {2.0 / 3.0, 1.0 / 3.0}; -const double Weno::_wenoC2[2*2] = { 0.5, -0.5, - 0.5, 1.5 }; -const double Weno::_wenoJbvv2[2*3*3] = -{0, 2, 0,-2, 0, 0, - 0,-2, 2, 2, -2, 0, - 0, 0, -2, 0, 2, 0}; +const double Weno::_wenoC2[2 * 2] = {0.5, -0.5, 0.5, 1.5}; +const double Weno::_wenoJbvv2[2 * 3 * 3] = {0, 2, 0, -2, 0, 0, 0, -2, 2, 2, -2, 0, 0, 0, -2, 0, 2, 0}; -const double Weno::_wenoD3[3] = { 0.3, 0.6, 0.1 }; +const double Weno::_wenoD3[3] = {0.3, 0.6, 0.1}; -const double Weno::_wenoC3[3*3] = { 1.0/3.0, -1.0/6.0, 1.0/3.0, - 5.0/6.0, 5.0/6.0, -7.0/6.0, - -1.0/6.0, 1.0/3.0, 11.0/6.0 }; -const double Weno::_wenoJbvv3[3*5*5] = // Used to generate Jbv: vec(Jbv) = A*v -{0,0,8.0/3, 0,0,-19.0/3, 0,0,11.0/3, 0,0,0, 0,0,0, - 0,0,-19.0/3, 0,8.0/3,50.0/3, 0,-13.0/3,-31.0/3, 0,5.0/3,0, 0,0,0, - 0,0,11.0/3, 0,-13.0/3,-31.0/3, 20.0/3,26.0/3,20.0/3, -31.0/3,-13.0/3,0, 11.0/3,0,0, - 0,0,0, 0,5.0/3,0, -31.0/3,-13.0/3,0, 50.0/3,8.0/3,0, -19.0/3,0,0, - 0,0,0, 0,0,0, 11.0/3,0,0, -19.0/3,0,0, 8.0/3,0,0}; +const double Weno::_wenoC3[3 * 3] = {1.0 / 3.0, -1.0 / 6.0, 1.0 / 3.0, 5.0 / 6.0, 5.0 / 6.0, + -7.0 / 6.0, -1.0 / 6.0, 1.0 / 3.0, 11.0 / 6.0}; +const double Weno::_wenoJbvv3[3 * 5 * 5] = // Used to generate Jbv: vec(Jbv) = A*v + {0, 0, 8.0 / 3, 0, 0, -19.0 / 3, 0, 0, 11.0 / 3, 0, + 0, 0, 0, 0, 0, 0, 0, -19.0 / 3, 0, 8.0 / 3, + 50.0 / 3, 0, -13.0 / 3, -31.0 / 3, 0, 5.0 / 3, 0, 0, 0, 0, + 0, 0, 11.0 / 3, 0, -13.0 / 3, -31.0 / 3, 20.0 / 3, 26.0 / 3, 20.0 / 3, -31.0 / 3, + -13.0 / 3, 0, 11.0 / 3, 0, 0, 0, 0, 0, 0, 5.0 / 3, + 0, -31.0 / 3, -13.0 / 3, 0, 50.0 / 3, 8.0 / 3, 0, -19.0 / 3, 0, 0, + 0, 0, 0, 0, 0, 0, 11.0 / 3, 0, 0, -19.0 / 3, + 0, 0, 8.0 / 3, 0, 0}; } // namespace cadet diff --git a/src/libcadet/Weno.hpp b/src/libcadet/Weno.hpp index b5f2c63bb..789e08f78 100644 --- a/src/libcadet/Weno.hpp +++ b/src/libcadet/Weno.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Implements the WENO method */ @@ -42,68 +42,84 @@ namespace cadet class Weno { public: - /** - * @brief Boundary treatment method determines how the reconstruction handles insufficient available elements (i.e., less elements available than stencil size) + * @brief Boundary treatment method determines how the reconstruction handles insufficient available elements (i.e., + * less elements available than stencil size) */ enum class BoundaryTreatment : int { - ReduceOrder = 0, //!< Reduce the order of the WENO method such that the stencil is small enough - ZeroWeights = 1, //!< Set weights of WENO method to 0 for unavailable elements - ZeroWeightsForPnotZero = 2, //!< Set weights of WENO method to 0 for unavailable elements, except for the first cell (order is reduced to 1) + ReduceOrder = 0, //!< Reduce the order of the WENO method such that the stencil is small enough + ZeroWeights = 1, //!< Set weights of WENO method to 0 for unavailable elements + ZeroWeightsForPnotZero = 2, //!< Set weights of WENO method to 0 for unavailable elements, except for the first + //!< cell (order is reduced to 1) LargeGhostNodes = 3, }; /** * @brief Creates the WENO scheme */ - Weno() : _order(maxOrder()), _boundaryTreatment(BoundaryTreatment::ReduceOrder), _intermediateValues(3 * maxOrder() * sizeof(active)) { } + Weno() + : _order(maxOrder()), _boundaryTreatment(BoundaryTreatment::ReduceOrder), + _intermediateValues(3 * maxOrder() * sizeof(active)) + { + } /** * @brief Returns the maximum order \f$ r \f$ of the implemented schemes * @return Maximum WENO order \f$ r \f$ */ - CADET_CONSTEXPR static inline unsigned int maxOrder() CADET_NOEXCEPT { return 3; } + CADET_CONSTEXPR static inline unsigned int maxOrder() CADET_NOEXCEPT + { + return 3; + } /** * @brief Returns the maximum stencil size for the implemented schemes * @return Maximum stencil size */ - CADET_CONSTEXPR static inline unsigned int maxStencilSize() CADET_NOEXCEPT { return 2 * maxOrder() - 1; } + CADET_CONSTEXPR static inline unsigned int maxStencilSize() CADET_NOEXCEPT + { + return 2 * maxOrder() - 1; + } /** * @brief Reconstructs a cell face value from volume averages - * @param [in] epsilon \f$ \varepsilon \f$ of the WENO emthod (prevents division by zero in the weights) + * @param [in] epsilon \f$ \varepsilon \f$ of the WENO emthod (prevents division by zero in the weights) * @param [in] cellIdx Index of the current cell * @param [in] numCells Number of cells - * @param [in] w Stencil that contains the \f$ 2r-1 \f$ volume averages from which the cell face values are reconstructed centered at the - * current cell (i.e., index 0 is the current cell, -2 the next to previous cell, 2 the next but one cell) + * @param [in] w Stencil that contains the \f$ 2r-1 \f$ volume averages from which the cell face values are + * reconstructed centered at the current cell (i.e., index 0 is the current cell, -2 the next to previous cell, 2 + * the next but one cell) * @param [out] result Reconstructed cell face value - * @param [out] Dvm Gradient of the reconstructed cell face value (array has to be of size \f$ 2r-1\f$ where \f$ r \f$ is the WENO order) + * @param [out] Dvm Gradient of the reconstructed cell face value (array has to be of size \f$ 2r-1\f$ where \f$ r + * \f$ is the WENO order) * @tparam StateType Type of the state variables * @tparam StencilType Type of the stencil (can be a dedicated class with overloaded operator[] or a simple pointer) * @return Order of the WENO scheme that was used in the computation */ template - int reconstruct(double epsilon, unsigned int cellIdx, unsigned int numCells, const StencilType& w, StateType& result, double* const Dvm) + int reconstruct(double epsilon, unsigned int cellIdx, unsigned int numCells, const StencilType& w, + StateType& result, double* const Dvm) { return reconstruct(epsilon, cellIdx, numCells, w, result, Dvm); } /** * @brief Reconstructs a cell face value from volume averages - * @param [in] epsilon \f$ \varepsilon \f$ of the WENO emthod (prevents division by zero in the weights) + * @param [in] epsilon \f$ \varepsilon \f$ of the WENO emthod (prevents division by zero in the weights) * @param [in] cellIdx Index of the current cell * @param [in] numCells Number of cells - * @param [in] w Stencil that contains the \f$ 2r-1 \f$ volume averages from which the cell face values are reconstructed centered at the - * current cell (i.e., index 0 is the current cell, -2 the next to previous cell, 2 the next but one cell) + * @param [in] w Stencil that contains the \f$ 2r-1 \f$ volume averages from which the cell face values are + * reconstructed centered at the current cell (i.e., index 0 is the current cell, -2 the next to previous cell, 2 + * the next but one cell) * @param [out] result Reconstructed cell face value * @tparam StateType Type of the state variables * @tparam StencilType Type of the stencil (can be a dedicated class with overloaded operator[] or a simple pointer) * @return Order of the WENO scheme that was used in the computation */ template - int reconstruct(double epsilon, unsigned int cellIdx, unsigned int numCells, const StencilType& w, StateType& result) + int reconstruct(double epsilon, unsigned int cellIdx, unsigned int numCells, const StencilType& w, + StateType& result) { return reconstruct(epsilon, cellIdx, numCells, w, result, nullptr); } @@ -118,24 +134,33 @@ class Weno cadet_assert(order > 0); _order = order; } - + /** * @brief Returns the WENO order * @return Order of the WENO method */ - inline int order() const CADET_NOEXCEPT { return _order; } + inline int order() const CADET_NOEXCEPT + { + return _order; + } /** * @brief Sets the boundary treatment method * @param [in] bndTreatment Boundary treatment method */ - inline void boundaryTreatment(BoundaryTreatment bndTreatment) { _boundaryTreatment = bndTreatment; } + inline void boundaryTreatment(BoundaryTreatment bndTreatment) + { + _boundaryTreatment = bndTreatment; + } /** * @brief Returns the boundary treatment method * @return Boundary treatment method */ - inline BoundaryTreatment boundaryTreatment() const CADET_NOEXCEPT { return _boundaryTreatment; } + inline BoundaryTreatment boundaryTreatment() const CADET_NOEXCEPT + { + return _boundaryTreatment; + } /** * @brief Sets the boundary treatment method @@ -143,20 +168,21 @@ class Weno */ inline void boundaryTreatment(int bndTreatment) { - switch(bndTreatment) + switch (bndTreatment) { - case static_cast::type>(BoundaryTreatment::ReduceOrder): - _boundaryTreatment = BoundaryTreatment::ReduceOrder; - return; - case static_cast::type>(BoundaryTreatment::ZeroWeights): - _boundaryTreatment = BoundaryTreatment::ZeroWeights; - return; - case static_cast::type>(BoundaryTreatment::ZeroWeightsForPnotZero): - _boundaryTreatment = BoundaryTreatment::ZeroWeightsForPnotZero; - return; - case static_cast::type>(BoundaryTreatment::LargeGhostNodes): - _boundaryTreatment = BoundaryTreatment::LargeGhostNodes; - return; + case static_cast::type>(BoundaryTreatment::ReduceOrder): + _boundaryTreatment = BoundaryTreatment::ReduceOrder; + return; + case static_cast::type>(BoundaryTreatment::ZeroWeights): + _boundaryTreatment = BoundaryTreatment::ZeroWeights; + return; + case static_cast::type>( + BoundaryTreatment::ZeroWeightsForPnotZero): + _boundaryTreatment = BoundaryTreatment::ZeroWeightsForPnotZero; + return; + case static_cast::type>(BoundaryTreatment::LargeGhostNodes): + _boundaryTreatment = BoundaryTreatment::LargeGhostNodes; + return; } throw InvalidParameterException("Unknown boundary treatment type"); } @@ -165,38 +191,49 @@ class Weno * @brief Returns the number of upper diagonals required in the Jacobian * @return Number of required Jacobian upper diagonals */ - inline unsigned int upperBandwidth() const CADET_NOEXCEPT { return _order - 1; } + inline unsigned int upperBandwidth() const CADET_NOEXCEPT + { + return _order - 1; + } /** * @brief Returns the number of lower diagonals required in the Jacobian * @return Number of required Jacobian lower diagonals */ - inline unsigned int lowerBandwidth() const CADET_NOEXCEPT { return _order - 1; } + inline unsigned int lowerBandwidth() const CADET_NOEXCEPT + { + return _order - 1; + } /** * @brief Returns the size of the stencil (i.e., the number of required elements) * @return Size of the stencil */ - inline unsigned int stencilSize() const CADET_NOEXCEPT { return 2 * _order - 1; } + inline unsigned int stencilSize() const CADET_NOEXCEPT + { + return 2 * _order - 1; + } private: - /** * @brief Reconstructs a cell face value from volume averages - * @param [in] epsilon \f$ \varepsilon \f$ of the WENO emthod (prevents division by zero in the weights) + * @param [in] epsilon \f$ \varepsilon \f$ of the WENO emthod (prevents division by zero in the weights) * @param [in] cellIdx Index of the current cell * @param [in] numCells Number of cells - * @param [in] w Stencil that contains the \f$ 2r-1 \f$ volume averages from which the cell face values are reconstructed centered at the - * current cell (i.e., index 0 is the current cell, -2 the next to previous cell, 2 the next but one cell) + * @param [in] w Stencil that contains the \f$ 2r-1 \f$ volume averages from which the cell face values are + * reconstructed centered at the current cell (i.e., index 0 is the current cell, -2 the next to previous cell, 2 + * the next but one cell) * @param [out] result Reconstructed cell face value - * @param [out] Dvm Gradient of the reconstructed cell face value (array has to be of size \f$ 2r-1\f$ where \f$ r \f$ is the WENO order) + * @param [out] Dvm Gradient of the reconstructed cell face value (array has to be of size \f$ 2r-1\f$ where \f$ r + * \f$ is the WENO order) * @tparam StateType Type of the state variables * @tparam StencilType Type of the stencil (can be a dedicated class with overloaded operator[] or a simple pointer) * @tparam wantJac Determines if the gradient is computed (@c true) or not (@c false) * @return Order of the WENO scheme that was used in the computation */ template - int reconstruct(double epsilon, unsigned int cellIdx, unsigned int numCells, const StencilType& w, StateType& result, double* const Dvm) + int reconstruct(double epsilon, unsigned int cellIdx, unsigned int numCells, const StencilType& w, + StateType& result, double* const Dvm) { #if defined(ACTIVE_SETFAD) || defined(ACTIVE_SFAD) using cadet::sqr; @@ -215,7 +252,8 @@ class Weno // Lower WENO order such that maximum order is used at all points // This very statement selects the max. weno order for the current column cell // order = min(maxOrderleft, maxOrderright) - order = std::min(std::min(static_cast(cellIdx) + 1, _order), std::min(static_cast(numCells - cellIdx), _order)); + order = std::min(std::min(static_cast(cellIdx) + 1, _order), + std::min(static_cast(numCells - cellIdx), _order)); break; case BoundaryTreatment::ZeroWeights: @@ -238,21 +276,21 @@ class Weno bnd = numCells - cellIdx; } break; -/* - case BoundaryTreatment::LargeGhostNodes: - // Large ghost points - if (cellIdx == 0) - { - w[-1] = 1e20; - w[-2] = 1e50; - } - else if (cellIdx == numCells - 2) - w[2] = 1e20; - else if (cellIdx == numCells - 1) - w[2] = 1e50; - break; -*/ - } + /* + case BoundaryTreatment::LargeGhostNodes: + // Large ghost points + if (cellIdx == 0) + { + w[-1] = 1e20; + w[-2] = 1e50; + } + else if (cellIdx == numCells - 2) + w[2] = 1e20; + else if (cellIdx == numCells - 1) + w[2] = 1e50; + break; + */ + } // Total stencil size const int sl = 2 * order - 1; @@ -268,10 +306,10 @@ class Weno // Allocate memory for intermediate values: beta, alpha (= omega), and vr StateType* const work = _intermediateValues.create(3 * order); - StateType* const beta = work; + StateType* const beta = work; StateType* const alpha = work + order; StateType* const omega = work + order; - StateType* const vr = work + 2*order; // Reconstructed values + StateType* const vr = work + 2 * order; // Reconstructed values const double* d = nullptr; const double* c = nullptr; @@ -280,21 +318,21 @@ class Weno // Calculate smoothness measures switch (order) { - case 2: - beta[0] = sqr(w[1] - w[0]); - beta[1] = sqr(w[0] - w[-1]); - d = _wenoD2; - c = _wenoC2; - Jbvv = _wenoJbvv2; - break; - case 3: - beta[0] = 13.0/12.0 * sqr(w[ 0] - 2.0 * w[ 1] + w[2]) + 0.25 * sqr(3.0 * w[ 0] - 4.0 * w[ 1] + w[2]); - beta[1] = 13.0/12.0 * sqr(w[-1] - 2.0 * w[ 0] + w[1]) + 0.25 * sqr( w[-1] - w[ 1] ); - beta[2] = 13.0/12.0 * sqr(w[-2] - 2.0 * w[-1] + w[0]) + 0.25 * sqr( w[-2] - 4.0 * w[-1] + 3.0 * w[0]); - d = _wenoD3; - c = _wenoC3; - Jbvv = _wenoJbvv3; - break; + case 2: + beta[0] = sqr(w[1] - w[0]); + beta[1] = sqr(w[0] - w[-1]); + d = _wenoD2; + c = _wenoC2; + Jbvv = _wenoJbvv2; + break; + case 3: + beta[0] = 13.0 / 12.0 * sqr(w[0] - 2.0 * w[1] + w[2]) + 0.25 * sqr(3.0 * w[0] - 4.0 * w[1] + w[2]); + beta[1] = 13.0 / 12.0 * sqr(w[-1] - 2.0 * w[0] + w[1]) + 0.25 * sqr(w[-1] - w[1]); + beta[2] = 13.0 / 12.0 * sqr(w[-2] - 2.0 * w[-1] + w[0]) + 0.25 * sqr(w[-2] - 4.0 * w[-1] + 3.0 * w[0]); + d = _wenoD3; + c = _wenoC3; + Jbvv = _wenoJbvv3; + break; } // Add eps to avoid divide-by-zeros @@ -330,7 +368,7 @@ class Weno { vr[r] = 0.0; for (int j = 0; j < order; ++j) - vr[r] += c[r + order * j] * w[-r+j]; + vr[r] += c[r + order * j] * w[-r + j]; } // Weighted sum @@ -348,7 +386,7 @@ class Weno // multiply with "d(omega)/d(alpha)" to get "d(result)/d(alpha)" double dot = 0.0; for (int r = 0; r < order; ++r) - dot += static_cast(vr[r]) * static_cast(omega[r]); //StateType(vr[r] * omega[r]); + dot += static_cast(vr[r]) * static_cast(omega[r]); // StateType(vr[r] * omega[r]); for (int r = 0; r < order; ++r) vr[r] = (vr[r] - dot) / alpha_sum; @@ -364,7 +402,8 @@ class Weno { dot = 0.0; for (int i = 0; i < sl; ++i) - dot += static_cast(Jbvv[r + order * j + order * sl * i]) * static_cast(w[i - order + 1]); + dot += static_cast(Jbvv[r + order * j + order * sl * i]) * + static_cast(w[i - order + 1]); // To do: re-arange Jbvv to reduce cache misses ! Dvm[j] += static_cast(vr[r]) * dot; // StateType(vr[r] * dot); } @@ -380,20 +419,19 @@ class Weno return order; } - int _order; //!< Selected WENO order + int _order; //!< Selected WENO order BoundaryTreatment _boundaryTreatment; //!< Controls how to treat boundary cells - ArrayPool _intermediateValues; //!< Buffer for intermediate and temporary values + ArrayPool _intermediateValues; //!< Buffer for intermediate and temporary values static const double _wenoD2[2]; - static const double _wenoC2[2*2]; - static const double _wenoJbvv2[2*3*3]; + static const double _wenoC2[2 * 2]; + static const double _wenoJbvv2[2 * 3 * 3]; static const double _wenoD3[3]; - static const double _wenoC3[3*3]; - static const double _wenoJbvv3[3*5*5]; - + static const double _wenoC3[3 * 3]; + static const double _wenoJbvv3[3 * 5 * 5]; }; } // namespace cadet -#endif // LIBCADET_WENO_HPP_ +#endif // LIBCADET_WENO_HPP_ diff --git a/src/libcadet/api/CAPIv1.cpp b/src/libcadet/api/CAPIv1.cpp index 7a1c13e65..7bdc8bcf1 100644 --- a/src/libcadet/api/CAPIv1.cpp +++ b/src/libcadet/api/CAPIv1.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET - The Chromatography Analysis and Design Toolkit -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -38,421 +38,430 @@ namespace api namespace v1 { - cdtDriver* createDriver() +cdtDriver* createDriver() +{ + return new cdtDriver{new cadet::Driver()}; +} + +void deleteDriver(cdtDriver* drv) +{ + if (!drv) + return; + if (!drv->driver) + return; + + delete drv->driver; + drv->driver = nullptr; +} + +/** + * @brief ParameterProvider implementation using C callback functions + */ +class CallbackParameterProvider : public IParameterProvider +{ +public: + CallbackParameterProvider(const cdtParameterProvider& pp) : _pp(pp) { - return new cdtDriver{ new cadet::Driver() }; + if (!pp.getDouble) + throw InvalidParameterException("ParameterProvider does not implement getDouble()"); + if (!pp.getInt) + throw InvalidParameterException("ParameterProvider does not implement getInt()"); + if (!pp.getBool) + throw InvalidParameterException("ParameterProvider does not implement getBool()"); + if (!pp.getString) + throw InvalidParameterException("ParameterProvider does not implement getString()"); + if (!pp.getDoubleArray && !pp.getDoubleArrayItem) + throw InvalidParameterException( + "ParameterProvider does neither implement getDoubleArray() nor getDoubleArrayItem()"); + if (!pp.getIntArray && !pp.getIntArrayItem) + throw InvalidParameterException( + "ParameterProvider does neither implement getIntArray() nor getIntArrayItem()"); + if (!pp.getBoolArray && !pp.getBoolArrayItem) + throw InvalidParameterException( + "ParameterProvider does neither implement getBoolArray() nor getBoolArrayItem()"); + if (!pp.getStringArray && !pp.getStringArrayItem) + throw InvalidParameterException( + "ParameterProvider does neither implement getStringArray() nor getStringArrayItem()"); + if (!pp.exists) + throw InvalidParameterException("ParameterProvider does not implement exists()"); + if (!pp.isArray) + throw InvalidParameterException("ParameterProvider does not implement isArray()"); + if (!pp.numElements) + throw InvalidParameterException("ParameterProvider does not implement numElements()"); + if (!pp.pushScope) + throw InvalidParameterException("ParameterProvider does not implement pushScope()"); + if (!pp.popScope) + throw InvalidParameterException("ParameterProvider does not implement popScope()"); } - - void deleteDriver(cdtDriver* drv) + virtual ~CallbackParameterProvider() CADET_NOEXCEPT { - if (!drv) - return; - if (!drv->driver) - return; - - delete drv->driver; - drv->driver = nullptr; } - /** - * @brief ParameterProvider implementation using C callback functions - */ - class CallbackParameterProvider : public IParameterProvider + virtual double getDouble(const std::string& paramName) { - public: + double v = 0.0; + if (CADET_ERR(_pp.getDouble(_pp.userData, paramName.c_str(), &v))) + throw InvalidParameterException("Retrieving double parameter " + paramName + " failed"); - CallbackParameterProvider(const cdtParameterProvider& pp) : _pp(pp) - { - if (!pp.getDouble) - throw InvalidParameterException("ParameterProvider does not implement getDouble()"); - if (!pp.getInt) - throw InvalidParameterException("ParameterProvider does not implement getInt()"); - if (!pp.getBool) - throw InvalidParameterException("ParameterProvider does not implement getBool()"); - if (!pp.getString) - throw InvalidParameterException("ParameterProvider does not implement getString()"); - if (!pp.getDoubleArray && !pp.getDoubleArrayItem) - throw InvalidParameterException("ParameterProvider does neither implement getDoubleArray() nor getDoubleArrayItem()"); - if (!pp.getIntArray && !pp.getIntArrayItem) - throw InvalidParameterException("ParameterProvider does neither implement getIntArray() nor getIntArrayItem()"); - if (!pp.getBoolArray && !pp.getBoolArrayItem) - throw InvalidParameterException("ParameterProvider does neither implement getBoolArray() nor getBoolArrayItem()"); - if (!pp.getStringArray && !pp.getStringArrayItem) - throw InvalidParameterException("ParameterProvider does neither implement getStringArray() nor getStringArrayItem()"); - if (!pp.exists) - throw InvalidParameterException("ParameterProvider does not implement exists()"); - if (!pp.isArray) - throw InvalidParameterException("ParameterProvider does not implement isArray()"); - if (!pp.numElements) - throw InvalidParameterException("ParameterProvider does not implement numElements()"); - if (!pp.pushScope) - throw InvalidParameterException("ParameterProvider does not implement pushScope()"); - if (!pp.popScope) - throw InvalidParameterException("ParameterProvider does not implement popScope()"); - } - virtual ~CallbackParameterProvider() CADET_NOEXCEPT { } - - virtual double getDouble(const std::string& paramName) - { - double v = 0.0; - if (CADET_ERR(_pp.getDouble(_pp.userData, paramName.c_str(), &v))) - throw InvalidParameterException("Retrieving double parameter " + paramName + " failed"); - - LOG(Debug) << "GET scalar [double] " << paramName << ": " << v; - return v; - } + LOG(Debug) << "GET scalar [double] " << paramName << ": " << v; + return v; + } - virtual int getInt(const std::string& paramName) - { - int v = 0; - if (CADET_ERR(_pp.getInt(_pp.userData, paramName.c_str(), &v))) - throw InvalidParameterException("Retrieving int parameter " + paramName + " failed"); + virtual int getInt(const std::string& paramName) + { + int v = 0; + if (CADET_ERR(_pp.getInt(_pp.userData, paramName.c_str(), &v))) + throw InvalidParameterException("Retrieving int parameter " + paramName + " failed"); - LOG(Debug) << "GET scalar [int] " << paramName << ": " << v; - return v; - } + LOG(Debug) << "GET scalar [int] " << paramName << ": " << v; + return v; + } - virtual uint64_t getUint64(const std::string& paramName) - { - throw std::logic_error("getUint64 not implemented"); - } + virtual uint64_t getUint64(const std::string& paramName) + { + throw std::logic_error("getUint64 not implemented"); + } - virtual bool getBool(const std::string& paramName) - { - uint8_t v = 0; - if (CADET_ERR(_pp.getBool(_pp.userData, paramName.c_str(), &v))) - throw InvalidParameterException("Retrieving bool parameter " + paramName + " failed"); + virtual bool getBool(const std::string& paramName) + { + uint8_t v = 0; + if (CADET_ERR(_pp.getBool(_pp.userData, paramName.c_str(), &v))) + throw InvalidParameterException("Retrieving bool parameter " + paramName + " failed"); - LOG(Debug) << "GET scalar [bool] " << paramName << ": " << bool(v); - return v; - } + LOG(Debug) << "GET scalar [bool] " << paramName << ": " << bool(v); + return v; + } - virtual std::string getString(const std::string& paramName) - { - char const* v = nullptr; - if (CADET_ERR(_pp.getString(_pp.userData, paramName.c_str(), &v))) - throw InvalidParameterException("Retrieving string parameter " + paramName + " failed"); + virtual std::string getString(const std::string& paramName) + { + char const* v = nullptr; + if (CADET_ERR(_pp.getString(_pp.userData, paramName.c_str(), &v))) + throw InvalidParameterException("Retrieving string parameter " + paramName + " failed"); - if (!v) - throw InvalidParameterException("Retrieving string parameter " + paramName + " failed (received nullptr)"); + if (!v) + throw InvalidParameterException("Retrieving string parameter " + paramName + " failed (received nullptr)"); - LOG(Debug) << "GET scalar [string] " << paramName << ": " << v << " (mem: " << static_cast(&v) << " " << static_cast(v) << ")"; - return std::string(v); - } + LOG(Debug) << "GET scalar [string] " << paramName << ": " << v << " (mem: " << static_cast(&v) << " " + << static_cast(v) << ")"; + return std::string(v); + } - virtual std::vector getDoubleArray(const std::string& paramName) + virtual std::vector getDoubleArray(const std::string& paramName) + { + if (_pp.getDoubleArray) { - if (_pp.getDoubleArray) + int num = 0; + double* v = nullptr; + if (CADET_ERR(_pp.getDoubleArray(_pp.userData, paramName.c_str(), &num, &v))) { - int num = 0; - double* v = nullptr; - if (CADET_ERR(_pp.getDoubleArray(_pp.userData, paramName.c_str(), &num, &v))) - { - if (_pp.getDoubleArrayItem) - return getDoubleArrayElementwise(paramName); - else - throw InvalidParameterException("Retrieving double parameter array " + paramName + " failed"); - } - - LOG(Debug) << "GET array [double] " << paramName << ": " << log::VectorPtr(v, num); - return std::vector(v, v + num); + if (_pp.getDoubleArrayItem) + return getDoubleArrayElementwise(paramName); + else + throw InvalidParameterException("Retrieving double parameter array " + paramName + " failed"); } - return getDoubleArrayElementwise(paramName); + LOG(Debug) << "GET array [double] " << paramName << ": " << log::VectorPtr(v, num); + return std::vector(v, v + num); } - virtual std::vector getIntArray(const std::string& paramName) + return getDoubleArrayElementwise(paramName); + } + + virtual std::vector getIntArray(const std::string& paramName) + { + if (_pp.getIntArray) { - if (_pp.getIntArray) + int num = 0; + int* v = nullptr; + if (CADET_ERR(_pp.getIntArray(_pp.userData, paramName.c_str(), &num, &v))) { - int num = 0; - int* v = nullptr; - if (CADET_ERR(_pp.getIntArray(_pp.userData, paramName.c_str(), &num, &v))) - { - if (_pp.getIntArrayItem) - return getIntArrayElementwise(paramName); - else - throw InvalidParameterException("Retrieving int parameter array " + paramName + " failed"); - } - - LOG(Debug) << "GET array [int] " << paramName << ": " << log::VectorPtr(v, num); - return std::vector(v, v + num); + if (_pp.getIntArrayItem) + return getIntArrayElementwise(paramName); + else + throw InvalidParameterException("Retrieving int parameter array " + paramName + " failed"); } - return getIntArrayElementwise(paramName); + LOG(Debug) << "GET array [int] " << paramName << ": " << log::VectorPtr(v, num); + return std::vector(v, v + num); } - virtual std::vector getUint64Array(const std::string& paramName) - { - throw std::logic_error("getUint64Array not implemented"); - } + return getIntArrayElementwise(paramName); + } + + virtual std::vector getUint64Array(const std::string& paramName) + { + throw std::logic_error("getUint64Array not implemented"); + } - virtual std::vector getBoolArray(const std::string& paramName) + virtual std::vector getBoolArray(const std::string& paramName) + { + if (_pp.getBoolArray) { - if (_pp.getBoolArray) + int num = 0; + uint8_t* v = nullptr; + if (CADET_ERR(_pp.getBoolArray(_pp.userData, paramName.c_str(), &num, &v))) { - int num = 0; - uint8_t* v = nullptr; - if (CADET_ERR(_pp.getBoolArray(_pp.userData, paramName.c_str(), &num, &v))) - { - if (_pp.getBoolArrayItem) - return getBoolArrayElementwise(paramName); - else - throw InvalidParameterException("Retrieving bool parameter array " + paramName + " failed"); - } - - std::vector vc(num); - for (int i = 0; i < num; ++i) - vc[i] = v[i]; - - LOG(Debug) << "GET array [bool] " << paramName << ": " << vc; - return vc; + if (_pp.getBoolArrayItem) + return getBoolArrayElementwise(paramName); + else + throw InvalidParameterException("Retrieving bool parameter array " + paramName + " failed"); } - return getBoolArrayElementwise(paramName); + std::vector vc(num); + for (int i = 0; i < num; ++i) + vc[i] = v[i]; + + LOG(Debug) << "GET array [bool] " << paramName << ": " << vc; + return vc; } - virtual std::vector getStringArray(const std::string& paramName) + return getBoolArrayElementwise(paramName); + } + + virtual std::vector getStringArray(const std::string& paramName) + { + if (_pp.getStringArray) { - if (_pp.getStringArray) + int num = 0; + char const** v = nullptr; + if (CADET_ERR(_pp.getStringArray(_pp.userData, paramName.c_str(), &num, &v))) { - int num = 0; - char const** v = nullptr; - if (CADET_ERR(_pp.getStringArray(_pp.userData, paramName.c_str(), &num, &v))) - { - if (_pp.getStringArrayItem) - return getStringArrayElementwise(paramName); - else - throw InvalidParameterException("Retrieving string parameter array " + paramName + " failed"); - } - - std::vector vc(num); - for (int i = 0; i < num; ++i) - vc[i] = v[i]; - - LOG(Debug) << "GET array [string] " << paramName << ": " << vc; - return vc; + if (_pp.getStringArrayItem) + return getStringArrayElementwise(paramName); + else + throw InvalidParameterException("Retrieving string parameter array " + paramName + " failed"); } - return getStringArrayElementwise(paramName); - } + std::vector vc(num); + for (int i = 0; i < num; ++i) + vc[i] = v[i]; - virtual bool exists(const std::string& paramName) - { - const bool r = _pp.exists(_pp.userData, paramName.c_str()) != 0; - LOG(Debug) << "EXISTS " << paramName << ": " << r; - return r; + LOG(Debug) << "GET array [string] " << paramName << ": " << vc; + return vc; } - virtual bool isArray(const std::string& paramName) - { - uint8_t res = 0; - if (CADET_ERR(_pp.isArray(_pp.userData, paramName.c_str(), &res))) - throw InvalidParameterException("Checking whether parameter " + paramName + " is array failed"); - - LOG(Debug) << "ISARRAY " << paramName << ": " << (res != 0); - return res != 0; - } + return getStringArrayElementwise(paramName); + } - virtual std::size_t numElements(const std::string& paramName) - { - const int num = _pp.numElements(_pp.userData, paramName.c_str()); - if (num < 0) - throw InvalidParameterException("Retrieving string parameter array " + paramName + " failed (does not exist)"); + virtual bool exists(const std::string& paramName) + { + const bool r = _pp.exists(_pp.userData, paramName.c_str()) != 0; + LOG(Debug) << "EXISTS " << paramName << ": " << r; + return r; + } - LOG(Debug) << "NUMELEMENTS " << paramName << ": " << num; - return num; - } + virtual bool isArray(const std::string& paramName) + { + uint8_t res = 0; + if (CADET_ERR(_pp.isArray(_pp.userData, paramName.c_str(), &res))) + throw InvalidParameterException("Checking whether parameter " + paramName + " is array failed"); - virtual void pushScope(const std::string& scope) - { - if (CADET_ERR(_pp.pushScope(_pp.userData, scope.c_str()))) - throw InvalidParameterException("Failed to enter scope " + scope); + LOG(Debug) << "ISARRAY " << paramName << ": " << (res != 0); + return res != 0; + } - LOG(Debug) << "PUSHSCOPE " << scope; - } + virtual std::size_t numElements(const std::string& paramName) + { + const int num = _pp.numElements(_pp.userData, paramName.c_str()); + if (num < 0) + throw InvalidParameterException("Retrieving string parameter array " + paramName + + " failed (does not exist)"); - virtual void popScope() - { - if (CADET_ERR(_pp.popScope(_pp.userData))) - throw InvalidParameterException("Failed to exit current scope"); + LOG(Debug) << "NUMELEMENTS " << paramName << ": " << num; + return num; + } - LOG(Debug) << "POPSCOPE"; - } + virtual void pushScope(const std::string& scope) + { + if (CADET_ERR(_pp.pushScope(_pp.userData, scope.c_str()))) + throw InvalidParameterException("Failed to enter scope " + scope); - private: - const cdtParameterProvider& _pp; + LOG(Debug) << "PUSHSCOPE " << scope; + } - std::vector getDoubleArrayElementwise(const std::string& paramName) - { - const int num = _pp.numElements(_pp.userData, paramName.c_str()); - if (num < 0) - throw InvalidParameterException("Retrieving double parameter array " + paramName + " failed (does not exist)"); + virtual void popScope() + { + if (CADET_ERR(_pp.popScope(_pp.userData))) + throw InvalidParameterException("Failed to exit current scope"); - if (num == 0) - return std::vector(0); + LOG(Debug) << "POPSCOPE"; + } - std::vector v(num); - for (int i = 0; i < num; ++i) - { - if (CADET_ERR(_pp.getDoubleArrayItem(_pp.userData, paramName.c_str(), i, v.data() + i))) - throw InvalidParameterException("Retrieving double parameter " + paramName + " failed"); +private: + const cdtParameterProvider& _pp; - LOG(Debug) << "GET array (" << i << ") [double] " << paramName << ": " << v[i]; - } + std::vector getDoubleArrayElementwise(const std::string& paramName) + { + const int num = _pp.numElements(_pp.userData, paramName.c_str()); + if (num < 0) + throw InvalidParameterException("Retrieving double parameter array " + paramName + + " failed (does not exist)"); - return v; - } + if (num == 0) + return std::vector(0); - std::vector getIntArrayElementwise(const std::string& paramName) + std::vector v(num); + for (int i = 0; i < num; ++i) { - const int num = _pp.numElements(_pp.userData, paramName.c_str()); - if (num < 0) - throw InvalidParameterException("Retrieving int parameter array " + paramName + " failed (does not exist)"); - - if (num == 0) - return std::vector(0); - - std::vector v(num); - for (int i = 0; i < num; ++i) - { - if (CADET_ERR(_pp.getIntArrayItem(_pp.userData, paramName.c_str(), i, v.data() + i))) - throw InvalidParameterException("Retrieving int parameter " + paramName + " failed"); - - LOG(Debug) << "GET array (" << i << ") [int] " << paramName << ": " << v[i]; - } + if (CADET_ERR(_pp.getDoubleArrayItem(_pp.userData, paramName.c_str(), i, v.data() + i))) + throw InvalidParameterException("Retrieving double parameter " + paramName + " failed"); - return v; + LOG(Debug) << "GET array (" << i << ") [double] " << paramName << ": " << v[i]; } - std::vector getBoolArrayElementwise(const std::string& paramName) - { - const int num = _pp.numElements(_pp.userData, paramName.c_str()); - if (num < 0) - throw InvalidParameterException("Retrieving bool parameter array " + paramName + " failed (does not exist)"); + return v; + } - if (num == 0) - return std::vector(0); + std::vector getIntArrayElementwise(const std::string& paramName) + { + const int num = _pp.numElements(_pp.userData, paramName.c_str()); + if (num < 0) + throw InvalidParameterException("Retrieving int parameter array " + paramName + " failed (does not exist)"); - std::vector v(num); - for (int i = 0; i < num; ++i) - { - uint8_t temp = 0; - if (CADET_ERR(_pp.getBoolArrayItem(_pp.userData, paramName.c_str(), i, &temp))) - throw InvalidParameterException("Retrieving bool parameter " + paramName + " failed"); + if (num == 0) + return std::vector(0); - v[i] = temp; - LOG(Debug) << "GET array (" << i << ") [bool] " << paramName << ": " << static_cast(v[i]); - } + std::vector v(num); + for (int i = 0; i < num; ++i) + { + if (CADET_ERR(_pp.getIntArrayItem(_pp.userData, paramName.c_str(), i, v.data() + i))) + throw InvalidParameterException("Retrieving int parameter " + paramName + " failed"); - return v; + LOG(Debug) << "GET array (" << i << ") [int] " << paramName << ": " << v[i]; } - std::vector getStringArrayElementwise(const std::string& paramName) - { - const int num = _pp.numElements(_pp.userData, paramName.c_str()); - if (num < 0) - throw InvalidParameterException("Retrieving string parameter array " + paramName + " failed (does not exist)"); - - if (num == 0) - return std::vector(0); + return v; + } - std::vector v(num); - for (int i = 0; i < num; ++i) - { - char const* temp = nullptr; - if (CADET_ERR(_pp.getStringArrayItem(_pp.userData, paramName.c_str(), i, &temp))) - throw InvalidParameterException("Retrieving string parameter " + paramName + " failed"); + std::vector getBoolArrayElementwise(const std::string& paramName) + { + const int num = _pp.numElements(_pp.userData, paramName.c_str()); + if (num < 0) + throw InvalidParameterException("Retrieving bool parameter array " + paramName + + " failed (does not exist)"); - if (!temp) - throw InvalidParameterException("Retrieving string parameter " + paramName + " failed (received nullptr)"); + if (num == 0) + return std::vector(0); - v[i] = temp; - LOG(Debug) << "GET array (" << i << ") [string] " << paramName << ": " << v[i]; - } + std::vector v(num); + for (int i = 0; i < num; ++i) + { + uint8_t temp = 0; + if (CADET_ERR(_pp.getBoolArrayItem(_pp.userData, paramName.c_str(), i, &temp))) + throw InvalidParameterException("Retrieving bool parameter " + paramName + " failed"); - return v; + v[i] = temp; + LOG(Debug) << "GET array (" << i << ") [bool] " << paramName << ": " << static_cast(v[i]); } - }; + return v; + } - cdtResult runSimulation(cdtDriver* drv, cdtParameterProvider const* paramProvider) + std::vector getStringArrayElementwise(const std::string& paramName) { - Driver* const realDrv = drv->driver; - if (!realDrv) - return cdtErrorInvalidInputs; - if (!paramProvider) - return cdtErrorInvalidInputs; + const int num = _pp.numElements(_pp.userData, paramName.c_str()); + if (num < 0) + throw InvalidParameterException("Retrieving string parameter array " + paramName + + " failed (does not exist)"); - try + if (num == 0) + return std::vector(0); + + std::vector v(num); + for (int i = 0; i < num; ++i) { - if (!realDrv->simulator()) - { - CallbackParameterProvider cpp(*paramProvider); - realDrv->configure(cpp); - } - else - realDrv->clearResults(); + char const* temp = nullptr; + if (CADET_ERR(_pp.getStringArrayItem(_pp.userData, paramName.c_str(), i, &temp))) + throw InvalidParameterException("Retrieving string parameter " + paramName + " failed"); - realDrv->run(); + if (!temp) + throw InvalidParameterException("Retrieving string parameter " + paramName + + " failed (received nullptr)"); - } - catch(const std::exception& e) - { - LOG(Error) << "Simulation failed: " << e.what(); - return cdtError; + v[i] = temp; + LOG(Debug) << "GET array (" << i << ") [string] " << paramName << ": " << v[i]; } - return cdtOK; + return v; } +}; - cdtResult getSolutionOutlet(cdtDriver* drv, int unitOpId, double const** time, double const** data, int* nTime, int* nPort, int* nComp) - { - Driver* const realDrv = drv->driver; - if (!realDrv) - return cdtErrorInvalidInputs; +cdtResult runSimulation(cdtDriver* drv, cdtParameterProvider const* paramProvider) +{ + Driver* const realDrv = drv->driver; + if (!realDrv) + return cdtErrorInvalidInputs; + if (!paramProvider) + return cdtErrorInvalidInputs; - InternalStorageSystemRecorder* const sysRec = realDrv->solution(); - if (!sysRec) + try + { + if (!realDrv->simulator()) { - LOG(Error) << "System solution recorder not available"; - return cdtError; + CallbackParameterProvider cpp(*paramProvider); + realDrv->configure(cpp); } + else + realDrv->clearResults(); - if (nTime) - *nTime = sysRec->numDataPoints(); - if (time) - *time = sysRec->time(); + realDrv->run(); + } + catch (const std::exception& e) + { + LOG(Error) << "Simulation failed: " << e.what(); + return cdtError; + } - InternalStorageUnitOpRecorder* const unitRec = sysRec->unitOperation(unitOpId); - if (!unitRec) - { - LOG(Error) << "Solution recorder for unit ID " << unitOpId << " not found"; - return cdtErrorInvalidInputs; - } + return cdtOK; +} - if (!unitRec->solutionConfig().storeOutlet) - { - LOG(Error) << "Outlet of unit " << unitOpId << " not recorded"; - return cdtError; - } +cdtResult getSolutionOutlet(cdtDriver* drv, int unitOpId, double const** time, double const** data, int* nTime, + int* nPort, int* nComp) +{ + Driver* const realDrv = drv->driver; + if (!realDrv) + return cdtErrorInvalidInputs; - if (nPort) - *nPort = unitRec->numOutletPorts(); - if (nComp) - *nComp = unitRec->numComponents(); - if (data) - *data = unitRec->outlet(); + InternalStorageSystemRecorder* const sysRec = realDrv->solution(); + if (!sysRec) + { + LOG(Error) << "System solution recorder not available"; + return cdtError; + } - return cdtOK; + if (nTime) + *nTime = sysRec->numDataPoints(); + if (time) + *time = sysRec->time(); + + InternalStorageUnitOpRecorder* const unitRec = sysRec->unitOperation(unitOpId); + if (!unitRec) + { + LOG(Error) << "Solution recorder for unit ID " << unitOpId << " not found"; + return cdtErrorInvalidInputs; + } + + if (!unitRec->solutionConfig().storeOutlet) + { + LOG(Error) << "Outlet of unit " << unitOpId << " not recorded"; + return cdtError; } -} // namespace v1 + if (nPort) + *nPort = unitRec->numOutletPorts(); + if (nComp) + *nComp = unitRec->numComponents(); + if (data) + *data = unitRec->outlet(); + + return cdtOK; +} -} // namespace api +} // namespace v1 -} // namespace cadet +} // namespace api +} // namespace cadet extern "C" { @@ -468,5 +477,4 @@ extern "C" ptr->getSolutionOutlet = &cadet::api::v1::getSolutionOutlet; return cdtOK; } - } diff --git a/src/libcadet/graph/GraphAlgos.cpp b/src/libcadet/graph/GraphAlgos.cpp index fb7e83687..6dcc2c72e 100644 --- a/src/libcadet/graph/GraphAlgos.cpp +++ b/src/libcadet/graph/GraphAlgos.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -18,111 +18,111 @@ namespace cadet namespace graph { - cadet::util::SlicedVector adjacencyListFromConnectionList(int const* conList, int nUnits, int nCon) - { - cadet::util::SlicedVector adj; - adj.reserve(nCon, nUnits); +cadet::util::SlicedVector adjacencyListFromConnectionList(int const* conList, int nUnits, int nCon) +{ + cadet::util::SlicedVector adj; + adj.reserve(nCon, nUnits); + + std::vector adjNode; + adjNode.reserve(nUnits); - std::vector adjNode; - adjNode.reserve(nUnits); - - // TODO: Improve runtime + // TODO: Improve runtime - for (int i = 0; i < nUnits; ++i) + for (int i = 0; i < nUnits; ++i) + { + adjNode.clear(); + for (int j = 0; j < nCon; ++j) { - adjNode.clear(); - for (int j = 0; j < nCon; ++j) - { - const int from = conList[6*j]; - if (from != i) - continue; + const int from = conList[6 * j]; + if (from != i) + continue; - const int to = conList[6*j+1]; + const int to = conList[6 * j + 1]; - bool found = false; - for (int k = 0; k < static_cast(adjNode.size()); ++k) + bool found = false; + for (int k = 0; k < static_cast(adjNode.size()); ++k) + { + if (adjNode[k] == to) { - if (adjNode[k] == to) - { - found = true; - break; - } + found = true; + break; } - - if (!found) - adjNode.push_back(to); } - adj.pushBackSlice(adjNode); + if (!found) + adjNode.push_back(to); } - return adj; + adj.pushBackSlice(adjNode); } - namespace detail - { - - bool topologicalSortHelper(const cadet::util::SlicedVector& adjList, int u, std::vector& colors, std::vector& topoOrder) - { - // Set color to gray (1) - colors[u] = 1; - - // Iterate over adjacent nodes - int const* const adj = adjList[u]; - const int nAdj = adjList.sliceSize(u); - for (int n = 0; n < nAdj; ++n) - { - const int nu = adj[n]; - if (colors[nu] == 0) - { - // Depth-first traversal - // Escalate cycles to caller - if (topologicalSortHelper(adjList, nu, colors, topoOrder)) - return true; - } - else if (colors[nu] == 1) - { - // Detected cycle - return true; - } - } + return adj; +} - // Set color to black (2) - colors[u] = 2; +namespace detail +{ - // Append node to topological ordering (reverse) - topoOrder.push_back(u); +bool topologicalSortHelper(const cadet::util::SlicedVector& adjList, int u, std::vector& colors, + std::vector& topoOrder) +{ + // Set color to gray (1) + colors[u] = 1; - // No cycle so far - return false; + // Iterate over adjacent nodes + int const* const adj = adjList[u]; + const int nAdj = adjList.sliceSize(u); + for (int n = 0; n < nAdj; ++n) + { + const int nu = adj[n]; + if (colors[nu] == 0) + { + // Depth-first traversal + // Escalate cycles to caller + if (topologicalSortHelper(adjList, nu, colors, topoOrder)) + return true; } + else if (colors[nu] == 1) + { + // Detected cycle + return true; + } + } - } // namespace detail + // Set color to black (2) + colors[u] = 2; + // Append node to topological ordering (reverse) + topoOrder.push_back(u); - bool topologicalSort(const cadet::util::SlicedVector& adjList, std::vector& topoOrder) - { - const int nUnits = adjList.slices(); - topoOrder.clear(); - topoOrder.reserve(nUnits); + // No cycle so far + return false; +} - // Set color of each node to white (0) - std::vector colors(nUnits, 0); +} // namespace detail - for (int u = 0; u < nUnits; ++u) - { - // Visit node if color is white (0) - if (colors[u] != 0) - continue; +bool topologicalSort(const cadet::util::SlicedVector& adjList, std::vector& topoOrder) +{ + const int nUnits = adjList.slices(); + topoOrder.clear(); + topoOrder.reserve(nUnits); - // Explore adjacent nodes and detect cycles - if (detail::topologicalSortHelper(adjList, u, colors, topoOrder)) - return true; - } + // Set color of each node to white (0) + std::vector colors(nUnits, 0); + + for (int u = 0; u < nUnits; ++u) + { + // Visit node if color is white (0) + if (colors[u] != 0) + continue; - return false; + // Explore adjacent nodes and detect cycles + if (detail::topologicalSortHelper(adjList, u, colors, topoOrder)) + return true; } + return false; +} + } // namespace graph } // namespace cadet diff --git a/src/libcadet/graph/GraphAlgos.hpp b/src/libcadet/graph/GraphAlgos.hpp index e74ef3a73..ee84f5588 100644 --- a/src/libcadet/graph/GraphAlgos.hpp +++ b/src/libcadet/graph/GraphAlgos.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides algorithms for graphs */ @@ -27,36 +27,36 @@ namespace cadet namespace graph { - /** - * @brief Converts connection list to adjacency list - * - * @param conList Connection list (6 items per line) - * @param[in] nUnits Number of unit operations - * @param[in] nCon Number of connections in @p conList - * - * @return Adjacency list for each unit operation - */ - cadet::util::SlicedVector adjacencyListFromConnectionList(int const* conList, int nUnits, int nCon); - - /** - * @brief Performs a topological sort of the given directed graph - * @details Topological sorting finds an ordering of the nodes (unit operations) - * such that all dependencies (inputs) of a node are listed before the - * node itself is listed. - * - * In addition, cycles in the graph are detected. - * - * Based on Cormen et al., Introduction to Algorithms (3rd ed.), Sec. 22.4 - * - * @param[in] adjList List of adjacent nodes for each node, see adjacencyListFromConnectionList() - * @param[out] topoOrder Reverse topological order (last item has to be processed first) - * - * @return @c true if the graph contains cycles, @c false otherwise - */ - bool topologicalSort(const cadet::util::SlicedVector& adjList, std::vector& topoOrder); +/** + * @brief Converts connection list to adjacency list + * + * @param conList Connection list (6 items per line) + * @param[in] nUnits Number of unit operations + * @param[in] nCon Number of connections in @p conList + * + * @return Adjacency list for each unit operation + */ +cadet::util::SlicedVector adjacencyListFromConnectionList(int const* conList, int nUnits, int nCon); + +/** + * @brief Performs a topological sort of the given directed graph + * @details Topological sorting finds an ordering of the nodes (unit operations) + * such that all dependencies (inputs) of a node are listed before the + * node itself is listed. + * + * In addition, cycles in the graph are detected. + * + * Based on Cormen et al., Introduction to Algorithms (3rd ed.), Sec. 22.4 + * + * @param[in] adjList List of adjacent nodes for each node, see adjacencyListFromConnectionList() + * @param[out] topoOrder Reverse topological order (last item has to be processed first) + * + * @return @c true if the graph contains cycles, @c false otherwise + */ +bool topologicalSort(const cadet::util::SlicedVector& adjList, std::vector& topoOrder); } // namespace graph } // namespace cadet -#endif // LIBCADET_GRAPHALGOS_HPP_ +#endif // LIBCADET_GRAPHALGOS_HPP_ diff --git a/src/libcadet/linalg/ActiveDenseMatrix.hpp b/src/libcadet/linalg/ActiveDenseMatrix.hpp index 267e83705..4a81b66c8 100644 --- a/src/libcadet/linalg/ActiveDenseMatrix.hpp +++ b/src/libcadet/linalg/ActiveDenseMatrix.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines a rectangular dense matrix comprised of AD elements */ @@ -30,8 +30,6 @@ namespace cadet namespace linalg { - - /** * @brief Represents a dense matrix base class providing common functionality * @details This class uses row-major storage ordering. It does not provide @@ -40,18 +38,20 @@ namespace linalg class ActiveDenseMatrix { public: - /** * @brief Creates an empty, unitialized matrix * @details No memory is allocated for the matrix. Users have to call resize() first. */ - ActiveDenseMatrix() CADET_NOEXCEPT : _data(nullptr), _rows(0), _cols(0) { } + ActiveDenseMatrix() CADET_NOEXCEPT : _data(nullptr), _rows(0), _cols(0) + { + } ~ActiveDenseMatrix() CADET_NOEXCEPT { delete[] _data; } - ActiveDenseMatrix(const ActiveDenseMatrix& cpy) : ActiveDenseMatrix(new active[cpy.stride() * cpy._rows], cpy._rows, cpy._cols) + ActiveDenseMatrix(const ActiveDenseMatrix& cpy) + : ActiveDenseMatrix(new active[cpy.stride() * cpy._rows], cpy._rows, cpy._cols) { copyValues(cpy._data); } @@ -99,7 +99,7 @@ class ActiveDenseMatrix * @details It is assumed that the matrix has enough memory to hold the given copy @p cpy. * All current data is lost in this operation. * The matrix is resized to match the size of @p cpy. - * + * * @param cpy Matrix to be copied */ inline void copyFrom(const ActiveDenseMatrix& cpy) @@ -118,7 +118,7 @@ class ActiveDenseMatrix /** * @brief Resizes the matrix to the given size * @details All data is lost in this operation. - * + * * @param [in] rows The number of rows * @param [in] cols The number of columns */ @@ -145,24 +145,30 @@ class ActiveDenseMatrix /** * @brief Accesses an element in a diagonal of the matrix where the main diagonal has index @c 0 * @details The @p diagonal determines the element in a row. A negative index indicates a lower diagonal, - * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main + * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main * diagonal of the matrix is retrieved. - * + * * @param [in] row Index of the row * @param [in] diagonal Index of the diagonal - * + * * @return Matrix element at the given position */ - inline active& diagonalElement(int row, int diagonal) { return (*this)(row, diagonal); } - inline const active& diagonalElement(int row, int diagonal) const { return (*this)(row, diagonal); } + inline active& diagonalElement(int row, int diagonal) + { + return (*this)(row, diagonal); + } + inline const active& diagonalElement(int row, int diagonal) const + { + return (*this)(row, diagonal); + } /** * @brief Accesses an element in the matrix * @details In contrast to diagonalElement(), the indices do not refer to diagonals but to columns of the matrix. - * + * * @param [in] row Index of the row * @param [in] col Index of the column (from @c 0 to @c columns) - * + * * @return Matrix element at the given position */ inline active& native(int row, int col) @@ -199,32 +205,50 @@ class ActiveDenseMatrix * @brief Returns the number of elements in the matrix * @return Number of elements in the matrix */ - inline int elements() const CADET_NOEXCEPT { return _cols * _rows; } + inline int elements() const CADET_NOEXCEPT + { + return _cols * _rows; + } /** * @brief Returns the number of columns * @return Number of columns */ - inline int columns() const CADET_NOEXCEPT { return _cols; } + inline int columns() const CADET_NOEXCEPT + { + return _cols; + } /** * @brief Returns the number of rows * @return Number of rows */ - inline int rows() const CADET_NOEXCEPT { return _rows; } - + inline int rows() const CADET_NOEXCEPT + { + return _rows; + } + /** * @brief Provides direct access to the underlying memory * @return Pointer to the first element of the underlying array */ - inline active* data() CADET_NOEXCEPT { return _data; } - inline active const* data() const CADET_NOEXCEPT { return _data; } + inline active* data() CADET_NOEXCEPT + { + return _data; + } + inline active const* data() const CADET_NOEXCEPT + { + return _data; + } /** * @brief Returns the number of elements in an array row * @return Number of elements in a matrix row */ - inline int stride() const CADET_NOEXCEPT { return _cols; } + inline int stride() const CADET_NOEXCEPT + { + return _cols; + } /** * @brief Provides access to the underlying data in the given row @@ -251,7 +275,8 @@ class ActiveDenseMatrix y[r] = 0.0; active const* const row = _data + r * stride(); for (int c = 0; c < _cols; ++c) - y[r] += static_cast::type>(row[c]) * static_cast::type>(x[c]); + y[r] += static_cast::type>(row[c]) * + static_cast::type>(x[c]); } } @@ -269,7 +294,8 @@ class ActiveDenseMatrix y[r] = 0.0; active const* const col = _data + r; for (int c = 0; c < _cols; ++c) - y[r] += static_cast::type>(col[c * stride()]) * static_cast::type>(x[c]); + y[r] += static_cast::type>(col[c * stride()]) * + static_cast::type>(x[c]); } } @@ -289,7 +315,8 @@ class ActiveDenseMatrix y[r] *= beta; active const* const row = _data + r * stride(); for (int c = 0; c < _cols; ++c) - y[r] += alpha * static_cast::type>(row[c]) * static_cast::type>(x[c]); + y[r] += alpha * static_cast::type>(row[c]) * + static_cast::type>(x[c]); } } @@ -307,7 +334,8 @@ class ActiveDenseMatrix { active const* const row = _data + r * stride(); for (int c = 0; c < _cols; ++c) - y[r] += alpha * static_cast::type>(row[c]) * static_cast::type>(x[c]); + y[r] += alpha * static_cast::type>(row[c]) * + static_cast::type>(x[c]); } } @@ -325,12 +353,15 @@ class ActiveDenseMatrix { active const* const row = _data + r * stride(); for (int c = 0; c < _cols; ++c) - y[r] += static_cast::type>(alpha) * static_cast::type>(row[c]) * static_cast::type>(x[c]); + y[r] += static_cast::type>(alpha) * + static_cast::type>(row[c]) * + static_cast::type>(x[c]); } } /** - * @brief Multiplies the transpose of the matrix @f$ A @f$ with a given vector @f$ x @f$ and adds it to another vector + * @brief Multiplies the transpose of the matrix @f$ A @f$ with a given vector @f$ x @f$ and adds it to another + * vector * @details Computes @f$ y = \alpha A^T x + \beta y @f$, where @f$ A @f$ is this matrix and @f$ x @f$ is given. * @param [in] x Vector this matrix is multiplied with * @param [in] alpha Factor @f$ \alpha @f$ in front of @f$ A^T x @f$ @@ -345,12 +376,14 @@ class ActiveDenseMatrix y[r] *= beta; active const* const col = _data + r; for (int c = 0; c < _cols; ++c) - y[r] += alpha * static_cast::type>(col[c * stride()]) * static_cast::type>(x[c]); + y[r] += alpha * static_cast::type>(col[c * stride()]) * + static_cast::type>(x[c]); } } /** - * @brief Multiplies the transpose of the matrix @f$ A @f$ with a given vector @f$ x @f$ and adds it to another vector + * @brief Multiplies the transpose of the matrix @f$ A @f$ with a given vector @f$ x @f$ and adds it to another + * vector * @details Computes @f$ y = \alpha A^T x + y @f$, where @f$ A @f$ is this matrix and @f$ x @f$ is given. * @param [in] x Vector this matrix is multiplied with * @param [in] alpha Factor @f$ \alpha @f$ in front of @f$ A^T x @f$ @@ -363,14 +396,15 @@ class ActiveDenseMatrix { active const* const col = _data + r; for (int c = 0; c < _cols; ++c) - y[r] += alpha * static_cast::type>(col[c * stride()]) * static_cast::type>(x[c]); + y[r] += alpha * static_cast::type>(col[c * stride()]) * + static_cast::type>(x[c]); } } /** * @brief Multiplies the matrix @f$ A @f$ with a given vector @f$ x @f$ and adds it to another vector * @details For given vector @f$ x @f$, compute - * @f[ \begin{align} + * @f[ \begin{align} * y_1 &= \alpha_1 (Ax)_1 + \beta y_1 \\ * y_2 &= \alpha_2 (Ax)_2 + \beta y_2, * \end{align} @f] @@ -383,14 +417,16 @@ class ActiveDenseMatrix * @param [out] y Result of the matrix-vector multiplication */ template - void multiplyVectorSplit(const operand_t* const x, double alpha1, const numeric_t& alpha2, double beta, int idxSplit, result_t* const y) const + void multiplyVectorSplit(const operand_t* const x, double alpha1, const numeric_t& alpha2, double beta, + int idxSplit, result_t* const y) const { for (int r = 0; r < idxSplit; ++r) { y[r] *= beta; active const* const row = _data + r * stride(); for (int c = 0; c < _cols; ++c) - y[r] += alpha1 * static_cast::type>(row[c]) * static_cast::type>(x[c]); + y[r] += alpha1 * static_cast::type>(row[c]) * + static_cast::type>(x[c]); } for (int r = idxSplit; r < _rows; ++r) @@ -398,7 +434,8 @@ class ActiveDenseMatrix y[r] *= beta; active const* const row = _data + r * stride(); for (int c = 0; c < _cols; ++c) - y[r] += alpha2 * static_cast::type>(row[c]) * static_cast::type>(x[c]); + y[r] += alpha2 * static_cast::type>(row[c]) * + static_cast::type>(x[c]); } } @@ -422,7 +459,7 @@ class ActiveDenseMatrix * The user has to take care that the memory is big enough to hold all * elements (beware of access violations). * All data is lost during this operation, the selected area is zeroed. - * + * * @param [in] rows The number of rows * @param [in] cols The number of columns */ @@ -434,8 +471,8 @@ class ActiveDenseMatrix protected: active* _data; //!< Pointer to the array in which the matrix is stored - int _rows; //!< Number of rows - int _cols; //!< Number of columns + int _rows; //!< Number of rows + int _cols; //!< Number of columns /** * @brief Initializes the matrix with the given memory of the given size @@ -443,7 +480,9 @@ class ActiveDenseMatrix * @param [in] rows Number of rows * @param [in] cols Number of columns */ - ActiveDenseMatrix(active* const data, int rows, int cols) CADET_NOEXCEPT : _data(data), _rows(rows), _cols(cols) { } + ActiveDenseMatrix(active* const data, int rows, int cols) CADET_NOEXCEPT : _data(data), _rows(rows), _cols(cols) + { + } /** * @brief Copies all values from the source to the local array @@ -459,4 +498,4 @@ class ActiveDenseMatrix } // namespace cadet -#endif // LIBCADET_ACTIVEDENSEMATRIX_HPP_ +#endif // LIBCADET_ACTIVEDENSEMATRIX_HPP_ diff --git a/src/libcadet/linalg/BandMatrix.cpp b/src/libcadet/linalg/BandMatrix.cpp index 3ddf6eeda..f56aee6e5 100644 --- a/src/libcadet/linalg/BandMatrix.cpp +++ b/src/libcadet/linalg/BandMatrix.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -23,13 +23,12 @@ namespace cadet namespace linalg { - // Anonymous namespace to hide implementation details in this translation unit namespace { -void bandMatrixVectorMultiplication(int rows, int upperBand, int lowerBand, int stride, - double const* const data, double alpha, double beta, double const* const x, double* const y) +void bandMatrixVectorMultiplication(int rows, int upperBand, int lowerBand, int stride, double const* const data, + double alpha, double beta, double const* const x, double* const y) { // Since LAPACK uses column-major storage and we use row-major, // we actually have constructed the transposed matrix. Thus, @@ -45,11 +44,11 @@ void bandMatrixVectorMultiplication(int rows, int upperBand, int lowerBand, int char trans[] = "T"; // LAPACK computes y <- alpha * A * x + beta * y - LapackMultiplyDenseBanded(trans, &n, &n, &kl, &ku, &alpha, const_cast(data), &ldab, const_cast(x), &inc, &beta, const_cast(y), &inc); + LapackMultiplyDenseBanded(trans, &n, &n, &kl, &ku, &alpha, const_cast(data), &ldab, const_cast(x), + &inc, &beta, const_cast(y), &inc); } -template -void bandMatrixToSparseString(std::ostream& out, const MatrixType& mt) +template void bandMatrixToSparseString(std::ostream& out, const MatrixType& mt) { std::ostringstream cols; std::ostringstream rows; @@ -119,8 +118,8 @@ void BandMatrix::multiplyVector(const double* const x, double alpha, double beta bandMatrixVectorMultiplication(_rows, _upperBand, _lowerBand, stride(), _data, alpha, beta, x, y); } -void BandMatrix::submatrixMultiplyVector(const double* const x, int startRow, int startDiag, - int numRows, int numCols, double alpha, double beta, double* const y) const +void BandMatrix::submatrixMultiplyVector(const double* const x, int startRow, int startDiag, int numRows, int numCols, + double alpha, double beta, double* const y) const { cadet_assert(startDiag >= -static_cast(_lowerBand)); cadet_assert(startDiag <= static_cast(_upperBand)); @@ -145,42 +144,43 @@ void BandMatrix::submatrixMultiplyVector(const double* const x, int startRow, in } // @todo Figure out why LAPACK implementation below does not work -/* - cadet_assert(startDiag >= -static_cast(_lowerBand)); - cadet_assert(startDiag <= static_cast(_upperBand)); - cadet_assert(startRow < _rows); - cadet_assert(startRow + numRows <= _rows); - - // Since LAPACK uses column-major storage and we use row-major, - // we actually have constructed the transposed matrix. Thus, - // upper and lower diagonals interchange. - lapackInt_t n = numRows; - lapackInt_t m = numCols; - lapackInt_t kl = _upperBand; - lapackInt_t ku = _lowerBand; - lapackInt_t ldab = static_cast(stride()); - lapackInt_t inc = 1; // Stride in vectors (here, elements are continuous without intermediate space) - - // For LAPACK the matrix looks like it's transposed. We, thus, - // multiply with the transposed matrix, which in the end uses the original matrix. - char trans[] = "T"; - - if (startDiag < 0) - { - ku = std::max(0, static_cast(ku) + startDiag); - kl = std::min(static_cast(numCols), kl - static_cast(startDiag)); - } - else if (startDiag > 0) - { - kl = std::max(0, static_cast(kl) - startDiag); - ku = std::min(static_cast(numCols), ku + static_cast(startDiag)); - } + /* + cadet_assert(startDiag >= -static_cast(_lowerBand)); + cadet_assert(startDiag <= static_cast(_upperBand)); + cadet_assert(startRow < _rows); + cadet_assert(startRow + numRows <= _rows); + + // Since LAPACK uses column-major storage and we use row-major, + // we actually have constructed the transposed matrix. Thus, + // upper and lower diagonals interchange. + lapackInt_t n = numRows; + lapackInt_t m = numCols; + lapackInt_t kl = _upperBand; + lapackInt_t ku = _lowerBand; + lapackInt_t ldab = static_cast(stride()); + lapackInt_t inc = 1; // Stride in vectors (here, elements are continuous without intermediate space) + + // For LAPACK the matrix looks like it's transposed. We, thus, + // multiply with the transposed matrix, which in the end uses the original matrix. + char trans[] = "T"; + + if (startDiag < 0) + { + ku = std::max(0, static_cast(ku) + startDiag); + kl = std::min(static_cast(numCols), kl - static_cast(startDiag)); + } + else if (startDiag > 0) + { + kl = std::max(0, static_cast(kl) - startDiag); + ku = std::min(static_cast(numCols), ku + static_cast(startDiag)); + } - int offset = 0; + int offset = 0; - // LAPACK computes y <- alpha * A * x + beta * y - LapackMultiplyDenseBanded(trans, &m, &n, &kl, &ku, &alpha, const_cast(_data) + startRow * stride() + offset, &ldab, const_cast(x), &inc, &beta, const_cast(y), &inc); -*/ + // LAPACK computes y <- alpha * A * x + beta * y + LapackMultiplyDenseBanded(trans, &m, &n, &kl, &ku, &alpha, const_cast(_data) + startRow * stride() + + offset, &ldab, const_cast(x), &inc, &beta, const_cast(y), &inc); + */ } void BandMatrix::scaleRows(double const* scalingFactors, int numRows) @@ -212,7 +212,7 @@ bool FactorizableBandMatrix::factorize() // upper and lower diagonals interchange. lapackInt_t n = _rows; lapackInt_t kl = _upperBand; - lapackInt_t ku = _lowerBand; + lapackInt_t ku = _lowerBand; lapackInt_t ldab = stride(); lapackInt_t flag = 0; @@ -239,7 +239,8 @@ bool FactorizableBandMatrix::solve(double* rhs) const // solve the transposed equation which uses the original matrix. char trans[] = "T"; - LapackSolveDenseBanded(trans, &n, &kl, &ku, &nrhs, const_cast(_data), &ldab, const_cast(_pivot), rhs, &n, &flag); + LapackSolveDenseBanded(trans, &n, &kl, &ku, &nrhs, const_cast(_data), &ldab, + const_cast(_pivot), rhs, &n, &flag); // If the flag is -i (for i > 0), the ith argument is invalid return flag == 0; @@ -276,7 +277,6 @@ std::ostream& operator<<(std::ostream& out, const FactorizableBandMatrix& fbm) return out; } +} // namespace linalg -} // namespace linalg - -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/linalg/BandMatrix.hpp b/src/libcadet/linalg/BandMatrix.hpp index 2775a228e..14a4d9e54 100644 --- a/src/libcadet/linalg/BandMatrix.hpp +++ b/src/libcadet/linalg/BandMatrix.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines a band matrix */ @@ -35,11 +35,9 @@ namespace linalg * @brief Iterates over rows of a banded matrix like BandMatrix or FactorizableBandMatrix * @tparam MatrixType Type of the matrix class, one of BandMatrix or FactorizableBandMatrix */ -template -class BandedRowIterator +template class BandedRowIterator { public: - /** * @brief Creates an empty BandedRowIterator pointing to nothing */ @@ -78,7 +76,8 @@ class BandedRowIterator * @param [in] mat MatrixType of the BandedRowIterator * @param [in] row Index of the row of the iterator points so */ - BandedRowIterator(MatrixType& mat, int row) CADET_NOEXCEPT : _matrix(&mat), _pos(_matrix->_data + row * _matrix->stride()) + BandedRowIterator(MatrixType& mat, int row) CADET_NOEXCEPT : _matrix(&mat), + _pos(_matrix->_data + row * _matrix->stride()) { #ifdef CADET_DEBUG _row = row; @@ -91,7 +90,9 @@ class BandedRowIterator * @param [in] row Index of the row of the iterator points so * @param [in] offset Additional offset */ - BandedRowIterator(MatrixType& mat, int row, int offset) CADET_NOEXCEPT : _matrix(&mat), _pos(_matrix->_data + row * _matrix->stride() + offset) + BandedRowIterator(MatrixType& mat, int row, int offset) CADET_NOEXCEPT + : _matrix(&mat), + _pos(_matrix->_data + row * _matrix->stride() + offset) { #ifdef CADET_DEBUG _row = row; @@ -104,7 +105,9 @@ class BandedRowIterator _row = cpy._row; #endif } - BandedRowIterator(const BandedRowIterator& cpy, int rowChange) CADET_NOEXCEPT : _matrix(cpy._matrix), _pos(cpy._pos + rowChange * static_cast(cpy._matrix->stride())) + BandedRowIterator(const BandedRowIterator& cpy, int rowChange) CADET_NOEXCEPT + : _matrix(cpy._matrix), + _pos(cpy._pos + rowChange * static_cast(cpy._matrix->stride())) { #ifdef CADET_DEBUG _row = cpy._row + rowChange; @@ -153,8 +156,7 @@ class BandedRowIterator * @brief Copies a row of another iterator to the row of this iterator * @param [in] it Iterator pointing to a row of a matrix */ - template - inline void copyRowFrom(const OtherIterator_t& it) + template inline void copyRowFrom(const OtherIterator_t& it) { cadet_assert(it.nonZeroColumnsPerRow() <= nonZeroColumnsPerRow()); cadet_assert(it.matrix().lowerBandwidth() == _matrix->lowerBandwidth()); @@ -204,24 +206,30 @@ class BandedRowIterator /** * @brief Accesses an element in the current row where the main diagonal is centered (index @c 0) * @details The @p diagonal determines the element in a row. A negative index indicates a lower diagonal, - * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main + * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main * diagonal is retrieved. - * + * * @param [in] diagonal Index of the diagonal (between negative lower bandwidth and upper bandwidth) - * + * * @return Matrix element at the given position */ - inline double& centered(int diagonal) { return (*this)(diagonal); } - inline double centered(int diagonal) const { return (*this)(diagonal); } + inline double& centered(int diagonal) + { + return (*this)(diagonal); + } + inline double centered(int diagonal) const + { + return (*this)(diagonal); + } /** * @brief Accesses an element in the current row where the lowest diagonal is indexed by @c 0 * @details In contrast to centered() the index of the column starts with the lowest diagonal (@c 0). * The main diagonal is, thus, retrieved for @c lowerBand and the highest upper diagonal * is returned for `lowerBand + upperBand`. - * + * * @param [in] col Index of the column (from @c 0 to `lowerBand + upperBand`) - * + * * @return Matrix element at the given position */ inline double& native(int col) @@ -254,8 +262,14 @@ class BandedRowIterator return _pos[diagonal + _matrix->_lowerBand]; } - inline double& operator[](int diagonal) { return (*this)(diagonal); } - inline double operator[](int diagonal) const { return (*this)(diagonal); } + inline double& operator[](int diagonal) + { + return (*this)(diagonal); + } + inline double operator[](int diagonal) const + { + return (*this)(diagonal); + } inline BandedRowIterator& operator++() CADET_NOEXCEPT { @@ -317,25 +331,34 @@ class BandedRowIterator * @brief Returns the underlying matrix this iterator is pointing into * @return Matrix this iterator is pointing into */ - inline const MatrixType& matrix() const CADET_NOEXCEPT { return *_matrix; } + inline const MatrixType& matrix() const CADET_NOEXCEPT + { + return *_matrix; + } /** * @brief Returns the number of (potentially) non-zero elements per row (the total bandwidth) * @return The number of (potentially) non-zero elements per row */ - inline int nonZeroColumnsPerRow() const CADET_NOEXCEPT { return _matrix->apparentStride(); } + inline int nonZeroColumnsPerRow() const CADET_NOEXCEPT + { + return _matrix->apparentStride(); + } #ifdef CADET_DEBUG /** * @brief Returns the index of the current row * @return Index of the current row */ - inline int row() const CADET_NOEXCEPT { return _row; } + inline int row() const CADET_NOEXCEPT + { + return _row; + } #endif private: MatrixType* _matrix; //!< Underlying matrix - double* _pos; //!< Current position, points to the lowest subdiagonal of a row + double* _pos; //!< Current position, points to the lowest subdiagonal of a row #ifdef CADET_DEBUG int _row; //!< Index of the current row #endif @@ -345,11 +368,9 @@ class BandedRowIterator * @brief Iterates over rows of a banded matrix like BandMatrix or FactorizableBandMatrix * @tparam MatrixType Type of the matrix class, one of BandMatrix or FactorizableBandMatrix */ -template -class ConstBandedRowIterator +template class ConstBandedRowIterator { public: - /** * @brief Creates an empty ConstBandedRowIterator pointing to nothing * @param [in] mat MatrixType of the ConstBandedRowIterator @@ -389,7 +410,9 @@ class ConstBandedRowIterator * @param [in] mat MatrixType of the ConstBandedRowIterator * @param [in] row Index of the row of the iterator points so */ - ConstBandedRowIterator(const MatrixType& mat, int row) CADET_NOEXCEPT : _matrix(&mat), _pos(_matrix->_data + row * _matrix->stride()) + ConstBandedRowIterator(const MatrixType& mat, int row) CADET_NOEXCEPT + : _matrix(&mat), + _pos(_matrix->_data + row * _matrix->stride()) { #ifdef CADET_DEBUG _row = row; @@ -402,7 +425,9 @@ class ConstBandedRowIterator * @param [in] row Index of the row of the iterator points so * @param [in] offset Additional offset */ - ConstBandedRowIterator(const MatrixType& mat, int row, int offset) CADET_NOEXCEPT : _matrix(&mat), _pos(_matrix->_data + row * _matrix->stride() + offset) + ConstBandedRowIterator(const MatrixType& mat, int row, int offset) CADET_NOEXCEPT + : _matrix(&mat), + _pos(_matrix->_data + row * _matrix->stride() + offset) { #ifdef CADET_DEBUG _row = row; @@ -415,7 +440,9 @@ class ConstBandedRowIterator _row = cpy._row; #endif } - ConstBandedRowIterator(const ConstBandedRowIterator& cpy, int rowChange) CADET_NOEXCEPT : _matrix(cpy._matrix), _pos(cpy._pos + rowChange * static_cast(cpy._matrix->stride())) + ConstBandedRowIterator(const ConstBandedRowIterator& cpy, int rowChange) CADET_NOEXCEPT + : _matrix(cpy._matrix), + _pos(cpy._pos + rowChange * static_cast(cpy._matrix->stride())) { #ifdef CADET_DEBUG _row = cpy._row + rowChange; @@ -453,23 +480,26 @@ class ConstBandedRowIterator /** * @brief Accesses an element in the current row where the main diagonal is centered (index @c 0) * @details The @p diagonal determines the element in a row. A negative index indicates a lower diagonal, - * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main + * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main * diagonal is retrieved. - * + * * @param [in] diagonal Index of the diagonal (between negative lower bandwidth and upper bandwidth) - * + * * @return Matrix element at the given position */ - inline double centered(int diagonal) const { return (*this)(diagonal); } + inline double centered(int diagonal) const + { + return (*this)(diagonal); + } /** * @brief Accesses an element in the current row where the lowest diagonal is indexed by @c 0 * @details In contrast to centered() the index of the column starts with the lowest diagonal (@c 0). * The main diagonal is, thus, retrieved for @c lowerBand and the highest upper diagonal * is returned for `lowerBand + upperBand`. - * + * * @param [in] col Index of the column (from @c 0 to `lowerBand + upperBand`) - * + * * @return Matrix element at the given position */ inline double native(int col) const @@ -487,7 +517,10 @@ class ConstBandedRowIterator return _pos[diagonal + _matrix->_lowerBand]; } - inline double operator[](int diagonal) const { return (*this)(diagonal); } + inline double operator[](int diagonal) const + { + return (*this)(diagonal); + } inline ConstBandedRowIterator& operator++() CADET_NOEXCEPT { @@ -549,32 +582,40 @@ class ConstBandedRowIterator * @brief Returns the underlying matrix this iterator is pointing into * @return Matrix this iterator is pointing into */ - inline const MatrixType& matrix() const CADET_NOEXCEPT { return *_matrix; } + inline const MatrixType& matrix() const CADET_NOEXCEPT + { + return *_matrix; + } /** * @brief Returns the number of (potentially) non-zero elements per row (the total bandwidth) * @return The number of (potentially) non-zero elements per row */ - inline int nonZeroColumnsPerRow() const CADET_NOEXCEPT { return _matrix->apparentStride(); } + inline int nonZeroColumnsPerRow() const CADET_NOEXCEPT + { + return _matrix->apparentStride(); + } #ifdef CADET_DEBUG /** * @brief Returns the index of the current row * @return Index of the current row */ - inline int row() const CADET_NOEXCEPT { return _row; } + inline int row() const CADET_NOEXCEPT + { + return _row; + } #endif private: MatrixType const* _matrix; //!< Underlying matrix - double const* _pos; //!< Current position, points to the lowest subdiagonal of a row + double const* _pos; //!< Current position, points to the lowest subdiagonal of a row #ifdef CADET_DEBUG int _row; //!< Index of the current row #endif }; -template -inline std::ostream& operator<<(std::ostream& out, const BandedRowIterator& bri) +template inline std::ostream& operator<<(std::ostream& out, const BandedRowIterator& bri) { const int stride = bri.matrix().apparentStride(); if (stride == 0) @@ -591,17 +632,16 @@ inline std::ostream& operator<<(std::ostream& out, const BandedRowIterator RowIterator; friend class BandedRowIterator; typedef ConstBandedRowIterator ConstRowIterator; @@ -621,19 +660,25 @@ class BandMatrix * @brief Creates an empty, unitialized band matrix * @details No memory is allocated for the matrix. Users have to call resize() first. */ - BandMatrix() CADET_NOEXCEPT : _data(nullptr), _lowerBand(0), _upperBand(0), _rows(0) { } + BandMatrix() CADET_NOEXCEPT : _data(nullptr), _lowerBand(0), _upperBand(0), _rows(0) + { + } ~BandMatrix() CADET_NOEXCEPT { delete[] _data; } - BandMatrix(const BandMatrix& cpy) : _data(new double[cpy.stride() * cpy._rows]), - _lowerBand(cpy._lowerBand), _upperBand(cpy._upperBand), _rows(cpy._rows) + BandMatrix(const BandMatrix& cpy) + : _data(new double[cpy.stride() * cpy._rows]), _lowerBand(cpy._lowerBand), _upperBand(cpy._upperBand), + _rows(cpy._rows) { copyValues(cpy._data); } - BandMatrix(BandMatrix&& cpy) CADET_NOEXCEPT : _data(cpy._data), _lowerBand(cpy._lowerBand), _upperBand(cpy._upperBand), _rows(cpy._rows) + BandMatrix(BandMatrix&& cpy) CADET_NOEXCEPT : _data(cpy._data), + _lowerBand(cpy._lowerBand), + _upperBand(cpy._upperBand), + _rows(cpy._rows) { cpy._data = nullptr; } @@ -670,7 +715,7 @@ class BandMatrix * @brief Resizes the matrix to the given size * @details All data is lost in this operation. Note that the allocated memory also * accounts for the main diagonal which is not counted in @p lowerBand or @p upperBand. - * + * * @param [in] rows Number of rows * @param [in] lowerBand Number of lower diagonals (excluding the main diagonal) * @param [in] upperBand Number of upper diagonals (excluding the main diagonal) @@ -693,7 +738,7 @@ class BandMatrix * @details The number of nonzero elements per row (i.e., sum of lower and upper bandwidth) * must not change. This way, all of the memory is reused. * Note that the data is not reset to @c 0.0 by this operation. - * + * * @param [in] lowerBand Number of lower diagonals (excluding the main diagonal) * @param [in] upperBand Number of upper diagonals (excluding the main diagonal) */ @@ -716,26 +761,32 @@ class BandMatrix /** * @brief Accesses an element in the matrix where the main diagonal is centered (index @c 0) * @details The @p diagonal determines the element in a row. A negative index indicates a lower diagonal, - * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main + * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main * diagonal is retrieved. - * + * * @param [in] row Index of the row * @param [in] diagonal Index of the diagonal (between negative lower bandwidth and upper bandwidth) - * + * * @return Matrix element at the given position */ - inline double& centered(int row, int diagonal) { return (*this)(row, diagonal); } - inline double centered(int row, int diagonal) const { return (*this)(row, diagonal); } + inline double& centered(int row, int diagonal) + { + return (*this)(row, diagonal); + } + inline double centered(int row, int diagonal) const + { + return (*this)(row, diagonal); + } /** * @brief Accesses an element in the matrix where the lowest diagonal is indexed by @c 0 * @details In contrast to centered() the index of the column starts with the lowest diagonal (@c 0). * The main diagonal is, thus, retrieved for @c lowerBand and the highest upper diagonal * is returned for `lowerBand + upperBand`. - * + * * @param [in] row Index of the row * @param [in] col Index of the column (from @c 0 to `lowerBand + upperBand`) - * + * * @return Matrix element at the given position */ inline double& native(int row, int col) @@ -773,33 +824,51 @@ class BandMatrix * @details The lower bandwidth is the number of diagonals below the main diagonal * @return Number of diagonals below the main diagonal */ - inline int lowerBandwidth() const CADET_NOEXCEPT { return _lowerBand; } - + inline int lowerBandwidth() const CADET_NOEXCEPT + { + return _lowerBand; + } + /** * @brief Returns the upper bandwidth * @details The upper bandwidth is the number of diagonals above the main diagonal * @return Number of diagonals above the main diagonal */ - inline int upperBandwidth() const CADET_NOEXCEPT { return _upperBand; } - + inline int upperBandwidth() const CADET_NOEXCEPT + { + return _upperBand; + } + /** * @brief Returns the number of rows * @return Number of rows */ - inline int rows() const CADET_NOEXCEPT { return _rows; } + inline int rows() const CADET_NOEXCEPT + { + return _rows; + } /** * @brief Provides direct access to the underlying memory * @return Pointer to the first element of the underlying array */ - inline double* data() CADET_NOEXCEPT { return _data; } - inline double const* data() const CADET_NOEXCEPT { return _data; } + inline double* data() CADET_NOEXCEPT + { + return _data; + } + inline double const* data() const CADET_NOEXCEPT + { + return _data; + } /** * @brief Returns the number of elements in a row * @return Number of elements in a row */ - inline int stride() const CADET_NOEXCEPT { return _lowerBand + _upperBand + 1; } + inline int stride() const CADET_NOEXCEPT + { + return _lowerBand + _upperBand + 1; + } /** * @brief Creates a RowIterator pointing to the given row @@ -846,8 +915,8 @@ class BandMatrix * @param [in] numCols Number of columns of the submatrix * @param [out] y Result of the submatrix-vector multiplication */ - inline void submatrixMultiplyVector(const double* const x, int startRow, int startDiag, - int numRows, int numCols, double* const y) const + inline void submatrixMultiplyVector(const double* const x, int startRow, int startDiag, int numRows, int numCols, + double* const y) const { submatrixMultiplyVector(x, startRow, startDiag, numRows, numCols, 1.0, 0.0, y); } @@ -865,8 +934,8 @@ class BandMatrix /** * @brief Multiplies a submatrix @f$ A @f$ with a given vector @f$ x @f$ and adds it to another vector using LAPACK - * @details Computes @f$ y = \alpha Ax + \beta y @f$, where @f$ A @f$ is a submatrix of this matrix and @f$ x @f$ is given. - * The submatrix is given by its first row and diagonal and its number of rows and columns. + * @details Computes @f$ y = \alpha Ax + \beta y @f$, where @f$ A @f$ is a submatrix of this matrix and @f$ x @f$ is + * given. The submatrix is given by its first row and diagonal and its number of rows and columns. * @param [in] x Vector the submatrix is multiplied with * @param [in] startRow Index of the first row of the submatrix * @param [in] startDiag Diagonal index of the first element of the submatrix @@ -876,8 +945,8 @@ class BandMatrix * @param [in] beta Factor @f$ \beta @f$ in front of @f$ y @f$ * @param [out] y Result of the submatrix-vector multiplication */ - void submatrixMultiplyVector(const double* const x, int startRow, int startDiag, - int numRows, int numCols, double alpha, double beta, double* const y) const; + void submatrixMultiplyVector(const double* const x, int startRow, int startDiag, int numRows, int numCols, + double alpha, double beta, double* const y) const; /** * @brief Multiplies the matrix @f$ A @f$ with a given vector @f$ x @f$ and adds it to another vector using LAPACK @@ -894,7 +963,10 @@ class BandMatrix * @details This corresponds to multiplying by a diagonal matrix from the left (i.e., @f$ D^{-1} A @f$). * @param [in] scalingFactors Vector with scaling factor for each row */ - inline void scaleRows(double const* scalingFactors) { scaleRows(scalingFactors, rows()); } + inline void scaleRows(double const* scalingFactors) + { + scaleRows(scalingFactors, rows()); + } /** * @brief Scales the first rows by dividing them with a given factor @@ -910,7 +982,10 @@ class BandMatrix * @details This can be used to equilibrate the matrix by calling scaleRows(). * @param [out] scalingFactors Vector in which the row scaling factors are written */ - inline void rowScaleFactors(double* scalingFactors) const { rowScaleFactors(scalingFactors, rows()); } + inline void rowScaleFactors(double* scalingFactors) const + { + rowScaleFactors(scalingFactors, rows()); + } /** * @brief Computes row scaling factors for some rows such that the largest absolute value in each row is 1 @@ -922,16 +997,19 @@ class BandMatrix void rowScaleFactors(double* scalingFactors, int numRows) const; protected: - double* _data; //!< Pointer to the array in which the matrix is stored + double* _data; //!< Pointer to the array in which the matrix is stored int _lowerBand; //!< Lower bandwidth excluding main diagonal int _upperBand; //!< Upper bandwidth excluding main diagonal - int _rows; //!< Number of rows + int _rows; //!< Number of rows /** * @brief Returns the number of true elements in a row (same as stride()) * @return Number of true elements in a row */ - inline int apparentStride() const CADET_NOEXCEPT { return stride(); } + inline int apparentStride() const CADET_NOEXCEPT + { + return stride(); + } /** * @brief Copies all values from the source to the local array @@ -945,31 +1023,29 @@ class BandMatrix std::ostream& operator<<(std::ostream& out, const BandMatrix& bm); - /** * @brief Represents a factorizable band matrix with given number of rows and diagonals * @details LAPACK uses column-major storage, whereas this class uses row-major. * Thus, what we call a row here is actually a column for LAPACK. - * Concluding, we have to use the transposed LAPACK operations for + * Concluding, we have to use the transposed LAPACK operations for * solution and matrix-vector multiplication. The ordering is irrelevant * for the factorization. - * + * * Because of the transposition induced by the differing ordering, - * the number of upper and lower diagonals switches (e.g., upper diagonals + * the number of upper and lower diagonals switches (e.g., upper diagonals * are transposed lower diagonals). The ordering of the elements inside * one (original) row is maintained (i.e., the first element in a row becomes * the first element in a column and the last element in a row transposes to * the last element in a column). - * + * * LAPACK needs additional space to hold intermediate values when calling a * factorization routine. This space, and the required pivoting arrays, are * also stored in this class. -* @todo Refactor and combine code with BandMatrix in order to save LOC - */ + * @todo Refactor and combine code with BandMatrix in order to save LOC + */ class FactorizableBandMatrix { public: - typedef BandedRowIterator RowIterator; friend class BandedRowIterator; typedef ConstBandedRowIterator ConstRowIterator; @@ -979,22 +1055,34 @@ class FactorizableBandMatrix * @brief Creates an empty, unitialized band matrix * @details No memory is allocated for the matrix. Users have to call resize() first. */ - FactorizableBandMatrix() CADET_NOEXCEPT : _data(nullptr), _lowerBand(0), _upperBand(0), _rows(0), _capacity(0), _pivot(nullptr) { } + FactorizableBandMatrix() CADET_NOEXCEPT : _data(nullptr), + _lowerBand(0), + _upperBand(0), + _rows(0), + _capacity(0), + _pivot(nullptr) + { + } ~FactorizableBandMatrix() CADET_NOEXCEPT { delete[] _pivot; delete[] _data; } - FactorizableBandMatrix(const FactorizableBandMatrix& cpy) : _data(new double[cpy.stride() * cpy._rows]), - _lowerBand(cpy._lowerBand), _upperBand(cpy._upperBand), _rows(cpy._rows), _capacity(cpy._capacity), _pivot(new lapackInt_t[cpy._rows]) + FactorizableBandMatrix(const FactorizableBandMatrix& cpy) + : _data(new double[cpy.stride() * cpy._rows]), _lowerBand(cpy._lowerBand), _upperBand(cpy._upperBand), + _rows(cpy._rows), _capacity(cpy._capacity), _pivot(new lapackInt_t[cpy._rows]) { copyValues(cpy._data); copyPivot(cpy._pivot); } - FactorizableBandMatrix(FactorizableBandMatrix&& cpy) CADET_NOEXCEPT : _data(cpy._data), _lowerBand(cpy._lowerBand), _upperBand(cpy._upperBand), - _rows(cpy._rows), _capacity(cpy._capacity), _pivot(cpy._pivot) + FactorizableBandMatrix(FactorizableBandMatrix&& cpy) CADET_NOEXCEPT : _data(cpy._data), + _lowerBand(cpy._lowerBand), + _upperBand(cpy._upperBand), + _rows(cpy._rows), + _capacity(cpy._capacity), + _pivot(cpy._pivot) { cpy._data = nullptr; cpy._pivot = nullptr; @@ -1048,7 +1136,7 @@ class FactorizableBandMatrix * @brief Resizes the matrix to the given size * @details All data is lost in this operation. Note that the allocated memory also * accounts for the main diagonal which is not counted in @p lowerBand or @p upperBand. - * + * * @param [in] rows Number of rows * @param [in] lowerBand Number of lower diagonals (excluding the main diagonal) * @param [in] upperBand Number of upper diagonals (excluding the main diagonal) @@ -1093,7 +1181,7 @@ class FactorizableBandMatrix * @brief Repartitions the matrix by changing lower and upper bandwidth * @details There has to be enough capacity for the new matrix size. Use resize() if the capacity is too small. * Note that the data is not reset to @c 0.0 by this operation. - * + * * @param [in] lowerBand Number of lower diagonals (excluding the main diagonal) * @param [in] upperBand Number of upper diagonals (excluding the main diagonal) */ @@ -1143,8 +1231,8 @@ class FactorizableBandMatrix { // Copy data over and take into account that the local storage is larger and that // LAPACK needs the first _upperBand values in each row as additional storage - const double *src = bdm.data(); - double *local = _data + _upperBand; + const double* src = bdm.data(); + double* local = _data + _upperBand; const int as = apparentStride(); const int ls = stride(); for (int i = 0; i < _rows; ++i, local += ls, src += as) @@ -1155,8 +1243,8 @@ class FactorizableBandMatrix else { // Only copy what bdm has and zero out the remaining entries - const double *src = bdm.data(); - double *local = _data + _upperBand; + const double* src = bdm.data(); + double* local = _data + _upperBand; const int srcStride = bdm.stride(); const int diff = apparentStride() - srcStride; const int ls = stride(); @@ -1180,26 +1268,32 @@ class FactorizableBandMatrix /** * @brief Accesses an element in the matrix where the main diagonal is centered (index @c 0) * @details The @p diagonal determines the element in a row. A negative index indicates a lower diagonal, - * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main + * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main * diagonal is retrieved. - * + * * @param [in] row Index of the row * @param [in] diagonal Index of the diagonal (between negative lower bandwidth and upper bandwidth) - * + * * @return Matrix element at the given position */ - inline double& centered(int row, int diagonal) { return (*this)(row, diagonal); } - inline double centered(int row, int diagonal) const { return (*this)(row, diagonal); } + inline double& centered(int row, int diagonal) + { + return (*this)(row, diagonal); + } + inline double centered(int row, int diagonal) const + { + return (*this)(row, diagonal); + } /** * @brief Accesses an element in the matrix where the lowest diagonal is indexed by @c 0 * @details In contrast to centered() the index of the column starts with the lowest diagonal (@c 0). * The main diagonal is, thus, retrieved for @c lowerBand and the highest upper diagonal * is returned for `lowerBand + upperBand`. - * + * * @param [in] row Index of the row * @param [in] col Index of the column (from @c 0 to `lowerBand + upperBand`) - * + * * @return Matrix element at the given position */ inline double& native(int row, int col) @@ -1239,36 +1333,60 @@ class FactorizableBandMatrix * @details The lower bandwidth is the number of diagonals below the main diagonal * @return Number of diagonals below the main diagonal */ - inline int lowerBandwidth() const CADET_NOEXCEPT { return _lowerBand; } - + inline int lowerBandwidth() const CADET_NOEXCEPT + { + return _lowerBand; + } + /** * @brief Returns the upper bandwidth * @details The upper bandwidth is the number of diagonals above the main diagonal * @return Number of diagonals above the main diagonal */ - inline int upperBandwidth() const CADET_NOEXCEPT { return _upperBand; } - + inline int upperBandwidth() const CADET_NOEXCEPT + { + return _upperBand; + } + /** * @brief Returns the number of rows * @return Number of rows */ - inline int rows() const CADET_NOEXCEPT { return _rows; } + inline int rows() const CADET_NOEXCEPT + { + return _rows; + } /** * @brief Provides direct access to the underlying memory * @return Pointer to the first element of the underlying array */ - inline double* data() CADET_NOEXCEPT { return _data; } - inline double const* data() const CADET_NOEXCEPT { return _data; } + inline double* data() CADET_NOEXCEPT + { + return _data; + } + inline double const* data() const CADET_NOEXCEPT + { + return _data; + } - inline lapackInt_t* pivot() CADET_NOEXCEPT { return _pivot; } - inline lapackInt_t const* pivot() const CADET_NOEXCEPT { return _pivot; } + inline lapackInt_t* pivot() CADET_NOEXCEPT + { + return _pivot; + } + inline lapackInt_t const* pivot() const CADET_NOEXCEPT + { + return _pivot; + } /** * @brief Returns the total number of elements in a row including additional storage for factorization * @return Total number of elements in a row */ - inline int stride() const CADET_NOEXCEPT { return stride(_lowerBand, _upperBand); } + inline int stride() const CADET_NOEXCEPT + { + return stride(_lowerBand, _upperBand); + } /** * @brief Creates a RowIterator pointing to the given row @@ -1280,7 +1398,7 @@ class FactorizableBandMatrix cadet_assert(idx < _rows); return RowIterator(*this, idx, _upperBand); } - + inline ConstRowIterator row(int idx) const { cadet_assert(idx < _rows); @@ -1331,7 +1449,8 @@ class FactorizableBandMatrix /** * @brief Uses the factorized matrix to solve the equation @f$ Ax = b @f$ with LAPACK * @details Before the equation can be solved, the matrix has to be factorized first by calling factorize(). - * @param [in,out] rhs On entry pointer to the right hand side vector @f$ b @f$ of the equation, on exit the solution @f$ x @f$ + * @param [in,out] rhs On entry pointer to the right hand side vector @f$ b @f$ of the equation, on exit the + * solution @f$ x @f$ * @return @c true if the solution process was successful, otherwise @c false */ bool solve(double* rhs) const; @@ -1343,7 +1462,8 @@ class FactorizableBandMatrix * In order to solve the equation system, the right hand side has to be scaled accordingly. * This is handled automatically by passing the required scaling factors. * @param [in] scalingFactors Vector with scaling factor for each row - * @param [in,out] rhs On entry pointer to the right hand side vector @f$ b @f$ of the equation, on exit the solution @f$ x @f$ + * @param [in,out] rhs On entry pointer to the right hand side vector @f$ b @f$ of the equation, on exit the + * solution @f$ x @f$ * @return @c true if the solution process was successful, otherwise @c false */ bool solve(double const* scalingFactors, double* rhs) const; @@ -1353,7 +1473,10 @@ class FactorizableBandMatrix * @details This corresponds to multiplying by a diagonal matrix from the left (i.e., @f$ D^{-1} A @f$). * @param [in] scalingFactors Vector with scaling factor for each row */ - inline void scaleRows(double const* scalingFactors) { scaleRows(scalingFactors, rows()); } + inline void scaleRows(double const* scalingFactors) + { + scaleRows(scalingFactors, rows()); + } /** * @brief Scales the first rows by dividing them with a given factor @@ -1369,7 +1492,10 @@ class FactorizableBandMatrix * @details This can be used to equilibrate the matrix by calling scaleRows(). * @param [out] scalingFactors Vector in which the row scaling factors are written */ - inline void rowScaleFactors(double* scalingFactors) const { rowScaleFactors(scalingFactors, rows()); } + inline void rowScaleFactors(double* scalingFactors) const + { + rowScaleFactors(scalingFactors, rows()); + } /** * @brief Computes row scaling factors for some rows such that the largest absolute value in each row is 1 @@ -1381,11 +1507,11 @@ class FactorizableBandMatrix void rowScaleFactors(double* scalingFactors, int numRows) const; protected: - double* _data; //!< Pointer to the array in which the matrix is stored - int _lowerBand; //!< Lower bandwidth excluding main diagonal - int _upperBand; //!< Upper bandwidth excluding main diagonal - int _rows; //!< Number of rows - int _capacity; //!< Allocated memory in sizeof(double) + double* _data; //!< Pointer to the array in which the matrix is stored + int _lowerBand; //!< Lower bandwidth excluding main diagonal + int _upperBand; //!< Upper bandwidth excluding main diagonal + int _rows; //!< Number of rows + int _capacity; //!< Allocated memory in sizeof(double) lapackInt_t* _pivot; //!< Pointer to an array which is used for pivoting by factorization methods /** @@ -1394,13 +1520,19 @@ class FactorizableBandMatrix * @param [in] upperBand Number of upper diagonals (excluding the main diagonal) * @return Total number of elements in a row */ - inline int stride(int lowerBand, int upperBand) const CADET_NOEXCEPT { return lowerBand + 2 * upperBand + 1; } + inline int stride(int lowerBand, int upperBand) const CADET_NOEXCEPT + { + return lowerBand + 2 * upperBand + 1; + } /** * @brief Returns the number of true elements in a row not counting additional storage for factorization * @return Number of true elements in a row */ - inline int apparentStride() const CADET_NOEXCEPT { return _lowerBand + _upperBand + 1; } + inline int apparentStride() const CADET_NOEXCEPT + { + return _lowerBand + _upperBand + 1; + } /** * @brief Copies all values from the source to the local array @@ -1423,9 +1555,8 @@ class FactorizableBandMatrix std::ostream& operator<<(std::ostream& out, const FactorizableBandMatrix& fbm); - } // namespace linalg } // namespace cadet -#endif // LIBCADET_BANDMATRIX_HPP_ +#endif // LIBCADET_BANDMATRIX_HPP_ diff --git a/src/libcadet/linalg/BandedEigenSparseRowIterator.hpp b/src/libcadet/linalg/BandedEigenSparseRowIterator.hpp index d30f7e13a..3522a6bbc 100644 --- a/src/libcadet/linalg/BandedEigenSparseRowIterator.hpp +++ b/src/libcadet/linalg/BandedEigenSparseRowIterator.hpp @@ -22,14 +22,16 @@ namespace cadet namespace linalg { - class BandedEigenSparseRowIterator { public: /** * @brief Creates a ConstBandedSparseRowIterator pointing nowhere */ - BandedEigenSparseRowIterator() : _matrix(nullptr), _values(nullptr), _colIdx(nullptr), _row(-1), _numNonZero(0), _dummy(0.0) { } + BandedEigenSparseRowIterator() + : _matrix(nullptr), _values(nullptr), _colIdx(nullptr), _row(-1), _numNonZero(0), _dummy(0.0) + { + } /** * @brief Creates a BandedSparseRowIterator of the given matrix row @@ -38,11 +40,13 @@ class BandedEigenSparseRowIterator */ BandedEigenSparseRowIterator(Eigen::SparseMatrix& mat, int row) : _matrix(&mat), _values(valuesOfRow(mat, row)), _colIdx(columnIndicesOfRow(mat, row)), _row(row), - _numNonZero(getInnerNumberOfNonZeros(mat, row)), _dummy(0.0) + _numNonZero(getInnerNumberOfNonZeros(mat, row)), _dummy(0.0) { } - ~BandedEigenSparseRowIterator() CADET_NOEXCEPT { } + ~BandedEigenSparseRowIterator() CADET_NOEXCEPT + { + } // Default copy and assignment semantics BandedEigenSparseRowIterator(const BandedEigenSparseRowIterator& cpy) = default; @@ -69,32 +73,31 @@ class BandedEigenSparseRowIterator * @details Assumes the same sparsity pattern of source and destination row. * @param [in] it Iterator pointing to a row of a matrix */ - template - inline void copyRowFrom(const OtherIterator_t& it) + template inline void copyRowFrom(const OtherIterator_t& it) { cadet_assert(_numNonZero == it.numNonZeros()); std::copy(it.data(), it.data() + _numNonZero, _values); } /** - * @brief Adds the given array to the current row - * @details Performs the operation @f$ y = y + \alpha x @f$, where @f$ x @f$ may only be a - * subset of the current row the iterator points to. The start of the subset is - * given by @p startDiag. The subset has to fully fit into the matrix row. - * @param [in] row Pointer to array @f$ x @f$ that is added to the given row @f$ y @f$ - * @param [in] startDiag Index of the diagonal at which the row is added - * @param [in] length Length of the array - * @param [in] factor Factor @f$ \alpha @f$ - */ + * @brief Adds the given array to the current row + * @details Performs the operation @f$ y = y + \alpha x @f$, where @f$ x @f$ may only be a + * subset of the current row the iterator points to. The start of the subset is + * given by @p startDiag. The subset has to fully fit into the matrix row. + * @param [in] row Pointer to array @f$ x @f$ that is added to the given row @f$ y @f$ + * @param [in] startDiag Index of the diagonal at which the row is added + * @param [in] length Length of the array + * @param [in] factor Factor @f$ \alpha @f$ + */ inline void addArray(double const* row, int startDiag, int length, double factor) { // shift to column index to start with int idx = startDiag - _row; - for (int i = 0; i < length; i++) { + for (int i = 0; i < length; i++) + { _values[idx + i] = factor * row[i]; } - } /** @@ -107,8 +110,14 @@ class BandedEigenSparseRowIterator * * @return Matrix element at the given position */ - inline double& centered(int diagonal) { return native(_row + diagonal); } - inline double centered(int diagonal) const { return native(_row + diagonal); } + inline double& centered(int diagonal) + { + return native(_row + diagonal); + } + inline double centered(int diagonal) const + { + return native(_row + diagonal); + } /** * @brief Accesses an element in the current row where the lowest diagonal is indexed by @c 0 @@ -148,11 +157,23 @@ class BandedEigenSparseRowIterator return 0.0; } - inline double& operator()(int diagonal) { return centered(diagonal); } - inline double operator()(int diagonal) const { return centered(diagonal); } + inline double& operator()(int diagonal) + { + return centered(diagonal); + } + inline double operator()(int diagonal) const + { + return centered(diagonal); + } - inline double& operator[](int diagonal) { return centered(diagonal); } - inline double operator[](int diagonal) const { return centered(diagonal); } + inline double& operator[](int diagonal) + { + return centered(diagonal); + } + inline double operator[](int diagonal) const + { + return centered(diagonal); + } inline BandedEigenSparseRowIterator& operator++() CADET_NOEXCEPT { @@ -206,29 +227,43 @@ class BandedEigenSparseRowIterator * @brief Returns the underlying matrix this iterator is pointing into * @return Matrix this iterator is pointing into */ - inline const Eigen::SparseMatrix& matrix() const CADET_NOEXCEPT { return *_matrix; } + inline const Eigen::SparseMatrix& matrix() const CADET_NOEXCEPT + { + return *_matrix; + } /** * @brief Returns the index of the current row * @return Index of the current row */ - inline int row() const CADET_NOEXCEPT { return _row; } + inline int row() const CADET_NOEXCEPT + { + return _row; + } /** * @brief Returns the number of non-zero entries in this row * @return Number of non-zero entries in this row */ - inline int numNonZeros() const { return _numNonZero; } + inline int numNonZeros() const + { + return _numNonZero; + } /** * @brief Returns an array of the matrix entries in this row * @return Array with matrix entries in this row */ - inline double* data() { return _values; } - inline double const* data() const { return _values; } + inline double* data() + { + return _values; + } + inline double const* data() const + { + return _values; + } protected: - inline void updateOnRowChange() { _values = valuesOfRow(*_matrix, _row); @@ -236,15 +271,18 @@ class BandedEigenSparseRowIterator _numNonZero = getInnerNumberOfNonZeros(*_matrix, _row); } - inline double* valuesOfRow(Eigen::SparseMatrix& mat, int row) { + inline double* valuesOfRow(Eigen::SparseMatrix& mat, int row) + { return mat.valuePtr() + mat.outerIndexPtr()[row]; } - inline const int* columnIndicesOfRow(Eigen::SparseMatrix& mat, int row) { + inline const int* columnIndicesOfRow(Eigen::SparseMatrix& mat, int row) + { return mat.innerIndexPtr() + mat.outerIndexPtr()[row]; } - inline int getInnerNumberOfNonZeros(Eigen::SparseMatrix& mat, int row) { + inline int getInnerNumberOfNonZeros(Eigen::SparseMatrix& mat, int row) + { return mat.outerIndexPtr()[row + 1] - mat.outerIndexPtr()[row]; } @@ -260,4 +298,4 @@ class BandedEigenSparseRowIterator } // namespace cadet -#endif // LIBCADET_BANDEDEIGENSPARSEROWITERATOR_HPP_ \ No newline at end of file +#endif // LIBCADET_BANDEDEIGENSPARSEROWITERATOR_HPP_ diff --git a/src/libcadet/linalg/CompressedSparseMatrix.cpp b/src/libcadet/linalg/CompressedSparseMatrix.cpp index b8022fe7e..954397df0 100644 --- a/src/libcadet/linalg/CompressedSparseMatrix.cpp +++ b/src/libcadet/linalg/CompressedSparseMatrix.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -65,7 +65,6 @@ std::ostream& operator<<(std::ostream& out, const CompressedSparseMatrix& sm) rows << "rows = ["; elems << "elems = ["; - for (int row = 0; row < sm.rows(); ++row) { sparse_int_t const* const colIdx = sm.columnIndicesOfRow(row); @@ -139,15 +138,15 @@ void CompressedSparseMatrix::copyFromSubPattern(const CompressedSparseMatrix& ma sparse_int_t curIdx = _rowStart[row]; // Iterate over source elements in row - for (sparse_int_t i = mat._rowStart[row]; i < mat._rowStart[row+1]; ++i) + for (sparse_int_t i = mat._rowStart[row]; i < mat._rowStart[row + 1]; ++i) { // Find destination column index mat._colIdx[i] in this row - // Due to the column indices being sorted, this should be reasonably fast + // Due to the column indices being sorted, this should be reasonably fast // TODO: Check whether to replace linear with binary search while (_colIdx[curIdx] != mat._colIdx[i]) { ++curIdx; - cadet_assert(curIdx < _rowStart[row+1]); + cadet_assert(curIdx < _rowStart[row + 1]); } _values[curIdx] = mat._values[i]; @@ -158,7 +157,7 @@ void CompressedSparseMatrix::copyFromSubPattern(const CompressedSparseMatrix& ma void CompressedSparseMatrix::copyRowToRowSamePattern(const CompressedSparseMatrix& mat, int srcRow, int destRow) { const sparse_int_t start = mat._rowStart[destRow]; - const sparse_int_t end = mat._rowStart[destRow+1]; + const sparse_int_t end = mat._rowStart[destRow + 1]; std::copy(mat._values.begin() + start, mat._values.begin() + end, _values.begin() + _rowStart[destRow]); } @@ -167,15 +166,15 @@ void CompressedSparseMatrix::copyRowToRowSubPattern(const CompressedSparseMatrix sparse_int_t curIdx = _rowStart[destRow]; // Iterate over source elements in row - for (sparse_int_t i = mat._rowStart[srcRow]; i < mat._rowStart[srcRow+1]; ++i) + for (sparse_int_t i = mat._rowStart[srcRow]; i < mat._rowStart[srcRow + 1]; ++i) { // Find destination column index mat._colIdx[i] in this row - // Due to the column indices being sorted, this should be reasonably fast + // Due to the column indices being sorted, this should be reasonably fast // TODO: Check whether to replace linear with binary search while (_colIdx[curIdx] != mat._colIdx[i]) { ++curIdx; - cadet_assert(curIdx < _rowStart[destRow+1]); + cadet_assert(curIdx < _rowStart[destRow + 1]); } _values[curIdx] = mat._values[i]; @@ -224,6 +223,6 @@ void CompressedSparseMatrix::multiplyVector(double const* const x, double alpha, } } -} // namespace linalg +} // namespace linalg -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/linalg/CompressedSparseMatrix.hpp b/src/libcadet/linalg/CompressedSparseMatrix.hpp index 9a96bb1a4..0e8ddf71b 100644 --- a/src/libcadet/linalg/CompressedSparseMatrix.hpp +++ b/src/libcadet/linalg/CompressedSparseMatrix.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines a compressed sparse matrix */ @@ -40,7 +40,6 @@ class SparsityPatternRowIterator; class SparsityPattern { public: - /** * @brief Creates an empty SparsityPattern with preallocated memory * @details Uses an estimate of the average number of non-zero entries per row to @@ -48,9 +47,14 @@ class SparsityPattern * @param [in] nRows Number of rows in the square matrix * @param [in] averageNumNonZeros Average number of non-zero entries per row */ - SparsityPattern(int nRows, int averageNumNonZeros) : _rows(nRows, MatrixRowPattern(averageNumNonZeros)), _numNonZero(0) { } + SparsityPattern(int nRows, int averageNumNonZeros) + : _rows(nRows, MatrixRowPattern(averageNumNonZeros)), _numNonZero(0) + { + } - ~SparsityPattern() CADET_NOEXCEPT { } + ~SparsityPattern() CADET_NOEXCEPT + { + } // Default copy and assignment semantics SparsityPattern(const SparsityPattern& cpy) = default; @@ -68,14 +72,20 @@ class SparsityPattern * @brief Returns the number of non-zero entries in this pattern * @return Number of non-zero entries in the matrix */ - inline int numNonZeros() const CADET_NOEXCEPT { return _numNonZero; } + inline int numNonZeros() const CADET_NOEXCEPT + { + return _numNonZero; + } /** * @brief Returns the number of rows (or columns) of this square matrix * @details It does not matter whether some rows are fully empty. * @return Number of rows (or columns) of this square matrix */ - inline int rows() const CADET_NOEXCEPT { return _rows.size(); } + inline int rows() const CADET_NOEXCEPT + { + return _rows.size(); + } /** * @brief Adds an entry to the pattern @@ -121,7 +131,6 @@ class SparsityPattern SparsityPatternRowIterator row(int idxRow) CADET_NOEXCEPT; protected: - /** * @brief Stores column indices of a matrix row * @details The column indices are always sorted ascendingly. @@ -129,20 +138,26 @@ class SparsityPattern class MatrixRowPattern { public: - /** * @brief Creates an empty MatrixRowPattern */ - MatrixRowPattern() CADET_NOEXCEPT : _colIdx(0) { } + MatrixRowPattern() CADET_NOEXCEPT : _colIdx(0) + { + } /** * @brief Creates an empty MatrixRowPattern with preallocated memory * @details Preallocates memory. * @param [in] averageNumNonZeros Average number of non-zero entries in this row */ - MatrixRowPattern(std::size_t averageNumNonZeros) : _colIdx(0) { _colIdx.reserve(averageNumNonZeros); } + MatrixRowPattern(std::size_t averageNumNonZeros) : _colIdx(0) + { + _colIdx.reserve(averageNumNonZeros); + } - ~MatrixRowPattern() CADET_NOEXCEPT { } + ~MatrixRowPattern() CADET_NOEXCEPT + { + } // Default copy and assignment semantics MatrixRowPattern(const MatrixRowPattern& cpy) = default; @@ -175,7 +190,7 @@ class SparsityPattern const auto it = std::upper_bound(_colIdx.begin(), _colIdx.end(), column); // Check whether this index already exists - if ((it != _colIdx.begin()) && (*(it-1) == column)) + if ((it != _colIdx.begin()) && (*(it - 1) == column)) return false; _colIdx.insert(it, column); @@ -196,20 +211,26 @@ class SparsityPattern * @brief Returns a sorted array of column indices * @return Sorted array of column indices */ - const std::vector& columnIndices() const CADET_NOEXCEPT { return _colIdx; } + const std::vector& columnIndices() const CADET_NOEXCEPT + { + return _colIdx; + } /** * @brief Returns the number of non-zero entries in this row * @return Number of non-zero entries in this row */ - int numNonZeros() const CADET_NOEXCEPT { return _colIdx.size(); } + int numNonZeros() const CADET_NOEXCEPT + { + return _colIdx.size(); + } protected: std::vector _colIdx; //!< Sorted column indices of non-zero elements in this row }; std::vector _rows; //!< Rows with their non-zero indices - int _numNonZero; //!< Number of non-zero entries in the matrix + int _numNonZero; //!< Number of non-zero entries in the matrix }; /** @@ -223,22 +244,30 @@ class SparsityPatternRowIterator /** * @brief Creates an empty SparsityPatternRowIterator pointing to nothing */ - SparsityPatternRowIterator() CADET_NOEXCEPT : _pattern(nullptr), _row(-1) { } + SparsityPatternRowIterator() CADET_NOEXCEPT : _pattern(nullptr), _row(-1) + { + } /** * @brief Creates a SparsityPatternRowIterator for the given SparsityPattern * @param [in] pattern SparsityPattern of the SparsityPatternRowIterator */ - SparsityPatternRowIterator(SparsityPattern& pattern) CADET_NOEXCEPT : _pattern(&pattern), _row(0) { } + SparsityPatternRowIterator(SparsityPattern& pattern) CADET_NOEXCEPT : _pattern(&pattern), _row(0) + { + } /** * @brief Creates a SparsityPatternRowIterator for the given SparsityPattern * @param [in] pattern SparsityPattern of the SparsityPatternRowIterator * @param [in] row Index of the row of the iterator points so */ - SparsityPatternRowIterator(SparsityPattern& pattern, int row) CADET_NOEXCEPT : _pattern(&pattern), _row(row) { } + SparsityPatternRowIterator(SparsityPattern& pattern, int row) CADET_NOEXCEPT : _pattern(&pattern), _row(row) + { + } - ~SparsityPatternRowIterator() CADET_NOEXCEPT { } + ~SparsityPatternRowIterator() CADET_NOEXCEPT + { + } // Default copy and assignment semantics SparsityPatternRowIterator(const SparsityPatternRowIterator& cpy) = default; @@ -270,11 +299,11 @@ class SparsityPatternRowIterator /** * @brief Accesses an element in the current row where the main diagonal is centered (index @c 0) * @details The @p diagonal determines the element in a row. A negative index indicates a lower diagonal, - * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main + * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main * diagonal is retrieved. - * + * * @param [in] diagonal Index of the diagonal (between negative lower bandwidth and upper bandwidth) - * + * * @return Dummy value or @c 0.0 */ inline double& centered(int diagonal) @@ -285,16 +314,19 @@ class SparsityPatternRowIterator _pattern->add(_row, _row + diagonal); return _dummy; } - inline double centered(int diagonal) const { return 0.0; } + inline double centered(int diagonal) const + { + return 0.0; + } /** * @brief Accesses an element in the current row where the lowest diagonal is indexed by @c 0 * @details In contrast to centered() the index of the column starts with the lowest diagonal (@c 0). * The main diagonal is, thus, retrieved for @c lowerBand and the highest upper diagonal * is returned for `lowerBand + upperBand`. - * + * * @param [in] col Index of the column (from @c 0 to `lowerBand + upperBand`) - * + * * @return Matrix element at the given position */ inline double& native(int col) @@ -306,7 +338,10 @@ class SparsityPatternRowIterator return _dummy; } - inline double native(int col) const { return 0.0; } + inline double native(int col) const + { + return 0.0; + } inline double& operator()(int diagonal) { @@ -317,10 +352,19 @@ class SparsityPatternRowIterator return _dummy; } - inline double operator()(int diagonal) const { return 0.0; } + inline double operator()(int diagonal) const + { + return 0.0; + } - inline double& operator[](int diagonal) { return (*this)(diagonal); } - inline double operator[](int diagonal) const { return (*this)(diagonal); } + inline double& operator[](int diagonal) + { + return (*this)(diagonal); + } + inline double operator[](int diagonal) const + { + return (*this)(diagonal); + } inline SparsityPatternRowIterator& operator++() CADET_NOEXCEPT { @@ -370,18 +414,24 @@ class SparsityPatternRowIterator * @brief Returns the underlying pattern this iterator is pointing into * @return SparsityPattern this iterator is pointing into */ - inline const SparsityPattern& pattern() const CADET_NOEXCEPT { return *_pattern; } + inline const SparsityPattern& pattern() const CADET_NOEXCEPT + { + return *_pattern; + } /** * @brief Returns the index of the current row * @return Index of the current row */ - inline int row() const CADET_NOEXCEPT { return _row; } + inline int row() const CADET_NOEXCEPT + { + return _row; + } private: SparsityPattern* _pattern; //!< Underlying pattern - int _row; //!< Index of the current row - double _dummy; //!< Dummy element for access by reference + int _row; //!< Index of the current row + double _dummy; //!< Dummy element for access by reference }; class BandedSparseRowIterator; @@ -393,27 +443,36 @@ class ConstBandedSparseRowIterator; class CompressedSparseMatrix { public: - /** * @brief Creates an empty CompressedSparseMatrix with capacity @c 0 * @details Users have to call resize() prior to populating the matrix. */ - CompressedSparseMatrix() CADET_NOEXCEPT : _values(0), _colIdx(0), _rowStart(0), _dummy(0.0) { } + CompressedSparseMatrix() CADET_NOEXCEPT : _values(0), _colIdx(0), _rowStart(0), _dummy(0.0) + { + } /** * @brief Creates an empty CompressedSparseMatrix with the given capacity * @param [in] numRows Matrix size (i.e., number of rows or columns) * @param [in] numNonZeros Maximum number of non-zero elements */ - CompressedSparseMatrix(int numRows, int numNonZeros) : _dummy(0.0) { resize(numRows, numNonZeros); } + CompressedSparseMatrix(int numRows, int numNonZeros) : _dummy(0.0) + { + resize(numRows, numNonZeros); + } /** * @brief Creates a CompressedSparseMatrix with the given pattern * @param [in] pattern Sparsity pattern */ - CompressedSparseMatrix(const SparsityPattern& pattern) : _values(0), _colIdx(0), _rowStart(0), _dummy(0.0) { assignPattern(pattern); } + CompressedSparseMatrix(const SparsityPattern& pattern) : _values(0), _colIdx(0), _rowStart(0), _dummy(0.0) + { + assignPattern(pattern); + } - ~CompressedSparseMatrix() CADET_NOEXCEPT { } + ~CompressedSparseMatrix() CADET_NOEXCEPT + { + } // Default copy and assignment semantics CompressedSparseMatrix(const CompressedSparseMatrix& cpy) = default; @@ -461,7 +520,7 @@ class CompressedSparseMatrix /** * @brief Allocates memory for the sparse matrix of given size * @details The matrix is reset to an empty state. All previous content is lost. - * + * * @param [in] numRows Matrix size (i.e., number of rows or columns) * @param [in] numNonZeros Maximum number of non-zero elements */ @@ -494,7 +553,7 @@ class CompressedSparseMatrix // Try to find the element // TODO: Use binary search - for (sparse_int_t i = _rowStart[row]; i < _rowStart[row+1]; ++i) + for (sparse_int_t i = _rowStart[row]; i < _rowStart[row + 1]; ++i) { if (_colIdx[i] == col) return true; @@ -517,7 +576,7 @@ class CompressedSparseMatrix // Try to find the element // TODO: Use binary search - for (sparse_int_t i = _rowStart[row]; i < _rowStart[row+1]; ++i) + for (sparse_int_t i = _rowStart[row]; i < _rowStart[row + 1]; ++i) { if (_colIdx[i] == col) return _values[i]; @@ -542,7 +601,7 @@ class CompressedSparseMatrix // Try to find the element // TODO: Use binary search - for (sparse_int_t i = _rowStart[row]; i < _rowStart[row+1]; ++i) + for (sparse_int_t i = _rowStart[row]; i < _rowStart[row + 1]; ++i) { if (_colIdx[i] == col) return _values[i]; @@ -557,19 +616,28 @@ class CompressedSparseMatrix * @details The returned vector contains an additional last element that holds the number of non-zeros. * @return Vector with row index pointers */ - inline const std::vector& rowStartIndices() const CADET_NOEXCEPT { return _rowStart; } + inline const std::vector& rowStartIndices() const CADET_NOEXCEPT + { + return _rowStart; + } /** * @brief Returns a vector with column indices of the values * @return Vector with column indices */ - inline const std::vector& columnIndices() const CADET_NOEXCEPT { return _colIdx; } + inline const std::vector& columnIndices() const CADET_NOEXCEPT + { + return _colIdx; + } /** * @brief Returns a vector with element values * @return Vector with element values */ - inline const std::vector& values() const CADET_NOEXCEPT { return _values; } + inline const std::vector& values() const CADET_NOEXCEPT + { + return _values; + } /** * @brief Returns an array with column indices of the values in the given row @@ -578,7 +646,7 @@ class CompressedSparseMatrix */ inline sparse_int_t const* columnIndicesOfRow(int row) const CADET_NOEXCEPT { -// cadet_assert((row >= 0) && (row < rows())); + // cadet_assert((row >= 0) && (row < rows())); return _colIdx.data() + _rowStart[row]; } @@ -589,7 +657,7 @@ class CompressedSparseMatrix */ inline double const* valuesOfRow(int row) const CADET_NOEXCEPT { -// cadet_assert((row >= 0) && (row < rows())); + // cadet_assert((row >= 0) && (row < rows())); return _values.data() + _rowStart[row]; } @@ -600,7 +668,7 @@ class CompressedSparseMatrix */ inline double* valuesOfRow(int row) CADET_NOEXCEPT { -// cadet_assert((row >= 0) && (row < rows())); + // cadet_assert((row >= 0) && (row < rows())); return _values.data() + _rowStart[row]; } @@ -609,25 +677,37 @@ class CompressedSparseMatrix * @param [in] row Index of the row * @return Number of (structurally) non-zero elements in the matrix row */ - inline sparse_int_t numNonZerosInRow(int row) const CADET_NOEXCEPT { return _rowStart[row+1] - _rowStart[row]; } + inline sparse_int_t numNonZerosInRow(int row) const CADET_NOEXCEPT + { + return _rowStart[row + 1] - _rowStart[row]; + } /** * @brief Returns the number of (structurally) non-zero elements in the matrix * @return Number of (structurally) non-zero elements in the matrix */ - inline int numNonZeros() const CADET_NOEXCEPT { return _values.size(); } + inline int numNonZeros() const CADET_NOEXCEPT + { + return _values.size(); + } /** * @brief Returns the number of rows and columns (square matrix) * @return Number of rows and columns */ - inline int matrixSize() const CADET_NOEXCEPT { return _rowStart.size() - 2; } - + inline int matrixSize() const CADET_NOEXCEPT + { + return _rowStart.size() - 2; + } + /** * @brief Returns the number of rows * @return Number of rows */ - inline int rows() const CADET_NOEXCEPT { return _rowStart.size() - 2; } + inline int rows() const CADET_NOEXCEPT + { + return _rowStart.size() - 2; + } /** * @brief Sets all matrix elements to the given value @@ -652,34 +732,52 @@ class CompressedSparseMatrix /** * @brief Accesses an element in the matrix where the main diagonal is centered (index @c 0) * @details The @p diagonal determines the element in a row. A negative index indicates a lower diagonal, - * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main + * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main * diagonal is retrieved. - * + * * @param [in] row Index of the row * @param [in] diagonal Index of the diagonal (between negative lower bandwidth and upper bandwidth) - * + * * @return Matrix element at the given position */ - inline double& centered(int row, int diagonal) { return (*this)(row, row + diagonal); } - inline double centered(int row, int diagonal) const { return (*this)(row, row + diagonal); } + inline double& centered(int row, int diagonal) + { + return (*this)(row, row + diagonal); + } + inline double centered(int row, int diagonal) const + { + return (*this)(row, row + diagonal); + } /** * @brief Accesses an element in the matrix - * + * * @param [in] row Index of the row * @param [in] col Index of the column - * + * * @return Matrix element at the given position */ - inline double& native(int row, int col) { return (*this)(row, col); } - inline double native(int row, int col) const { return (*this)(row, col); } + inline double& native(int row, int col) + { + return (*this)(row, col); + } + inline double native(int row, int col) const + { + return (*this)(row, col); + } /** * @brief Provides direct access to the underlying memory * @return Pointer to the first element of the underlying array */ - inline double* data() CADET_NOEXCEPT { return _values.data(); } - inline double const* data() const CADET_NOEXCEPT { return _values.data(); } + inline double* data() CADET_NOEXCEPT + { + return _values.data(); + } + inline double const* data() const CADET_NOEXCEPT + { + return _values.data(); + } /** * @brief Creates a RowIterator pointing to the given row @@ -708,9 +806,11 @@ class CompressedSparseMatrix void multiplyVector(double const* const x, double alpha, double beta, double* y) const; protected: - std::vector _values; //!< Values of matrix entries + std::vector _values; //!< Values of matrix entries std::vector _colIdx; //!< Column of the value in _values (size is _values.size() = numNonZeros) - std::vector _rowStart; //!< Index into _colIdx that marks the beginning of a row (two entries more than rows; second to last entry points beyond the last row, so that _rowStart[numRows] = _values.size() = numNonZeros) + std::vector + _rowStart; //!< Index into _colIdx that marks the beginning of a row (two entries more than rows; second to last + //!< entry points beyond the last row, so that _rowStart[numRows] = _values.size() = numNonZeros) double _dummy; //!< Dummy for access by reference }; @@ -720,20 +820,25 @@ class BandedSparseRowIterator /** * @brief Creates a ConstBandedSparseRowIterator pointing nowhere */ - BandedSparseRowIterator() : _matrix(nullptr), _values(nullptr), _colIdx(nullptr), _row(-1), _numNonZero(0), _dummy(0.0) { } + BandedSparseRowIterator() + : _matrix(nullptr), _values(nullptr), _colIdx(nullptr), _row(-1), _numNonZero(0), _dummy(0.0) + { + } /** * @brief Creates a BandedSparseRowIterator of the given matrix row * @param [in] mat Matrix * @param [in] row Index of the row */ - BandedSparseRowIterator(CompressedSparseMatrix& mat, int row) + BandedSparseRowIterator(CompressedSparseMatrix& mat, int row) : _matrix(&mat), _values(mat.valuesOfRow(row)), _colIdx(mat.columnIndicesOfRow(row)), _row(row), - _numNonZero(mat.numNonZerosInRow(row)), _dummy(0.0) + _numNonZero(mat.numNonZerosInRow(row)), _dummy(0.0) { } - ~BandedSparseRowIterator() CADET_NOEXCEPT { } + ~BandedSparseRowIterator() CADET_NOEXCEPT + { + } // Default copy and assignment semantics BandedSparseRowIterator(const BandedSparseRowIterator& cpy) = default; @@ -760,8 +865,7 @@ class BandedSparseRowIterator * @details Assumes the same sparsity pattern of source and destination row. * @param [in] it Iterator pointing to a row of a matrix */ - template - inline void copyRowFrom(const OtherIterator_t& it) + template inline void copyRowFrom(const OtherIterator_t& it) { cadet_assert(_numNonZero == it.numNonZeros()); std::copy(it.data(), it.data() + _numNonZero, _values); @@ -770,15 +874,21 @@ class BandedSparseRowIterator /** * @brief Accesses an element in the current row where the main diagonal is centered (index @c 0) * @details The @p diagonal determines the element in a row. A negative index indicates a lower diagonal, - * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main + * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main * diagonal is retrieved. - * + * * @param [in] diagonal Index of the diagonal - * + * * @return Matrix element at the given position */ - inline double& centered(int diagonal) { return native(_row + diagonal); } - inline double centered(int diagonal) const { return native(_row + diagonal); } + inline double& centered(int diagonal) + { + return native(_row + diagonal); + } + inline double centered(int diagonal) const + { + return native(_row + diagonal); + } /** * @brief Accesses an element in the current row where the lowest diagonal is indexed by @c 0 @@ -818,11 +928,23 @@ class BandedSparseRowIterator return 0.0; } - inline double& operator()(int diagonal) { return centered(diagonal); } - inline double operator()(int diagonal) const { return centered(diagonal); } + inline double& operator()(int diagonal) + { + return centered(diagonal); + } + inline double operator()(int diagonal) const + { + return centered(diagonal); + } - inline double& operator[](int diagonal) { return centered(diagonal); } - inline double operator[](int diagonal) const { return centered(diagonal); } + inline double& operator[](int diagonal) + { + return centered(diagonal); + } + inline double operator[](int diagonal) const + { + return centered(diagonal); + } inline BandedSparseRowIterator& operator++() CADET_NOEXCEPT { @@ -876,34 +998,48 @@ class BandedSparseRowIterator * @brief Returns the underlying matrix this iterator is pointing into * @return Matrix this iterator is pointing into */ - inline const CompressedSparseMatrix& matrix() const CADET_NOEXCEPT { return *_matrix; } + inline const CompressedSparseMatrix& matrix() const CADET_NOEXCEPT + { + return *_matrix; + } /** * @brief Returns the index of the current row * @return Index of the current row */ - inline int row() const CADET_NOEXCEPT { return _row; } + inline int row() const CADET_NOEXCEPT + { + return _row; + } /** * @brief Returns the number of non-zero entries in this row * @return Number of non-zero entries in this row */ - inline int numNonZeros() const CADET_NOEXCEPT { return _numNonZero; } + inline int numNonZeros() const CADET_NOEXCEPT + { + return _numNonZero; + } /** * @brief Returns an array of the matrix entries in this row * @return Array with matrix entries in this row */ - inline double* data() CADET_NOEXCEPT { return _values; } - inline double const* data() const CADET_NOEXCEPT { return _values; } + inline double* data() CADET_NOEXCEPT + { + return _values; + } + inline double const* data() const CADET_NOEXCEPT + { + return _values; + } protected: - inline void updateOnRowChange() CADET_NOEXCEPT { _values = _matrix->valuesOfRow(_row); _colIdx = _matrix->columnIndicesOfRow(_row); - _numNonZero = _matrix->numNonZerosInRow(_row); + _numNonZero = _matrix->numNonZerosInRow(_row); } CompressedSparseMatrix* _matrix; @@ -914,28 +1050,30 @@ class BandedSparseRowIterator double _dummy; }; - class ConstBandedSparseRowIterator { public: - /** * @brief Creates a ConstBandedSparseRowIterator pointing nowhere */ - ConstBandedSparseRowIterator() : _matrix(nullptr), _values(nullptr), _colIdx(nullptr), _row(-1), _numNonZero(0) { } + ConstBandedSparseRowIterator() : _matrix(nullptr), _values(nullptr), _colIdx(nullptr), _row(-1), _numNonZero(0) + { + } /** * @brief Creates a ConstBandedSparseRowIterator of the given matrix row * @param [in] mat Matrix * @param [in] row Index of the row */ - ConstBandedSparseRowIterator(const CompressedSparseMatrix& mat, int row) + ConstBandedSparseRowIterator(const CompressedSparseMatrix& mat, int row) : _matrix(&mat), _values(mat.valuesOfRow(row)), _colIdx(mat.columnIndicesOfRow(row)), _row(row), - _numNonZero(mat.numNonZerosInRow(row)) + _numNonZero(mat.numNonZerosInRow(row)) { } - ~ConstBandedSparseRowIterator() CADET_NOEXCEPT { } + ~ConstBandedSparseRowIterator() CADET_NOEXCEPT + { + } // Default copy and assignment semantics ConstBandedSparseRowIterator(const ConstBandedSparseRowIterator& cpy) = default; @@ -951,14 +1089,17 @@ class ConstBandedSparseRowIterator /** * @brief Accesses an element in the current row where the main diagonal is centered (index @c 0) * @details The @p diagonal determines the element in a row. A negative index indicates a lower diagonal, - * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main + * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main * diagonal is retrieved. - * + * * @param [in] diagonal Index of the diagonal - * + * * @return Matrix element at the given position */ - inline double centered(int diagonal) const { return native(_row + diagonal); } + inline double centered(int diagonal) const + { + return native(_row + diagonal); + } /** * @brief Accesses an element in the current row where the lowest diagonal is indexed by @c 0 @@ -981,8 +1122,14 @@ class ConstBandedSparseRowIterator return 0.0; } - inline double operator()(int diagonal) const { return centered(diagonal); } - inline double operator[](int diagonal) const { return centered(diagonal); } + inline double operator()(int diagonal) const + { + return centered(diagonal); + } + inline double operator[](int diagonal) const + { + return centered(diagonal); + } inline ConstBandedSparseRowIterator& operator++() CADET_NOEXCEPT { @@ -1036,33 +1183,44 @@ class ConstBandedSparseRowIterator * @brief Returns the underlying matrix this iterator is pointing into * @return Matrix this iterator is pointing into */ - inline const CompressedSparseMatrix& matrix() const CADET_NOEXCEPT { return *_matrix; } + inline const CompressedSparseMatrix& matrix() const CADET_NOEXCEPT + { + return *_matrix; + } /** * @brief Returns the index of the current row * @return Index of the current row */ - inline int row() const CADET_NOEXCEPT { return _row; } + inline int row() const CADET_NOEXCEPT + { + return _row; + } /** * @brief Returns the number of non-zero entries in this row * @return Number of non-zero entries in this row */ - inline int numNonZeros() const CADET_NOEXCEPT { return _numNonZero; } + inline int numNonZeros() const CADET_NOEXCEPT + { + return _numNonZero; + } /** * @brief Returns an array of the matrix entries in this row * @return Array with matrix entries in this row */ - inline double const* data() const CADET_NOEXCEPT { return _values; } + inline double const* data() const CADET_NOEXCEPT + { + return _values; + } protected: - inline void updateOnRowChange() CADET_NOEXCEPT { _values = _matrix->valuesOfRow(_row); _colIdx = _matrix->columnIndicesOfRow(_row); - _numNonZero = _matrix->numNonZerosInRow(_row); + _numNonZero = _matrix->numNonZerosInRow(_row); } CompressedSparseMatrix const* _matrix; @@ -1072,11 +1230,10 @@ class ConstBandedSparseRowIterator int _numNonZero; }; - std::ostream& operator<<(std::ostream& out, const CompressedSparseMatrix& sm); } // namespace linalg } // namespace cadet -#endif // LIBCADET_COMPRESSEDSPARSEMATRIX_HPP_ +#endif // LIBCADET_COMPRESSEDSPARSEMATRIX_HPP_ diff --git a/src/libcadet/linalg/DenseMatrix.cpp b/src/libcadet/linalg/DenseMatrix.cpp index 2986fb45c..1c363f441 100644 --- a/src/libcadet/linalg/DenseMatrix.cpp +++ b/src/libcadet/linalg/DenseMatrix.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -23,8 +23,7 @@ namespace linalg namespace detail { -void DenseMatrixBase::submatrixSetAll(double val, int startRow, int startCol, - int numRows, int numCols) +void DenseMatrixBase::submatrixSetAll(double val, int startRow, int startCol, int numRows, int numCols) { cadet_assert(_rows > startRow); cadet_assert(_cols > startCol); @@ -39,8 +38,7 @@ void DenseMatrixBase::submatrixSetAll(double val, int startRow, int startCol, } } -void DenseMatrixBase::submatrixAssign(const DenseMatrixBase& mat, int startRow, int startCol, - int numRows, int numCols) +void DenseMatrixBase::submatrixAssign(const DenseMatrixBase& mat, int startRow, int startCol, int numRows, int numCols) { cadet_assert(numRows == mat.rows()); cadet_assert(numCols == mat.columns()); @@ -73,11 +71,12 @@ void DenseMatrixBase::multiplyVector(const double* const x, double alpha, double char trans[] = "T"; // LAPACK computes y <- alpha * A * x + beta * y - LapackMultiplyDense(trans, &m, &n, &alpha, const_cast(_data), &lda, const_cast(x), &inc, &beta, const_cast(y), &inc); + LapackMultiplyDense(trans, &m, &n, &alpha, const_cast(_data), &lda, const_cast(x), &inc, &beta, + const_cast(y), &inc); } -void DenseMatrixBase::submatrixMultiplyVector(const double* const x, int startRow, int startCol, - int numRows, int numCols, double alpha, double beta, double* const y) const +void DenseMatrixBase::submatrixMultiplyVector(const double* const x, int startRow, int startCol, int numRows, + int numCols, double alpha, double beta, double* const y) const { cadet_assert(_rows > startRow); cadet_assert(_cols > startCol); @@ -100,7 +99,8 @@ void DenseMatrixBase::submatrixMultiplyVector(const double* const x, int startRo double* const data = const_cast(_data) + startRow * lda + startCol; // LAPACK computes y <- alpha * A * x + beta * y - LapackMultiplyDense(trans, &m, &n, &alpha, data, &lda, const_cast(x), &inc, &beta, const_cast(y), &inc); + LapackMultiplyDense(trans, &m, &n, &alpha, data, &lda, const_cast(x), &inc, &beta, const_cast(y), + &inc); } void DenseMatrixBase::transposedMultiplyVector(const double* const x, double alpha, double beta, double* const y) const @@ -116,11 +116,12 @@ void DenseMatrixBase::transposedMultiplyVector(const double* const x, double alp char trans[] = "N"; // LAPACK computes y <- alpha * A * x + beta * y - LapackMultiplyDense(trans, &m, &n, &alpha, const_cast(_data), &lda, const_cast(x), &inc, &beta, const_cast(y), &inc); + LapackMultiplyDense(trans, &m, &n, &alpha, const_cast(_data), &lda, const_cast(x), &inc, &beta, + const_cast(y), &inc); } -void DenseMatrixBase::transposedSubmatrixMultiplyVector(const double* const x, int startRow, int startCol, - int numRows, int numCols, double alpha, double beta, double* const y) const +void DenseMatrixBase::transposedSubmatrixMultiplyVector(const double* const x, int startRow, int startCol, int numRows, + int numCols, double alpha, double beta, double* const y) const { cadet_assert(_rows > startRow); cadet_assert(_cols > startCol); @@ -141,7 +142,8 @@ void DenseMatrixBase::transposedSubmatrixMultiplyVector(const double* const x, i double* const data = const_cast(_data) + startRow * lda + startCol; // LAPACK computes y <- alpha * A * x + beta * y - LapackMultiplyDense(trans, &m, &n, &alpha, data, &lda, const_cast(x), &inc, &beta, const_cast(y), &inc); + LapackMultiplyDense(trans, &m, &n, &alpha, data, &lda, const_cast(x), &inc, &beta, const_cast(y), + &inc); } bool DenseMatrixBase::factorize() @@ -177,7 +179,8 @@ bool DenseMatrixBase::solve(double* rhs) const // solve the transposed equation which uses the original matrix. char trans[] = "T"; - LapackSolveDense(trans, &n, &nrhs, const_cast(_data), &lda, const_cast(_pivot), rhs, &n, &flag); + LapackSolveDense(trans, &n, &nrhs, const_cast(_data), &lda, const_cast(_pivot), rhs, &n, + &flag); // If the flag is -i (for i > 0), the ith argument is invalid return flag == 0; @@ -250,7 +253,7 @@ bool DenseMatrixBase::robustFactorize(double* const workspace) lapackInt_t flag = 0; // Compute LQ factorization A = L * Q with orthogonal matrix Q and lower triagonal matrix L - // Note that LAPACK sees the tranposed matrix A^T instead of A. The LQ factorization of A^T + // Note that LAPACK sees the tranposed matrix A^T instead of A. The LQ factorization of A^T // is essentially the same as QR factorization of A. LapackFactorLQDense(&m, &n, _data, &lda, workspace, workspace + _rows, &n, &flag); @@ -285,7 +288,8 @@ bool DenseMatrixBase::robustSolve(double* rhs, double* const workspace) const lapackInt_t flag = 0; // Calculate z = Q * y - LapackMultiplyFactorizedQ(side, transQ, &m, &nrhs, &m, _data, &lda, workspace, rhs, &n, workspace + _rows, &n, &flag); + LapackMultiplyFactorizedQ(side, transQ, &m, &nrhs, &m, _data, &lda, workspace, rhs, &n, workspace + _rows, &n, + &flag); if (flag != 0) return false; @@ -341,7 +345,6 @@ void DenseMatrixBase::columnScaleFactors(double* scalingFactors, int numCols) co } } - std::ostream& operator<<(std::ostream& out, const DenseMatrixBase& bm) { out << "["; @@ -360,8 +363,8 @@ std::ostream& operator<<(std::ostream& out, const DenseMatrixBase& bm) return out; } -} // namespace detail +} // namespace detail -} // namespace linalg +} // namespace linalg -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/linalg/DenseMatrix.hpp b/src/libcadet/linalg/DenseMatrix.hpp index 2bc9cf16a..c55b38772 100644 --- a/src/libcadet/linalg/DenseMatrix.hpp +++ b/src/libcadet/linalg/DenseMatrix.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines a rectangular dense matrix that can be factorized if it is square */ @@ -34,890 +34,995 @@ namespace linalg namespace detail { +/** + * @brief Iterates over rows of a dense matrix providing a banded interface + * @details The iterator always points to the main diagonal element in a row of + * a rectangular matrix. The user can then use the index of a sub- + * or superdiagonal to query and set matrix elements. Element queries + * outside the actual matrix are ignored and do not lead to failure. + * @tparam MatrixType Type of the underlying dense matrix class + */ +template class DenseBandedRowIterator +{ +public: /** - * @brief Iterates over rows of a dense matrix providing a banded interface - * @details The iterator always points to the main diagonal element in a row of - * a rectangular matrix. The user can then use the index of a sub- - * or superdiagonal to query and set matrix elements. Element queries - * outside the actual matrix are ignored and do not lead to failure. - * @tparam MatrixType Type of the underlying dense matrix class - */ - template - class DenseBandedRowIterator - { - public: - - /** - * @brief Creates an empty DenseBandedRowIterator pointing to nothing - * @param [in] mat Matrix this DenseBandedRowIterator accesses - */ - DenseBandedRowIterator() CADET_NOEXCEPT : _matrix(nullptr), _pos(nullptr), _rowIdx(0) { } - - /** - * @brief Creates a DenseBandedRowIterator for the given matrix - * @param [in] mat Matrix this DenseBandedRowIterator accesses - */ - DenseBandedRowIterator(MatrixType& mat) CADET_NOEXCEPT : _matrix(&mat), _pos(_matrix->_data), _rowIdx(0) { } - - /** - * @brief Creates a DenseBandedRowIterator for the given matrix - * @param [in] mat Matrix this DenseBandedRowIterator accesses - * @param [in] row Index of the row of the iterator points so - */ - DenseBandedRowIterator(MatrixType& mat, int row) CADET_NOEXCEPT : _matrix(&mat), _pos(_matrix->_data + row * _matrix->stride() + row), _rowIdx(row) { } - - /** - * @brief Creates a DenseBandedRowIterator for the given matrix - * @param [in] mat Matrix this DenseBandedRowIterator accesses - * @param [in] row Index of the row of the iterator points so - * @param [in] diagOffset Diagonal this iterator points to (offset) - */ - DenseBandedRowIterator(MatrixType& mat, int row, int diagOffset) CADET_NOEXCEPT : _matrix(&mat), _pos(_matrix->_data + static_cast(row * _matrix->stride() + row) + diagOffset), _rowIdx(row) { } - - DenseBandedRowIterator(const DenseBandedRowIterator& cpy, int rowChange) CADET_NOEXCEPT : _matrix(cpy._matrix), _pos(cpy._pos + rowChange * static_cast(cpy._matrix->stride() + 1)), _rowIdx(static_cast(cpy._rowIdx) + rowChange) { } - DenseBandedRowIterator(const DenseBandedRowIterator& cpy) CADET_NOEXCEPT : _matrix(cpy._matrix), _pos(cpy._pos), _rowIdx(cpy._rowIdx) { } - DenseBandedRowIterator(DenseBandedRowIterator&& cpy) CADET_NOEXCEPT : _matrix(cpy._matrix), _pos(cpy._pos), _rowIdx(cpy._rowIdx) { } - - inline DenseBandedRowIterator& operator=(const DenseBandedRowIterator& cpy) CADET_NOEXCEPT - { - cadet_assert(&cpy != this); + * @brief Creates an empty DenseBandedRowIterator pointing to nothing + * @param [in] mat Matrix this DenseBandedRowIterator accesses + */ + DenseBandedRowIterator() CADET_NOEXCEPT : _matrix(nullptr), _pos(nullptr), _rowIdx(0) + { + } - _matrix = cpy._matrix; - _pos = cpy._pos; - _rowIdx = cpy._rowIdx; - return *this; - } + /** + * @brief Creates a DenseBandedRowIterator for the given matrix + * @param [in] mat Matrix this DenseBandedRowIterator accesses + */ + DenseBandedRowIterator(MatrixType& mat) CADET_NOEXCEPT : _matrix(&mat), _pos(_matrix->_data), _rowIdx(0) + { + } - inline DenseBandedRowIterator& operator=(DenseBandedRowIterator&& cpy) CADET_NOEXCEPT - { - _matrix = cpy._matrix; - _pos = cpy._pos; - _rowIdx = cpy._rowIdx; - return *this; - } + /** + * @brief Creates a DenseBandedRowIterator for the given matrix + * @param [in] mat Matrix this DenseBandedRowIterator accesses + * @param [in] row Index of the row of the iterator points so + */ + DenseBandedRowIterator(MatrixType& mat, int row) CADET_NOEXCEPT + : _matrix(&mat), + _pos(_matrix->_data + row * _matrix->stride() + row), + _rowIdx(row) + { + } - /** - * @brief Accesses an element in the current row where the main diagonal is centered (index @c 0) - * @details The @p diagonal determines the element in a row. A negative index indicates a lower diagonal, - * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main - * diagonal is retrieved. - * - * @param [in] diagonal Index of the diagonal - * @return Matrix element at the given position - */ - inline double& centered(int diagonal) { return (*this)(diagonal); } - inline double centered(int diagonal) const { return (*this)(diagonal); } - - inline double& operator()(int diagonal) - { - // Check if out of scope - if (cadet_unlikely(diagonal >= static_cast(_matrix->columns()) - _rowIdx)) - return _dummy; - if (cadet_unlikely(diagonal < -_rowIdx)) - return _dummy; + /** + * @brief Creates a DenseBandedRowIterator for the given matrix + * @param [in] mat Matrix this DenseBandedRowIterator accesses + * @param [in] row Index of the row of the iterator points so + * @param [in] diagOffset Diagonal this iterator points to (offset) + */ + DenseBandedRowIterator(MatrixType& mat, int row, int diagOffset) CADET_NOEXCEPT + : _matrix(&mat), + _pos(_matrix->_data + static_cast(row * _matrix->stride() + row) + diagOffset), + _rowIdx(row) + { + } - return _pos[diagonal]; - } + DenseBandedRowIterator(const DenseBandedRowIterator& cpy, int rowChange) CADET_NOEXCEPT + : _matrix(cpy._matrix), + _pos(cpy._pos + rowChange * static_cast(cpy._matrix->stride() + 1)), + _rowIdx(static_cast(cpy._rowIdx) + rowChange) + { + } + DenseBandedRowIterator(const DenseBandedRowIterator& cpy) CADET_NOEXCEPT : _matrix(cpy._matrix), + _pos(cpy._pos), + _rowIdx(cpy._rowIdx) + { + } + DenseBandedRowIterator(DenseBandedRowIterator&& cpy) CADET_NOEXCEPT : _matrix(cpy._matrix), + _pos(cpy._pos), + _rowIdx(cpy._rowIdx) + { + } - inline double operator()(int diagonal) const - { - // Check if out of scope - if (cadet_unlikely(diagonal >= static_cast(_matrix->columns()) - _rowIdx)) - return 0.0; - if (cadet_unlikely(diagonal < -_rowIdx)) - return 0.0; + inline DenseBandedRowIterator& operator=(const DenseBandedRowIterator& cpy) CADET_NOEXCEPT + { + cadet_assert(&cpy != this); - return _pos[diagonal]; - } + _matrix = cpy._matrix; + _pos = cpy._pos; + _rowIdx = cpy._rowIdx; + return *this; + } - inline double& operator[](int diagonal) { return (*this)(diagonal); } - inline double operator[](int diagonal) const { return (*this)(diagonal); } + inline DenseBandedRowIterator& operator=(DenseBandedRowIterator&& cpy) CADET_NOEXCEPT + { + _matrix = cpy._matrix; + _pos = cpy._pos; + _rowIdx = cpy._rowIdx; + return *this; + } - /** - * @brief Sets all row elements to the given value - * @param [in] val Value all row elements are set to - */ - inline void setAll(double val) - { - std::fill(_pos - _rowIdx, _pos + static_cast(_matrix->columns()) - _rowIdx, val); - } + /** + * @brief Accesses an element in the current row where the main diagonal is centered (index @c 0) + * @details The @p diagonal determines the element in a row. A negative index indicates a lower diagonal, + * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main + * diagonal is retrieved. + * + * @param [in] diagonal Index of the diagonal + * @return Matrix element at the given position + */ + inline double& centered(int diagonal) + { + return (*this)(diagonal); + } + inline double centered(int diagonal) const + { + return (*this)(diagonal); + } - /** - * @brief Copies a row of another row iterator to the current row - * @param [in] ri Other row iterator - * @tparam OtherMatrix Underlying matrix type of other row iterator - */ - template - inline void copyRowFrom(const DenseBandedRowIterator& ri) - { - cadet_assert(_matrix->columns() >= ri._matrix->columns()); - std::copy_n(ri._pos - ri._rowIdx, _matrix->columns(), _pos - _rowIdx); - } + inline double& operator()(int diagonal) + { + // Check if out of scope + if (cadet_unlikely(diagonal >= static_cast(_matrix->columns()) - _rowIdx)) + return _dummy; + if (cadet_unlikely(diagonal < -_rowIdx)) + return _dummy; - /** - * @brief Adds a subset of this row @f$ x @f$ to another row @f$ y @f$ performing @f$ y = y + \alpha x @f$ - * @details Although the subset is specified with a bandmatrix interface, the rows are - added as in a normal matrix. - * @param [in] riDest Row iterator pointing to the destination row @f$ y @f$ - * @param [in] factor Factor @f$ \alpha @f$ - * @param [in] lowerBand Start point of the row subset as lower band index - * @param [in] upperBand End point of the row subset as upper band index - * @tparam OtherMatrix Underlying matrix type of other row iterator - */ - template - inline void addSubsetTo(const DenseBandedRowIterator& riDest, double factor, int lowerBand, int upperBand) const - { - cadet_assert(_matrix->columns() >= riDest._matrix->columns()); - - const int idxStart = _rowIdx + lowerBand; - const int idxEnd = _rowIdx + upperBand; - - cadet_assert(idxStart >= 0); - cadet_assert(idxEnd <= _matrix->columns()); - - double* const dest = riDest._pos - riDest._rowIdx; - double const* const src = _pos - _rowIdx; - for (int i = idxStart; i < idxEnd; ++i) - dest[i] += factor * src[i]; - } + return _pos[diagonal]; + } - /** - * @brief Adds a subset of this row @f$ x @f$ to another row @f$ y @f$ performing @f$ y = y + \alpha x @f$ - * @details Although the subset is specified with a bandmatrix interface, the rows are - added as in a normal matrix. - * @param [in] riDest Row iterator pointing to the destination row @f$ y @f$ - * @param [in] factor Factor @f$ \alpha @f$ - * @param [in] lowerBand Start point of the row subset as lower band index - * @param [in] upperBand End point of the row subset as upper band index - * @param [in] shift Unused - * @tparam OtherMatrix Underlying matrix type of other row iterator - */ - template - inline void addSubsetTo(const DenseBandedRowIterator& riDest, double factor, int lowerBand, int upperBand, int shift) const - { - addSubsetTo(riDest, factor, lowerBand, upperBand); - } + inline double operator()(int diagonal) const + { + // Check if out of scope + if (cadet_unlikely(diagonal >= static_cast(_matrix->columns()) - _rowIdx)) + return 0.0; + if (cadet_unlikely(diagonal < -_rowIdx)) + return 0.0; - /** - * @brief Adds the given array to the current row - * @details Performs the operation @f$ y = y + \alpha x @f$, where @f$ x @f$ may only be a - * subset of the current row the iterator points to. The start of the subset is - * given by @p startDiag. The subset has to fully fit into the matrix row. - * @param [in] row Pointer to array @f$ x @f$ that is added to the given row @f$ y @f$ - * @param [in] startDiag Index of the diagonal at which the row is added - * @param [in] length Length of the array - * @param [in] factor Factor @f$ \alpha @f$ - */ - inline void addArray(double const* row, int startDiag, int length, double factor) - { - cadet_assert(startDiag + _rowIdx >= 0); - cadet_assert(startDiag + length + _rowIdx <= static_cast(_matrix->columns())); - double* const dest = _pos + startDiag; - for (int i = 0; i < length; ++i) - dest[i] += factor * row[i]; - } + return _pos[diagonal]; + } - inline DenseBandedRowIterator& operator++() CADET_NOEXCEPT - { - // Add one additional shift to stay on the main diagonal - _pos += _matrix->stride() + 1; - ++_rowIdx; - return *this; - } + inline double& operator[](int diagonal) + { + return (*this)(diagonal); + } + inline double operator[](int diagonal) const + { + return (*this)(diagonal); + } - inline DenseBandedRowIterator& operator--() CADET_NOEXCEPT - { - // Subtract one additional shift to stay on the main diagonal - _pos -= _matrix->stride() + 1; - --_rowIdx; - return *this; - } + /** + * @brief Sets all row elements to the given value + * @param [in] val Value all row elements are set to + */ + inline void setAll(double val) + { + std::fill(_pos - _rowIdx, _pos + static_cast(_matrix->columns()) - _rowIdx, val); + } - inline DenseBandedRowIterator& operator+=(int idx) CADET_NOEXCEPT - { - // Add additional shifts to stay on the main diagonal - _pos += idx * _matrix->stride() + idx; - _rowIdx += idx; - return *this; - } + /** + * @brief Copies a row of another row iterator to the current row + * @param [in] ri Other row iterator + * @tparam OtherMatrix Underlying matrix type of other row iterator + */ + template inline void copyRowFrom(const DenseBandedRowIterator& ri) + { + cadet_assert(_matrix->columns() >= ri._matrix->columns()); + std::copy_n(ri._pos - ri._rowIdx, _matrix->columns(), _pos - _rowIdx); + } - inline DenseBandedRowIterator& operator-=(int idx) CADET_NOEXCEPT - { - // Subtract additional shifts to stay on the main diagonal - _pos -= idx * _matrix->stride() + idx; - _rowIdx -= idx; - return *this; - } + /** + * @brief Adds a subset of this row @f$ x @f$ to another row @f$ y @f$ performing @f$ y = y + \alpha x @f$ + * @details Although the subset is specified with a bandmatrix interface, the rows are + added as in a normal matrix. + * @param [in] riDest Row iterator pointing to the destination row @f$ y @f$ + * @param [in] factor Factor @f$ \alpha @f$ + * @param [in] lowerBand Start point of the row subset as lower band index + * @param [in] upperBand End point of the row subset as upper band index + * @tparam OtherMatrix Underlying matrix type of other row iterator + */ + template + inline void addSubsetTo(const DenseBandedRowIterator& riDest, double factor, int lowerBand, + int upperBand) const + { + cadet_assert(_matrix->columns() >= riDest._matrix->columns()); - inline DenseBandedRowIterator operator+(int op) const CADET_NOEXCEPT - { - return DenseBandedRowIterator(*this, op); - } + const int idxStart = _rowIdx + lowerBand; + const int idxEnd = _rowIdx + upperBand; - inline friend DenseBandedRowIterator operator+(int op, const DenseBandedRowIterator& it) CADET_NOEXCEPT - { - return DenseBandedRowIterator(it, op); - } + cadet_assert(idxStart >= 0); + cadet_assert(idxEnd <= _matrix->columns()); - inline DenseBandedRowIterator operator-(int op) const CADET_NOEXCEPT - { - return DenseBandedRowIterator(*this, -op); - } + double* const dest = riDest._pos - riDest._rowIdx; + double const* const src = _pos - _rowIdx; + for (int i = idxStart; i < idxEnd; ++i) + dest[i] += factor * src[i]; + } - inline friend DenseBandedRowIterator operator-(int op, const DenseBandedRowIterator& it) CADET_NOEXCEPT - { - return DenseBandedRowIterator(it, -op); - } + /** + * @brief Adds a subset of this row @f$ x @f$ to another row @f$ y @f$ performing @f$ y = y + \alpha x @f$ + * @details Although the subset is specified with a bandmatrix interface, the rows are + added as in a normal matrix. + * @param [in] riDest Row iterator pointing to the destination row @f$ y @f$ + * @param [in] factor Factor @f$ \alpha @f$ + * @param [in] lowerBand Start point of the row subset as lower band index + * @param [in] upperBand End point of the row subset as upper band index + * @param [in] shift Unused + * @tparam OtherMatrix Underlying matrix type of other row iterator + */ + template + inline void addSubsetTo(const DenseBandedRowIterator& riDest, double factor, int lowerBand, + int upperBand, int shift) const + { + addSubsetTo(riDest, factor, lowerBand, upperBand); + } - inline const MatrixType& matrix() const CADET_NOEXCEPT { return _matrix; } - - private: - MatrixType* _matrix; - double* _pos; - double _dummy; - int _rowIdx; - }; - - /** - * @brief Represents a dense matrix base class providing common functionality (e.g., factorization) - * @details LAPACK uses column-major storage, whereas this class uses row-major. - * Thus, what we call a row here is actually a column for LAPACK. - * Concluding, we have to use the transposed LAPACK operations for - * solution and matrix-vector multiplication. The ordering is irrelevant - * for the factorization. - * - * Because of the transposition induced by the differing ordering, - * the number of columns and rows switches (e.g., columns are transposed - * rows). The ordering of the elements inside one (original) row is maintained - * (i.e., the first element in a row becomes the first element in a column - * and the last element in a row transposes to the last element in a column). - * - * LAPACK needs additional pivoting arrays for factorization, which are - * also stored in this class. - */ - class DenseMatrixBase - { - public: - - typedef DenseBandedRowIterator RowIterator; - friend class DenseBandedRowIterator; - - ~DenseMatrixBase() CADET_NOEXCEPT { } - - /** - * @brief Sets all matrix elements to the given value - * @param [in] val Value all matrix elements are set to - */ - inline void setAll(double val) - { - std::fill(_data, _data + stride() * _rows, val); - } + /** + * @brief Adds the given array to the current row + * @details Performs the operation @f$ y = y + \alpha x @f$, where @f$ x @f$ may only be a + * subset of the current row the iterator points to. The start of the subset is + * given by @p startDiag. The subset has to fully fit into the matrix row. + * @param [in] row Pointer to array @f$ x @f$ that is added to the given row @f$ y @f$ + * @param [in] startDiag Index of the diagonal at which the row is added + * @param [in] length Length of the array + * @param [in] factor Factor @f$ \alpha @f$ + */ + inline void addArray(double const* row, int startDiag, int length, double factor) + { + cadet_assert(startDiag + _rowIdx >= 0); + cadet_assert(startDiag + length + _rowIdx <= static_cast(_matrix->columns())); + double* const dest = _pos + startDiag; + for (int i = 0; i < length; ++i) + dest[i] += factor * row[i]; + } - /** - * @brief Accesses an element in a diagonal of the matrix where the main diagonal has index @c 0 - * @details The @p diagonal determines the element in a row. A negative index indicates a lower diagonal, - * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main - * diagonal of the matrix is retrieved. - * - * @param [in] row Index of the row - * @param [in] diagonal Index of the diagonal - * - * @return Matrix element at the given position - */ - inline double& diagonalElement(int row, int diagonal) { return (*this)(row, diagonal); } - inline double diagonalElement(int row, int diagonal) const { return (*this)(row, diagonal); } - - /** - * @brief Accesses an element in the matrix - * @details In contrast to diagonalElement(), the indices do not refer to diagonals but to columns of the matrix. - * - * @param [in] row Index of the row - * @param [in] col Index of the column (from @c 0 to @c columns) - * - * @return Matrix element at the given position - */ - inline double& native(int row, int col) - { - cadet_assert(row < _rows); - cadet_assert(col < _cols); - return _data[row * stride() + col]; - } + inline DenseBandedRowIterator& operator++() CADET_NOEXCEPT + { + // Add one additional shift to stay on the main diagonal + _pos += _matrix->stride() + 1; + ++_rowIdx; + return *this; + } - inline double native(int row, int col) const - { - cadet_assert(row < _rows); - cadet_assert(col < _cols); - return _data[row * stride() + col]; - } + inline DenseBandedRowIterator& operator--() CADET_NOEXCEPT + { + // Subtract one additional shift to stay on the main diagonal + _pos -= _matrix->stride() + 1; + --_rowIdx; + return *this; + } - inline double& operator()(int row, int diagonal) - { - cadet_assert(row < _rows); - cadet_assert(diagonal < static_cast(_cols - row)); - cadet_assert(-diagonal <= static_cast(row)); - return _data[static_cast(row * stride() + row) + diagonal]; - } + inline DenseBandedRowIterator& operator+=(int idx) CADET_NOEXCEPT + { + // Add additional shifts to stay on the main diagonal + _pos += idx * _matrix->stride() + idx; + _rowIdx += idx; + return *this; + } - inline double operator()(int row, int diagonal) const - { - cadet_assert(row < _rows); - cadet_assert(diagonal < static_cast(_cols - row)); - cadet_assert(-diagonal <= static_cast(row)); - return _data[static_cast(row * stride() + row) + diagonal]; - } + inline DenseBandedRowIterator& operator-=(int idx) CADET_NOEXCEPT + { + // Subtract additional shifts to stay on the main diagonal + _pos -= idx * _matrix->stride() + idx; + _rowIdx -= idx; + return *this; + } - /** - * @brief Returns the number of elements in the matrix - * @return Number of elements in the matrix - */ - inline int elements() const CADET_NOEXCEPT { return _cols * _rows; } - - /** - * @brief Returns the number of columns - * @return Number of columns - */ - inline int columns() const CADET_NOEXCEPT { return _cols; } - - /** - * @brief Returns the number of rows - * @return Number of rows - */ - inline int rows() const CADET_NOEXCEPT { return _rows; } - - /** - * @brief Provides direct access to the underlying memory - * @return Pointer to the first element of the underlying array - */ - inline double* data() CADET_NOEXCEPT { return _data; } - inline double const* data() const CADET_NOEXCEPT { return _data; } - - /** - * @brief Provides direct access to the underlying memory - * @return Pointer to the first element of the underlying array - */ - inline lapackInt_t* pivotData() CADET_NOEXCEPT { return _pivot; } - inline lapackInt_t const* pivotData() const CADET_NOEXCEPT { return _pivot; } - - /** - * @brief Returns the number of elements in an array row - * @return Number of elements in a matrix row - */ - inline int stride() const CADET_NOEXCEPT { return _cols; } - - /** - * @brief Provides access to the underlying data in the given row - * @param [in] idx Index of the row - * @return Pointer to first element in the given row - */ - inline double* rowPtr(int idx) - { - cadet_assert(idx < _rows); - return _data + stride() * idx; - } + inline DenseBandedRowIterator operator+(int op) const CADET_NOEXCEPT + { + return DenseBandedRowIterator(*this, op); + } - /** - * @brief Creates a RowIterator pointing to the given row - * @param [in] idx Index of the row - * @return RowIterator pointing to the given row - */ - inline RowIterator row(int idx) - { - cadet_assert(idx < _rows); - return RowIterator(*this, idx); - } + inline friend DenseBandedRowIterator operator+(int op, const DenseBandedRowIterator& it) CADET_NOEXCEPT + { + return DenseBandedRowIterator(it, op); + } - /** - * @brief Creates a RowIterator pointing to the given row - * @param [in] idx Index of the row - * @param [in] diag Index of the diagonal the iterator points to - * @return RowIterator pointing to the given row - */ - inline RowIterator row(int idx, int diag) - { - cadet_assert(idx < _rows); - cadet_assert(static_cast(idx) + diag < _cols); - cadet_assert(static_cast(idx) + diag >= 0); - return RowIterator(*this, idx, diag); - } + inline DenseBandedRowIterator operator-(int op) const CADET_NOEXCEPT + { + return DenseBandedRowIterator(*this, -op); + } - /** - * @brief Copies a submatrix of a given banded matrix into this matrix - * @details Copies a rectangular submatrix from a given source in banded storage into this matrix. - * The top left matrix element is given by @p startRow and @p startDiag. The size of the - * submatrix is specified by @p numRows and @p numCols. - * @param [in] mat Source matrix in banded storage format - * @param [in] startRow Index of the first row of the submatrix in the source - * @param [in] startDiag Diagonal of the top left element in the source submatrix - * @param [in] numRows Number of rows to be copied - * @param [in] numCols Number of columns to be copied - * @tparam MatType_t Type of a matrix in banded storage - */ - template - inline void copySubmatrixFromBanded(const MatType_t& mat, const int startRow, const int startDiag, const int numRows, const int numCols) - { - cadet_assert(_rows >= numRows); - cadet_assert(_cols >= numCols); - cadet_assert(mat.rows() > startRow); - cadet_assert(mat.rows() >= startRow + numRows); - // @todo Add more asserts + inline friend DenseBandedRowIterator operator-(int op, const DenseBandedRowIterator& it) CADET_NOEXCEPT + { + return DenseBandedRowIterator(it, -op); + } - const int upperBand = static_cast(mat.upperBandwidth()); - const int lowerBand = static_cast(mat.lowerBandwidth()); + inline const MatrixType& matrix() const CADET_NOEXCEPT + { + return _matrix; + } - double* ptrDest = _data; - for (int r = 0; r < numRows; ++r) - { - for (int c = 0; c < numCols; ++c, ++ptrDest) - { - *ptrDest = 0.0; +private: + MatrixType* _matrix; + double* _pos; + double _dummy; + int _rowIdx; +}; - // Compute diagonal index of current position and shift it by startDiag - const int curDiag = c - r + startDiag; +/** + * @brief Represents a dense matrix base class providing common functionality (e.g., factorization) + * @details LAPACK uses column-major storage, whereas this class uses row-major. + * Thus, what we call a row here is actually a column for LAPACK. + * Concluding, we have to use the transposed LAPACK operations for + * solution and matrix-vector multiplication. The ordering is irrelevant + * for the factorization. + * + * Because of the transposition induced by the differing ordering, + * the number of columns and rows switches (e.g., columns are transposed + * rows). The ordering of the elements inside one (original) row is maintained + * (i.e., the first element in a row becomes the first element in a column + * and the last element in a row transposes to the last element in a column). + * + * LAPACK needs additional pivoting arrays for factorization, which are + * also stored in this class. + */ +class DenseMatrixBase +{ +public: + typedef DenseBandedRowIterator RowIterator; + friend class DenseBandedRowIterator; - if (cadet_likely((curDiag >= -lowerBand) && (curDiag <= upperBand))) - *ptrDest = mat.centered(r + startRow, curDiag); - } - } - } + ~DenseMatrixBase() CADET_NOEXCEPT + { + } - /** - * @brief Copies a submatrix of a given dense matrix into this matrix - * @details Copies a rectangular submatrix from a given source in dense storage into this matrix. - * The top left matrix element is given by @p startRow and @p startCol. The size of the - * submatrix is specified by @p numRows and @p numCols. - * - * The submatrix is copied into the top left corner of this matrix. The rest of the matrix - * is left unchanged. - * @param [in] mat Source matrix in dense storage format - * @param [in] startRow Index of the first row of the submatrix in the source - * @param [in] startCol Index of the first column of the submatrix in the source - * @param [in] numRows Number of rows to be copied - * @param [in] numCols Number of columns to be copied - */ - inline void copySubmatrix(const DenseMatrixBase& mat, const int startRow, const int startCol, const int numRows, const int numCols) - { - cadet_assert(_rows >= numRows); - cadet_assert(_cols >= numCols); - cadet_assert(mat._rows > startRow); - cadet_assert(mat._rows >= startRow + numRows); - cadet_assert(mat._cols > startCol); - cadet_assert(mat._cols >= startCol + numCols); - - double* ptrDest = _data; - double const* ptrSrc = mat._data + mat._cols * startRow + startCol; - for (int r = 0; r < numRows; ++r, ptrDest += _cols, ptrSrc += mat._cols) - { - std::copy(ptrSrc, ptrSrc + numCols, ptrDest); - } - } + /** + * @brief Sets all matrix elements to the given value + * @param [in] val Value all matrix elements are set to + */ + inline void setAll(double val) + { + std::fill(_data, _data + stride() * _rows, val); + } + + /** + * @brief Accesses an element in a diagonal of the matrix where the main diagonal has index @c 0 + * @details The @p diagonal determines the element in a row. A negative index indicates a lower diagonal, + * while a positive index indicates an upper diagonal. If @p diagonal is @c 0, then the main + * diagonal of the matrix is retrieved. + * + * @param [in] row Index of the row + * @param [in] diagonal Index of the diagonal + * + * @return Matrix element at the given position + */ + inline double& diagonalElement(int row, int diagonal) + { + return (*this)(row, diagonal); + } + inline double diagonalElement(int row, int diagonal) const + { + return (*this)(row, diagonal); + } + + /** + * @brief Accesses an element in the matrix + * @details In contrast to diagonalElement(), the indices do not refer to diagonals but to columns of the matrix. + * + * @param [in] row Index of the row + * @param [in] col Index of the column (from @c 0 to @c columns) + * + * @return Matrix element at the given position + */ + inline double& native(int row, int col) + { + cadet_assert(row < _rows); + cadet_assert(col < _cols); + return _data[row * stride() + col]; + } + + inline double native(int row, int col) const + { + cadet_assert(row < _rows); + cadet_assert(col < _cols); + return _data[row * stride() + col]; + } + + inline double& operator()(int row, int diagonal) + { + cadet_assert(row < _rows); + cadet_assert(diagonal < static_cast(_cols - row)); + cadet_assert(-diagonal <= static_cast(row)); + return _data[static_cast(row * stride() + row) + diagonal]; + } + + inline double operator()(int row, int diagonal) const + { + cadet_assert(row < _rows); + cadet_assert(diagonal < static_cast(_cols - row)); + cadet_assert(-diagonal <= static_cast(row)); + return _data[static_cast(row * stride() + row) + diagonal]; + } + + /** + * @brief Returns the number of elements in the matrix + * @return Number of elements in the matrix + */ + inline int elements() const CADET_NOEXCEPT + { + return _cols * _rows; + } + + /** + * @brief Returns the number of columns + * @return Number of columns + */ + inline int columns() const CADET_NOEXCEPT + { + return _cols; + } + + /** + * @brief Returns the number of rows + * @return Number of rows + */ + inline int rows() const CADET_NOEXCEPT + { + return _rows; + } + + /** + * @brief Provides direct access to the underlying memory + * @return Pointer to the first element of the underlying array + */ + inline double* data() CADET_NOEXCEPT + { + return _data; + } + inline double const* data() const CADET_NOEXCEPT + { + return _data; + } + + /** + * @brief Provides direct access to the underlying memory + * @return Pointer to the first element of the underlying array + */ + inline lapackInt_t* pivotData() CADET_NOEXCEPT + { + return _pivot; + } + inline lapackInt_t const* pivotData() const CADET_NOEXCEPT + { + return _pivot; + } + + /** + * @brief Returns the number of elements in an array row + * @return Number of elements in a matrix row + */ + inline int stride() const CADET_NOEXCEPT + { + return _cols; + } - /** - * @brief Adds a submatrix @f$ X @f$ of this matrix to a submatrix @f$ B @f$ of another matrix - * @details Performs @f$ Y = Y + \alpha X @f$. - * @param [in,out] dest Destination matrix containing @f$ Y @f$ - * @param [in] factor Factor @f$ \alpha @f$ - * @param [in] srcRow Index of the first row of the source submatrix - * @param [in] srcCol Index of the first column of the source submatrix - * @param [in] numRows Number of rows of the submatrix - * @param [in] numCols Number of columns of the submatrix - * @param [in] destRow Index of the first row of the destination submatrix - * @param [in] destCol Index of the first column of the destination submatrix - */ - inline void addSubmatrixTo(DenseMatrixBase& dest, const double factor, const int srcRow, const int srcCol, const int numRows, const int numCols, const int destRow, const int destCol) + /** + * @brief Provides access to the underlying data in the given row + * @param [in] idx Index of the row + * @return Pointer to first element in the given row + */ + inline double* rowPtr(int idx) + { + cadet_assert(idx < _rows); + return _data + stride() * idx; + } + + /** + * @brief Creates a RowIterator pointing to the given row + * @param [in] idx Index of the row + * @return RowIterator pointing to the given row + */ + inline RowIterator row(int idx) + { + cadet_assert(idx < _rows); + return RowIterator(*this, idx); + } + + /** + * @brief Creates a RowIterator pointing to the given row + * @param [in] idx Index of the row + * @param [in] diag Index of the diagonal the iterator points to + * @return RowIterator pointing to the given row + */ + inline RowIterator row(int idx, int diag) + { + cadet_assert(idx < _rows); + cadet_assert(static_cast(idx) + diag < _cols); + cadet_assert(static_cast(idx) + diag >= 0); + return RowIterator(*this, idx, diag); + } + + /** + * @brief Copies a submatrix of a given banded matrix into this matrix + * @details Copies a rectangular submatrix from a given source in banded storage into this matrix. + * The top left matrix element is given by @p startRow and @p startDiag. The size of the + * submatrix is specified by @p numRows and @p numCols. + * @param [in] mat Source matrix in banded storage format + * @param [in] startRow Index of the first row of the submatrix in the source + * @param [in] startDiag Diagonal of the top left element in the source submatrix + * @param [in] numRows Number of rows to be copied + * @param [in] numCols Number of columns to be copied + * @tparam MatType_t Type of a matrix in banded storage + */ + template + inline void copySubmatrixFromBanded(const MatType_t& mat, const int startRow, const int startDiag, + const int numRows, const int numCols) + { + cadet_assert(_rows >= numRows); + cadet_assert(_cols >= numCols); + cadet_assert(mat.rows() > startRow); + cadet_assert(mat.rows() >= startRow + numRows); + // @todo Add more asserts + + const int upperBand = static_cast(mat.upperBandwidth()); + const int lowerBand = static_cast(mat.lowerBandwidth()); + + double* ptrDest = _data; + for (int r = 0; r < numRows; ++r) { - cadet_assert(_rows >= srcRow + numRows); - cadet_assert(_cols >= srcCol + numCols); - cadet_assert(_rows > srcRow); - cadet_assert(_cols > srcCol); - cadet_assert(dest._rows >= destRow + numRows); - cadet_assert(dest._cols >= destCol + numCols); - cadet_assert(dest._rows > destRow); - cadet_assert(dest._cols > destCol); - - double* ptrDest = dest._data + destRow * dest.stride() + destCol; - double const* ptrSrc = _data + srcRow * stride() + srcCol; - for (int r = 0; r < numRows; ++r, ptrDest += dest.stride(), ptrSrc += stride()) + for (int c = 0; c < numCols; ++c, ++ptrDest) { - for (int c = 0; c < numCols; ++c) - ptrDest[c] += factor * ptrSrc[c]; + *ptrDest = 0.0; + + // Compute diagonal index of current position and shift it by startDiag + const int curDiag = c - r + startDiag; + + if (cadet_likely((curDiag >= -lowerBand) && (curDiag <= upperBand))) + *ptrDest = mat.centered(r + startRow, curDiag); } } + } - /** - * @brief Sets all elements of a submatrix of this matrix to the given value - * @details The submatrix is given by its first row and column and its number of rows and columns. - * @param [in] val Value the submatrix is set to - * @param [in] startRow Index of the first row of the submatrix - * @param [in] startCol Index of the first column of the submatrix - * @param [in] numRows Number of rows of the submatrix - * @param [in] numCols Number of columns of the submatrix - */ - void submatrixSetAll(double val, int startRow, int startCol, - int numRows, int numCols); - - /** - * @brief Copies a given matrix into a submatrix of this matrix - * @details The submatrix is given by its first row and column and its number of rows and columns. - * @param [in] mat Source matrix to be copied into the submatrix - * @param [in] startRow Index of the first row of the submatrix - * @param [in] startCol Index of the first column of the submatrix - * @param [in] numRows Number of rows of the submatrix - * @param [in] numCols Number of columns of the submatrix - */ - void submatrixAssign(const DenseMatrixBase& mat, int startRow, int startCol, - int numRows, int numCols); - - /** - * @brief Copies a row of a given dense matrix into this matrix - * @details Copies a row from a given source in dense storage into this matrix. - * @param [in] mat Source matrix in dense storage format - * @param [in] srcRow Index of the source row to be copied from @p mat - * @param [in] destRow Index of the target row - */ - inline void copyRowFrom(const DenseMatrixBase& mat, const int srcRow, int destRow) + /** + * @brief Copies a submatrix of a given dense matrix into this matrix + * @details Copies a rectangular submatrix from a given source in dense storage into this matrix. + * The top left matrix element is given by @p startRow and @p startCol. The size of the + * submatrix is specified by @p numRows and @p numCols. + * + * The submatrix is copied into the top left corner of this matrix. The rest of the matrix + * is left unchanged. + * @param [in] mat Source matrix in dense storage format + * @param [in] startRow Index of the first row of the submatrix in the source + * @param [in] startCol Index of the first column of the submatrix in the source + * @param [in] numRows Number of rows to be copied + * @param [in] numCols Number of columns to be copied + */ + inline void copySubmatrix(const DenseMatrixBase& mat, const int startRow, const int startCol, const int numRows, + const int numCols) + { + cadet_assert(_rows >= numRows); + cadet_assert(_cols >= numCols); + cadet_assert(mat._rows > startRow); + cadet_assert(mat._rows >= startRow + numRows); + cadet_assert(mat._cols > startCol); + cadet_assert(mat._cols >= startCol + numCols); + + double* ptrDest = _data; + double const* ptrSrc = mat._data + mat._cols * startRow + startCol; + for (int r = 0; r < numRows; ++r, ptrDest += _cols, ptrSrc += mat._cols) { - cadet_assert(mat._rows > srcRow); - cadet_assert(_cols >= mat._cols); - std::copy_n(mat._data + mat._cols * srcRow, mat._cols, _data + _cols * destRow); + std::copy(ptrSrc, ptrSrc + numCols, ptrDest); } + } - /** - * @brief Multiplies the matrix @f$ A @f$ with a given vector @f$ x @f$ using LAPACK - * @details Computes @f$ y = Ax @f$, where @f$ A @f$ is this matrix and @f$ x @f$ is given. - * @param [in] x Vector this matrix is multiplied with - * @param [out] y Result of the matrix-vector multiplication - */ - inline void multiplyVector(const double* const x, double* const y) const + /** + * @brief Adds a submatrix @f$ X @f$ of this matrix to a submatrix @f$ B @f$ of another matrix + * @details Performs @f$ Y = Y + \alpha X @f$. + * @param [in,out] dest Destination matrix containing @f$ Y @f$ + * @param [in] factor Factor @f$ \alpha @f$ + * @param [in] srcRow Index of the first row of the source submatrix + * @param [in] srcCol Index of the first column of the source submatrix + * @param [in] numRows Number of rows of the submatrix + * @param [in] numCols Number of columns of the submatrix + * @param [in] destRow Index of the first row of the destination submatrix + * @param [in] destCol Index of the first column of the destination submatrix + */ + inline void addSubmatrixTo(DenseMatrixBase& dest, const double factor, const int srcRow, const int srcCol, + const int numRows, const int numCols, const int destRow, const int destCol) + { + cadet_assert(_rows >= srcRow + numRows); + cadet_assert(_cols >= srcCol + numCols); + cadet_assert(_rows > srcRow); + cadet_assert(_cols > srcCol); + cadet_assert(dest._rows >= destRow + numRows); + cadet_assert(dest._cols >= destCol + numCols); + cadet_assert(dest._rows > destRow); + cadet_assert(dest._cols > destCol); + + double* ptrDest = dest._data + destRow * dest.stride() + destCol; + double const* ptrSrc = _data + srcRow * stride() + srcCol; + for (int r = 0; r < numRows; ++r, ptrDest += dest.stride(), ptrSrc += stride()) { - multiplyVector(x, 1.0, 0.0, y); + for (int c = 0; c < numCols; ++c) + ptrDest[c] += factor * ptrSrc[c]; } + } - /** - * @brief Multiplies a submatrix @f$ A @f$ with a given vector @f$ x @f$ using LAPACK - * @details Computes @f$ y = Ax @f$, where @f$ A @f$ is a submatrix of this matrix and @f$ x @f$ is given. - * The submatrix is given by its first row and column and its number of rows and columns. - * @param [in] x Vector the submatrix is multiplied with - * @param [in] startRow Index of the first row of the submatrix - * @param [in] startCol Index of the first column of the submatrix - * @param [in] numRows Number of rows of the submatrix - * @param [in] numCols Number of columns of the submatrix - * @param [out] y Result of the submatrix-vector multiplication - */ - inline void submatrixMultiplyVector(const double* const x, int startRow, int startCol, - int numRows, int numCols, double* const y) const - { - submatrixMultiplyVector(x, startRow, startCol, numRows, numCols, 1.0, 0.0, y); - } + /** + * @brief Sets all elements of a submatrix of this matrix to the given value + * @details The submatrix is given by its first row and column and its number of rows and columns. + * @param [in] val Value the submatrix is set to + * @param [in] startRow Index of the first row of the submatrix + * @param [in] startCol Index of the first column of the submatrix + * @param [in] numRows Number of rows of the submatrix + * @param [in] numCols Number of columns of the submatrix + */ + void submatrixSetAll(double val, int startRow, int startCol, int numRows, int numCols); - /** - * @brief Multiplies the transpose of the matrix @f$ A @f$ with a given vector @f$ x @f$ using LAPACK - * @details Computes @f$ y = A^Tx @f$, where @f$ A @f$ is this matrix and @f$ x @f$ is given. - * @param [in] x Vector this matrix is multiplied with - * @param [out] y Result of the matrix-vector multiplication - */ - inline void transposedMultiplyVector(const double* const x, double* const y) const - { - transposedMultiplyVector(x, 1.0, 0.0, y); - } + /** + * @brief Copies a given matrix into a submatrix of this matrix + * @details The submatrix is given by its first row and column and its number of rows and columns. + * @param [in] mat Source matrix to be copied into the submatrix + * @param [in] startRow Index of the first row of the submatrix + * @param [in] startCol Index of the first column of the submatrix + * @param [in] numRows Number of rows of the submatrix + * @param [in] numCols Number of columns of the submatrix + */ + void submatrixAssign(const DenseMatrixBase& mat, int startRow, int startCol, int numRows, int numCols); - /** - * @brief Multiplies the transpose of a submatrix @f$ A @f$ with a given vector @f$ x @f$ using LAPACK - * @details Computes @f$ y = A^Tx @f$, where @f$ A @f$ is a submatrix of this matrix and @f$ x @f$ is given. - * The submatrix is given by its first row and column and its number of rows and columns. - * @param [in] x Vector the submatrix is multiplied with - * @param [in] startRow Index of the first row of the submatrix - * @param [in] startCol Index of the first column of the submatrix - * @param [in] numRows Number of rows of the submatrix - * @param [in] numCols Number of columns of the submatrix - * @param [out] y Result of the submatrix-vector multiplication - */ - inline void transposedSubmatrixMultiplyVector(const double* const x, int startRow, int startCol, - int numRows, int numCols, double* const y) const - { - transposedSubmatrixMultiplyVector(x, startRow, startCol, numRows, numCols, 1.0, 0.0, y); - } + /** + * @brief Copies a row of a given dense matrix into this matrix + * @details Copies a row from a given source in dense storage into this matrix. + * @param [in] mat Source matrix in dense storage format + * @param [in] srcRow Index of the source row to be copied from @p mat + * @param [in] destRow Index of the target row + */ + inline void copyRowFrom(const DenseMatrixBase& mat, const int srcRow, int destRow) + { + cadet_assert(mat._rows > srcRow); + cadet_assert(_cols >= mat._cols); + std::copy_n(mat._data + mat._cols * srcRow, mat._cols, _data + _cols * destRow); + } - /** - * @brief Multiplies the matrix @f$ A @f$ with a given vector @f$ x @f$ and adds it to another vector using LAPACK - * @details Computes @f$ y = \alpha Ax + \beta y @f$, where @f$ A @f$ is this matrix and @f$ x @f$ is given. - * @param [in] x Vector this matrix is multiplied with - * @param [in] alpha Factor @f$ \alpha @f$ in front of @f$ Ax @f$ - * @param [in] beta Factor @f$ \beta @f$ in front of @f$ y @f$ - * @param [out] y Result of the matrix-vector multiplication - */ - void multiplyVector(const double* const x, double alpha, double beta, double* const y) const; - - /** - * @brief Multiplies a submatrix @f$ A @f$ with a given vector @f$ x @f$ and adds it to another vector using LAPACK - * @details Computes @f$ y = \alpha Ax + \beta y @f$, where @f$ A @f$ is a submatrix of this matrix and @f$ x @f$ is given. - * The submatrix is given by its first row and column and its number of rows and columns. - * @param [in] x Vector the submatrix is multiplied with - * @param [in] startRow Index of the first row of the submatrix - * @param [in] startCol Index of the first column of the submatrix - * @param [in] numRows Number of rows of the submatrix - * @param [in] numCols Number of columns of the submatrix - * @param [in] alpha Factor @f$ \alpha @f$ in front of @f$ Ax @f$ - * @param [in] beta Factor @f$ \beta @f$ in front of @f$ y @f$ - * @param [out] y Result of the submatrix-vector multiplication - */ - void submatrixMultiplyVector(const double* const x, int startRow, int startCol, - int numRows, int numCols, double alpha, double beta, double* const y) const; - - /** - * @brief Multiplies the transpose of the matrix @f$ A @f$ with a given vector @f$ x @f$ and adds it to another vector using LAPACK - * @details Computes @f$ y = \alpha A^T x + \beta y @f$, where @f$ A @f$ is this matrix and @f$ x @f$ is given. - * @param [in] x Vector this matrix is multiplied with - * @param [in] alpha Factor @f$ \alpha @f$ in front of @f$ A^T x @f$ - * @param [in] beta Factor @f$ \beta @f$ in front of @f$ y @f$ - * @param [out] y Result of the matrix-vector multiplication - */ - void transposedMultiplyVector(const double* const x, double alpha, double beta, double* const y) const; - - /** - * @brief Multiplies the transpose of a submatrix @f$ A @f$ with a given vector @f$ x @f$ and adds it to another vector using LAPACK - * @details Computes @f$ y = \alpha A^T x + \beta y @f$, where @f$ A @f$ is a submatrix of this matrix and @f$ x @f$ is given. - * The submatrix is given by its first row and column and its number of rows and columns. - * @param [in] x Vector the submatrix is multiplied with - * @param [in] startRow Index of the first row of the submatrix - * @param [in] startCol Index of the first column of the submatrix - * @param [in] numRows Number of rows of the submatrix - * @param [in] numCols Number of columns of the submatrix - * @param [in] alpha Factor @f$ \alpha @f$ in front of @f$ A^T x @f$ - * @param [in] beta Factor @f$ \beta @f$ in front of @f$ y @f$ - * @param [out] y Result of the submatrix-vector multiplication - */ - void transposedSubmatrixMultiplyVector(const double* const x, int startRow, int startCol, - int numRows, int numCols, double alpha, double beta, double* const y) const; - - /** - * @brief Factorizes the matrix using LAPACK (performs LU factorization) - * @details The original matrix is overwritten with the factorization and all data is lost. - * @return @c true if the factorization was successful, otherwise @c false - */ - bool factorize(); - - /** - * @brief Uses the factorized matrix to solve the equation @f$ Ax = y @f$ with LAPACK - * @details Before the equation can be solved, the matrix has to be factorized first by calling factorize(). - * @param [in,out] rhs On entry pointer to the right hand side vector @f$ y @f$ of the equation, on exit the solution @f$ x @f$ - * @return @c true if the solution process was successful, otherwise @c false - */ - bool solve(double* rhs) const; - - /** - * @brief Uses the factorized matrix to solve the equation @f$ Ax = y @f$ with LAPACK - * @details Before the equation can be solved, the matrix has to be factorized first by calling factorize(). - * It is assumed that row scaling has been applied to the matrix before factorization. - * In order to solve the equation system, the right hand side has to be scaled accordingly. - * This is handled automatically by passing the required scaling factors. - * @param [in] scalingFactors Vector with scaling factor for each row - * @param [in,out] rhs On entry pointer to the right hand side vector @f$ y @f$ of the equation, on exit the solution @f$ x @f$ - * @return @c true if the solution process was successful, otherwise @c false - */ - bool solve(double const* scalingFactors, double* rhs) const; - - /** - * @brief Returns the optimal working memory size for solving @f$ \text{min}_x \lVert Ax - y \rVert @f$ with LAPACK - * @details LAPACK requires at least @f$ 2 mn @f$ doubles, where @f$ m @f$ is the number of rows and @f$ n @f$ the number of columns. - * @return The optimal number of doubles in the working memory - */ - int optimalLeastSquaresWorkspace() const; - - /** - * @brief Solves the linear least squares problem @f$ \text{min}_x \lVert Ax - y \rVert @f$ using LAPACK (QR decomposition) - * @details The least squares system is solved using a QR decomposition performed by LAPACK. The matrix is - * overwritten with the factorization and the original data is lost. It is assumed that @f$ A @f$ - * has full rank, otherwise the solution will fail. The optimal amount of working memory can be - * computed by optimalLeastSquaresWorkspace(). - * @param [in,out] rhs On entry pointer to the right hand side vector @f$ y @f$ of the equation, - * on exit the solution @f$ x @f$ in the first components - * @param [in,out] workspace Pointer to workspace required by LAPACK to perform the factorization - * (at least @f$ 2 \min\{m,n\} @f$ for an @f$ m \times n @f$ matrix) - * @param [in] size Size of the provided workspace - * @return @c true if the solution process was successful, otherwise @c false - */ - bool leastSquaresSolve(double* rhs, double* workspace, int size); - - /** - * @brief Performs a numerically robust (but slower and memory consuming) factorization of the matrix using LAPACK (QR factorization) - * @details The original matrix is overwritten with the factorization and all data is lost. - * @param [in,out] workspace Working memory which on exit contains the scalar factors of the Householder reflectors (size @f$ 2n @f$ for an @f$ n \times n@f$ matrix). - * @return @c true if the factorization was successful, otherwise @c false - */ - bool robustFactorize(double* const workspace); - - /** - * @brief Uses the factorized matrix to solve the equation @f$ Ax = y @f$ with LAPACK - * @details Before the equation can be solved, the matrix has to be factorized first by calling robustFactorize(). - * @param [in,out] rhs On entry pointer to the right hand side vector @f$ y @f$ of the equation, on exit the solution @f$ x @f$ - * @param [in,out] workspace Workspace used by robustFactorize() (i.e., it has to contain the scalar factors of the Householder reflectors). - * For an @f$ n \times n@f$ matrix, an array of size @f$ 2n @f$ is required. - * Note that if the equation system is singular, the least squares solution @f$ x @f$ satisfying @f$ \text{min}_x \lVert Ax - y \rVert @f$ - * is returned. - * @return @c true if the solution process was successful, otherwise @c false - */ - bool robustSolve(double* rhs, double* const workspace) const; - - /** - * @brief Uses the factorized matrix to solve the equation @f$ Ax = y @f$ with LAPACK - * @details Before the equation can be solved, the matrix has to be factorized first by calling robustFactorize(). - * It is assumed that row scaling has been applied to the matrix before factorization. - * In order to solve the equation system, the right hand side has to be scaled accordingly. - * This is handled automatically by passing the required scaling factors. - * @param [in] scalingFactors Vector with scaling factor for each row - * @param [in,out] rhs On entry pointer to the right hand side vector @f$ y @f$ of the equation, on exit the solution @f$ x @f$ - * @param [in,out] workspace Workspace used by robustFactorize() (i.e., it has to contain the scalar factors of the Householder reflectors). - * For an @f$ n \times n@f$ matrix, an array of size @f$ 2n @f$ is required. - * Note that if the equation system is singular, the least squares solution @f$ x @f$ satisfying @f$ \text{min}_x \lVert Ax - y \rVert @f$ - * is returned. - * @return @c true if the solution process was successful, otherwise @c false - */ - bool robustSolve(double const* scalingFactors, double* rhs, double* const workspace) const; - - /** - * @brief Returns the size of the workspace required for calling robustFactorize() - * @details The workspace size is @f$ 2n @f$ for an @f$ n \times n@f$ matrix. - * @return Size of the workspace required for robustFactorize() - */ - inline int robustWorkspaceSize() const { return 2 * rows(); } - - /** - * @brief Scales rows by dividing them with a given factor - * @details This corresponds to multiplying by a diagonal matrix from the left (i.e., @f$ D^{-1} A @f$). - * @param [in] scalingFactors Vector with scaling factor for each row - */ - inline void scaleRows(double const* scalingFactors) { scaleRows(scalingFactors, rows()); } - - /** - * @brief Scales the first rows by dividing them with a given factor - * @details This corresponds to multiplying by a diagonal matrix from the left (i.e., @f$ D^{-1} A @f$). - * Only the first @p numRows rows are scaled. - * @param [in] scalingFactors Vector with scaling factor for each row - * @param [in] numRows The number of rows which are to be scaled (from the top) - */ - void scaleRows(double const* scalingFactors, int numRows); - - /** - * @brief Scales columns by dividing them with a given factor - * @details This corresponds to multiplying by a diagonal matrix from the right (i.e., @f$ A D^{-1} @f$). - * @param [in] scalingFactors Vector with scaling factor for each column - */ - inline void scaleColumns(double const* scalingFactors) { scaleColumns(scalingFactors, columns()); } - - /** - * @brief Scales the first columns by dividing them with a given factor - * @details This corresponds to multiplying by a diagonal matrix from the right (i.e., @f$ A D^{-1} @f$). - * Only the first @p numCols columns are scaled. - * @param [in] scalingFactors Vector with scaling factor for each column - * @param [in] numCols The number of columns which are to be scaled (from the left) - */ - void scaleColumns(double const* scalingFactors, int numCols); - - /** - * @brief Computes row scaling factors such that the largest absolute value in each row is 1 - * @details This can be used to equilibrate the matrix by calling scaleRows(). - * @param [out] scalingFactors Vector in which the row scaling factors are written - */ - inline void rowScaleFactors(double* scalingFactors) const { rowScaleFactors(scalingFactors, rows()); } - - /** - * @brief Computes row scaling factors for some rows such that the largest absolute value in each row is 1 - * @details This can be used to equilibrate the matrix by calling scaleRows(). - * The scaling factors are computed for the first @p numRows rows from the top. - * @param [out] scalingFactors Vector in which the row scaling factors are written - * @param [in] numRows The number of rows to be scaled - */ - void rowScaleFactors(double* scalingFactors, int numRows) const; - - /** - * @brief Computes column scaling factors such that the largest absolute value in each column is 1 - * @details This can be used to equilibrate the matrix by calling scaleColumns(). - * @param [out] scalingFactors Vector in which the column scaling factors are written - */ - inline void columnScaleFactors(double* scalingFactors) const { columnScaleFactors(scalingFactors, columns()); } - - /** - * @brief Computes column scaling factors for some columns such that the largest absolute value in each column is 1 - * @details This can be used to equilibrate the matrix by calling scaleColumns(). - * The scaling factors are computed for the first @p numCols columns from the left. - * @param [out] scalingFactors Vector in which the column scaling factors are written - * @param [in] numCols The number of columns to be scaled - */ - void columnScaleFactors(double* scalingFactors, int numCols) const; - - /** - * @brief Resizes the matrix to the given size without deleting its content - * @details The matrix size is not checked and no memory is allocated or deleted. - * The user has to take care that the memory is big enough to hold all - * elements (beware of access violations). - * @param [in] rows The number of rows - * @param [in] cols The number of columns - */ - inline void shrinkOrExpandFast(int rows, int cols) - { - _cols = cols; - _rows = rows; - } + /** + * @brief Multiplies the matrix @f$ A @f$ with a given vector @f$ x @f$ using LAPACK + * @details Computes @f$ y = Ax @f$, where @f$ A @f$ is this matrix and @f$ x @f$ is given. + * @param [in] x Vector this matrix is multiplied with + * @param [out] y Result of the matrix-vector multiplication + */ + inline void multiplyVector(const double* const x, double* const y) const + { + multiplyVector(x, 1.0, 0.0, y); + } - /** - * @brief Resizes the matrix to the given size - * @details The matrix size is not checked and no memory is allocated or deleted. - * The user has to take care that the memory is big enough to hold all - * elements (beware of access violations). - * All data is lost during this operation, the selected area is zeroed. - * - * @param [in] rows The number of rows - * @param [in] cols The number of columns - */ - inline void shrinkOrExpand(int rows, int cols) - { - shrinkOrExpandFast(rows, cols); - setAll(0.0); - } + /** + * @brief Multiplies a submatrix @f$ A @f$ with a given vector @f$ x @f$ using LAPACK + * @details Computes @f$ y = Ax @f$, where @f$ A @f$ is a submatrix of this matrix and @f$ x @f$ is given. + * The submatrix is given by its first row and column and its number of rows and columns. + * @param [in] x Vector the submatrix is multiplied with + * @param [in] startRow Index of the first row of the submatrix + * @param [in] startCol Index of the first column of the submatrix + * @param [in] numRows Number of rows of the submatrix + * @param [in] numCols Number of columns of the submatrix + * @param [out] y Result of the submatrix-vector multiplication + */ + inline void submatrixMultiplyVector(const double* const x, int startRow, int startCol, int numRows, int numCols, + double* const y) const + { + submatrixMultiplyVector(x, startRow, startCol, numRows, numCols, 1.0, 0.0, y); + } - protected: - double* _data; //!< Pointer to the array in which the matrix is stored - int _rows; //!< Number of rows - int _cols; //!< Number of columns - lapackInt_t* _pivot; //!< Pointer to an array which is used for pivoting by factorization methods - - /** - * @brief Creates an empty, unitialized matrix - * @details No memory is allocated for the matrix. Users have to call resize() first. - */ - DenseMatrixBase() CADET_NOEXCEPT : _data(nullptr), _rows(0), _cols(0), _pivot(nullptr) { } - - /** - * @brief Initializes the matrix with the given memory of the given size - * @param [in] data Pointer to data array of size `rows * cols` - * @param [in] pivot Pointer to pivot array of size at least `min(rows, cols)` - * @param [in] rows Number of rows - * @param [in] cols Number of columns - */ - DenseMatrixBase(double* const data, lapackInt_t* const pivot, int rows, int cols) CADET_NOEXCEPT : _data(data), _rows(rows), _cols(cols), _pivot(pivot) { } - - /** - * @brief Copies all values from the source to the local array - * @param [in] src Data to be copied - */ - inline void copyValues(double const* const src) - { - std::copy(src, src + stride() * _rows, _data); - } + /** + * @brief Multiplies the transpose of the matrix @f$ A @f$ with a given vector @f$ x @f$ using LAPACK + * @details Computes @f$ y = A^Tx @f$, where @f$ A @f$ is this matrix and @f$ x @f$ is given. + * @param [in] x Vector this matrix is multiplied with + * @param [out] y Result of the matrix-vector multiplication + */ + inline void transposedMultiplyVector(const double* const x, double* const y) const + { + transposedMultiplyVector(x, 1.0, 0.0, y); + } - /** - * @brief Copies all pivots from the source to the local array - * @param [in] src Data to be copied - */ - inline void copyPivot(lapackInt_t const* const src) - { - std::copy(src, src + _cols, _pivot); - } - }; + /** + * @brief Multiplies the transpose of a submatrix @f$ A @f$ with a given vector @f$ x @f$ using LAPACK + * @details Computes @f$ y = A^Tx @f$, where @f$ A @f$ is a submatrix of this matrix and @f$ x @f$ is given. + * The submatrix is given by its first row and column and its number of rows and columns. + * @param [in] x Vector the submatrix is multiplied with + * @param [in] startRow Index of the first row of the submatrix + * @param [in] startCol Index of the first column of the submatrix + * @param [in] numRows Number of rows of the submatrix + * @param [in] numCols Number of columns of the submatrix + * @param [out] y Result of the submatrix-vector multiplication + */ + inline void transposedSubmatrixMultiplyVector(const double* const x, int startRow, int startCol, int numRows, + int numCols, double* const y) const + { + transposedSubmatrixMultiplyVector(x, startRow, startCol, numRows, numCols, 1.0, 0.0, y); + } - std::ostream& operator<<(std::ostream& out, const DenseMatrixBase& bm); + /** + * @brief Multiplies the matrix @f$ A @f$ with a given vector @f$ x @f$ and adds it to another vector using LAPACK + * @details Computes @f$ y = \alpha Ax + \beta y @f$, where @f$ A @f$ is this matrix and @f$ x @f$ is given. + * @param [in] x Vector this matrix is multiplied with + * @param [in] alpha Factor @f$ \alpha @f$ in front of @f$ Ax @f$ + * @param [in] beta Factor @f$ \beta @f$ in front of @f$ y @f$ + * @param [out] y Result of the matrix-vector multiplication + */ + void multiplyVector(const double* const x, double alpha, double beta, double* const y) const; + + /** + * @brief Multiplies a submatrix @f$ A @f$ with a given vector @f$ x @f$ and adds it to another vector using LAPACK + * @details Computes @f$ y = \alpha Ax + \beta y @f$, where @f$ A @f$ is a submatrix of this matrix and @f$ x @f$ is + * given. The submatrix is given by its first row and column and its number of rows and columns. + * @param [in] x Vector the submatrix is multiplied with + * @param [in] startRow Index of the first row of the submatrix + * @param [in] startCol Index of the first column of the submatrix + * @param [in] numRows Number of rows of the submatrix + * @param [in] numCols Number of columns of the submatrix + * @param [in] alpha Factor @f$ \alpha @f$ in front of @f$ Ax @f$ + * @param [in] beta Factor @f$ \beta @f$ in front of @f$ y @f$ + * @param [out] y Result of the submatrix-vector multiplication + */ + void submatrixMultiplyVector(const double* const x, int startRow, int startCol, int numRows, int numCols, + double alpha, double beta, double* const y) const; + + /** + * @brief Multiplies the transpose of the matrix @f$ A @f$ with a given vector @f$ x @f$ and adds it to another + * vector using LAPACK + * @details Computes @f$ y = \alpha A^T x + \beta y @f$, where @f$ A @f$ is this matrix and @f$ x @f$ is given. + * @param [in] x Vector this matrix is multiplied with + * @param [in] alpha Factor @f$ \alpha @f$ in front of @f$ A^T x @f$ + * @param [in] beta Factor @f$ \beta @f$ in front of @f$ y @f$ + * @param [out] y Result of the matrix-vector multiplication + */ + void transposedMultiplyVector(const double* const x, double alpha, double beta, double* const y) const; + + /** + * @brief Multiplies the transpose of a submatrix @f$ A @f$ with a given vector @f$ x @f$ and adds it to another + * vector using LAPACK + * @details Computes @f$ y = \alpha A^T x + \beta y @f$, where @f$ A @f$ is a submatrix of this matrix and @f$ x @f$ + * is given. The submatrix is given by its first row and column and its number of rows and columns. + * @param [in] x Vector the submatrix is multiplied with + * @param [in] startRow Index of the first row of the submatrix + * @param [in] startCol Index of the first column of the submatrix + * @param [in] numRows Number of rows of the submatrix + * @param [in] numCols Number of columns of the submatrix + * @param [in] alpha Factor @f$ \alpha @f$ in front of @f$ A^T x @f$ + * @param [in] beta Factor @f$ \beta @f$ in front of @f$ y @f$ + * @param [out] y Result of the submatrix-vector multiplication + */ + void transposedSubmatrixMultiplyVector(const double* const x, int startRow, int startCol, int numRows, int numCols, + double alpha, double beta, double* const y) const; + + /** + * @brief Factorizes the matrix using LAPACK (performs LU factorization) + * @details The original matrix is overwritten with the factorization and all data is lost. + * @return @c true if the factorization was successful, otherwise @c false + */ + bool factorize(); -} + /** + * @brief Uses the factorized matrix to solve the equation @f$ Ax = y @f$ with LAPACK + * @details Before the equation can be solved, the matrix has to be factorized first by calling factorize(). + * @param [in,out] rhs On entry pointer to the right hand side vector @f$ y @f$ of the equation, on exit the + * solution @f$ x @f$ + * @return @c true if the solution process was successful, otherwise @c false + */ + bool solve(double* rhs) const; + + /** + * @brief Uses the factorized matrix to solve the equation @f$ Ax = y @f$ with LAPACK + * @details Before the equation can be solved, the matrix has to be factorized first by calling factorize(). + * It is assumed that row scaling has been applied to the matrix before factorization. + * In order to solve the equation system, the right hand side has to be scaled accordingly. + * This is handled automatically by passing the required scaling factors. + * @param [in] scalingFactors Vector with scaling factor for each row + * @param [in,out] rhs On entry pointer to the right hand side vector @f$ y @f$ of the equation, on exit the + * solution @f$ x @f$ + * @return @c true if the solution process was successful, otherwise @c false + */ + bool solve(double const* scalingFactors, double* rhs) const; + + /** + * @brief Returns the optimal working memory size for solving @f$ \text{min}_x \lVert Ax - y \rVert @f$ with LAPACK + * @details LAPACK requires at least @f$ 2 mn @f$ doubles, where @f$ m @f$ is the number of rows and @f$ n @f$ the + * number of columns. + * @return The optimal number of doubles in the working memory + */ + int optimalLeastSquaresWorkspace() const; + + /** + * @brief Solves the linear least squares problem @f$ \text{min}_x \lVert Ax - y \rVert @f$ using LAPACK (QR + * decomposition) + * @details The least squares system is solved using a QR decomposition performed by LAPACK. The matrix is + * overwritten with the factorization and the original data is lost. It is assumed that @f$ A @f$ + * has full rank, otherwise the solution will fail. The optimal amount of working memory can be + * computed by optimalLeastSquaresWorkspace(). + * @param [in,out] rhs On entry pointer to the right hand side vector @f$ y @f$ of the equation, + * on exit the solution @f$ x @f$ in the first components + * @param [in,out] workspace Pointer to workspace required by LAPACK to perform the factorization + * (at least @f$ 2 \min\{m,n\} @f$ for an @f$ m \times n @f$ matrix) + * @param [in] size Size of the provided workspace + * @return @c true if the solution process was successful, otherwise @c false + */ + bool leastSquaresSolve(double* rhs, double* workspace, int size); + + /** + * @brief Performs a numerically robust (but slower and memory consuming) factorization of the matrix using LAPACK + * (QR factorization) + * @details The original matrix is overwritten with the factorization and all data is lost. + * @param [in,out] workspace Working memory which on exit contains the scalar factors of the Householder reflectors + * (size @f$ 2n @f$ for an @f$ n \times n@f$ matrix). + * @return @c true if the factorization was successful, otherwise @c false + */ + bool robustFactorize(double* const workspace); + + /** + * @brief Uses the factorized matrix to solve the equation @f$ Ax = y @f$ with LAPACK + * @details Before the equation can be solved, the matrix has to be factorized first by calling robustFactorize(). + * @param [in,out] rhs On entry pointer to the right hand side vector @f$ y @f$ of the equation, on exit the + * solution @f$ x @f$ + * @param [in,out] workspace Workspace used by robustFactorize() (i.e., it has to contain the scalar factors of the + * Householder reflectors). For an @f$ n \times n@f$ matrix, an array of size @f$ 2n @f$ is required. Note that if + * the equation system is singular, the least squares solution @f$ x @f$ satisfying @f$ \text{min}_x \lVert Ax - y + * \rVert @f$ is returned. + * @return @c true if the solution process was successful, otherwise @c false + */ + bool robustSolve(double* rhs, double* const workspace) const; + + /** + * @brief Uses the factorized matrix to solve the equation @f$ Ax = y @f$ with LAPACK + * @details Before the equation can be solved, the matrix has to be factorized first by calling robustFactorize(). + * It is assumed that row scaling has been applied to the matrix before factorization. + * In order to solve the equation system, the right hand side has to be scaled accordingly. + * This is handled automatically by passing the required scaling factors. + * @param [in] scalingFactors Vector with scaling factor for each row + * @param [in,out] rhs On entry pointer to the right hand side vector @f$ y @f$ of the equation, on exit the + * solution @f$ x @f$ + * @param [in,out] workspace Workspace used by robustFactorize() (i.e., it has to contain the scalar factors of the + * Householder reflectors). For an @f$ n \times n@f$ matrix, an array of size @f$ 2n @f$ is required. Note that if + * the equation system is singular, the least squares solution @f$ x @f$ satisfying @f$ \text{min}_x \lVert Ax - y + * \rVert @f$ is returned. + * @return @c true if the solution process was successful, otherwise @c false + */ + bool robustSolve(double const* scalingFactors, double* rhs, double* const workspace) const; + + /** + * @brief Returns the size of the workspace required for calling robustFactorize() + * @details The workspace size is @f$ 2n @f$ for an @f$ n \times n@f$ matrix. + * @return Size of the workspace required for robustFactorize() + */ + inline int robustWorkspaceSize() const + { + return 2 * rows(); + } + + /** + * @brief Scales rows by dividing them with a given factor + * @details This corresponds to multiplying by a diagonal matrix from the left (i.e., @f$ D^{-1} A @f$). + * @param [in] scalingFactors Vector with scaling factor for each row + */ + inline void scaleRows(double const* scalingFactors) + { + scaleRows(scalingFactors, rows()); + } + + /** + * @brief Scales the first rows by dividing them with a given factor + * @details This corresponds to multiplying by a diagonal matrix from the left (i.e., @f$ D^{-1} A @f$). + * Only the first @p numRows rows are scaled. + * @param [in] scalingFactors Vector with scaling factor for each row + * @param [in] numRows The number of rows which are to be scaled (from the top) + */ + void scaleRows(double const* scalingFactors, int numRows); + + /** + * @brief Scales columns by dividing them with a given factor + * @details This corresponds to multiplying by a diagonal matrix from the right (i.e., @f$ A D^{-1} @f$). + * @param [in] scalingFactors Vector with scaling factor for each column + */ + inline void scaleColumns(double const* scalingFactors) + { + scaleColumns(scalingFactors, columns()); + } + + /** + * @brief Scales the first columns by dividing them with a given factor + * @details This corresponds to multiplying by a diagonal matrix from the right (i.e., @f$ A D^{-1} @f$). + * Only the first @p numCols columns are scaled. + * @param [in] scalingFactors Vector with scaling factor for each column + * @param [in] numCols The number of columns which are to be scaled (from the left) + */ + void scaleColumns(double const* scalingFactors, int numCols); + + /** + * @brief Computes row scaling factors such that the largest absolute value in each row is 1 + * @details This can be used to equilibrate the matrix by calling scaleRows(). + * @param [out] scalingFactors Vector in which the row scaling factors are written + */ + inline void rowScaleFactors(double* scalingFactors) const + { + rowScaleFactors(scalingFactors, rows()); + } + + /** + * @brief Computes row scaling factors for some rows such that the largest absolute value in each row is 1 + * @details This can be used to equilibrate the matrix by calling scaleRows(). + * The scaling factors are computed for the first @p numRows rows from the top. + * @param [out] scalingFactors Vector in which the row scaling factors are written + * @param [in] numRows The number of rows to be scaled + */ + void rowScaleFactors(double* scalingFactors, int numRows) const; + + /** + * @brief Computes column scaling factors such that the largest absolute value in each column is 1 + * @details This can be used to equilibrate the matrix by calling scaleColumns(). + * @param [out] scalingFactors Vector in which the column scaling factors are written + */ + inline void columnScaleFactors(double* scalingFactors) const + { + columnScaleFactors(scalingFactors, columns()); + } + + /** + * @brief Computes column scaling factors for some columns such that the largest absolute value in each column is 1 + * @details This can be used to equilibrate the matrix by calling scaleColumns(). + * The scaling factors are computed for the first @p numCols columns from the left. + * @param [out] scalingFactors Vector in which the column scaling factors are written + * @param [in] numCols The number of columns to be scaled + */ + void columnScaleFactors(double* scalingFactors, int numCols) const; + + /** + * @brief Resizes the matrix to the given size without deleting its content + * @details The matrix size is not checked and no memory is allocated or deleted. + * The user has to take care that the memory is big enough to hold all + * elements (beware of access violations). + * @param [in] rows The number of rows + * @param [in] cols The number of columns + */ + inline void shrinkOrExpandFast(int rows, int cols) + { + _cols = cols; + _rows = rows; + } + + /** + * @brief Resizes the matrix to the given size + * @details The matrix size is not checked and no memory is allocated or deleted. + * The user has to take care that the memory is big enough to hold all + * elements (beware of access violations). + * All data is lost during this operation, the selected area is zeroed. + * + * @param [in] rows The number of rows + * @param [in] cols The number of columns + */ + inline void shrinkOrExpand(int rows, int cols) + { + shrinkOrExpandFast(rows, cols); + setAll(0.0); + } + +protected: + double* _data; //!< Pointer to the array in which the matrix is stored + int _rows; //!< Number of rows + int _cols; //!< Number of columns + lapackInt_t* _pivot; //!< Pointer to an array which is used for pivoting by factorization methods + + /** + * @brief Creates an empty, unitialized matrix + * @details No memory is allocated for the matrix. Users have to call resize() first. + */ + DenseMatrixBase() CADET_NOEXCEPT : _data(nullptr), _rows(0), _cols(0), _pivot(nullptr) + { + } + + /** + * @brief Initializes the matrix with the given memory of the given size + * @param [in] data Pointer to data array of size `rows * cols` + * @param [in] pivot Pointer to pivot array of size at least `min(rows, cols)` + * @param [in] rows Number of rows + * @param [in] cols Number of columns + */ + DenseMatrixBase(double* const data, lapackInt_t* const pivot, int rows, int cols) CADET_NOEXCEPT : _data(data), + _rows(rows), + _cols(cols), + _pivot(pivot) + { + } + + /** + * @brief Copies all values from the source to the local array + * @param [in] src Data to be copied + */ + inline void copyValues(double const* const src) + { + std::copy(src, src + stride() * _rows, _data); + } + + /** + * @brief Copies all pivots from the source to the local array + * @param [in] src Data to be copied + */ + inline void copyPivot(lapackInt_t const* const src) + { + std::copy(src, src + _cols, _pivot); + } +}; + +std::ostream& operator<<(std::ostream& out, const DenseMatrixBase& bm); + +} // namespace detail typedef detail::DenseMatrixBase::RowIterator DenseBandedRowIterator; @@ -925,35 +1030,38 @@ typedef detail::DenseMatrixBase::RowIterator DenseBandedRowIterator; * @brief Represents a dense matrix that is factorizable if it is square * @details LAPACK uses column-major storage, whereas this class uses row-major. * Thus, what we call a row here is actually a column for LAPACK. - * Concluding, we have to use the transposed LAPACK operations for + * Concluding, we have to use the transposed LAPACK operations for * solution and matrix-vector multiplication. The ordering is irrelevant * for the factorization. - * + * * Because of the transposition induced by the differing ordering, - * the number of columns and rows switches (e.g., columns are transposed + * the number of columns and rows switches (e.g., columns are transposed * rows). The ordering of the elements inside one (original) row is maintained * (i.e., the first element in a row becomes the first element in a column * and the last element in a row transposes to the last element in a column). - * + * * LAPACK needs additional pivoting arrays for factorization, which are * also stored in this class. */ class DenseMatrix : public detail::DenseMatrixBase { public: - /** * @brief Creates an empty, unitialized matrix * @details No memory is allocated for the matrix. Users have to call resize() first. */ - DenseMatrix() CADET_NOEXCEPT { } + DenseMatrix() CADET_NOEXCEPT + { + } ~DenseMatrix() CADET_NOEXCEPT { delete[] _pivot; delete[] _data; } - DenseMatrix(const DenseMatrix& cpy) : DenseMatrixBase(new double[cpy.stride() * cpy._rows], new lapackInt_t[std::min(cpy._rows, cpy._cols)], cpy._rows, cpy._cols) + DenseMatrix(const DenseMatrix& cpy) + : DenseMatrixBase(new double[cpy.stride() * cpy._rows], new lapackInt_t[std::min(cpy._rows, cpy._cols)], + cpy._rows, cpy._cols) { copyValues(cpy._data); copyPivot(cpy._pivot); @@ -1015,7 +1123,7 @@ class DenseMatrix : public detail::DenseMatrixBase * @details It is assumed that the matrix has enough memory to hold the given copy @p cpy. * All current data is lost in this operation. * The matrix is resized to match the size of @p cpy. - * + * * @param cpy Matrix to be copied */ inline void copyFrom(const DenseMatrixBase& cpy) @@ -1035,7 +1143,7 @@ class DenseMatrix : public detail::DenseMatrixBase /** * @brief Resizes the matrix to the given size * @details All data is lost in this operation. - * + * * @param [in] rows The number of rows * @param [in] cols The number of columns */ @@ -1054,17 +1162,16 @@ class DenseMatrix : public detail::DenseMatrixBase } }; - /** * @brief An interface to a general rectangular dense matrix that is factorizable (if square) with provided memory. * @details LAPACK uses column-major storage, whereas this class uses row-major. * Thus, what we call a row here is actually a column for LAPACK. - * Concluding, we have to use the transposed LAPACK operations for + * Concluding, we have to use the transposed LAPACK operations for * solution and matrix-vector multiplication. The ordering is irrelevant * for the factorization. - * + * * Because of the transposition induced by the differing ordering, - * the number of columns and rows switches (e.g., columns are transposed + * the number of columns and rows switches (e.g., columns are transposed * rows). The ordering of the elements inside one (original) row is maintained * (i.e., the first element in a row becomes the first element in a column * and the last element in a row transposes to the last element in a column). @@ -1072,7 +1179,6 @@ class DenseMatrix : public detail::DenseMatrixBase class DenseMatrixView : public detail::DenseMatrixBase { public: - /** * @brief Initializes the view with the given memory of the given size * @param [in] data Pointer to data array of size `rows * cols` @@ -1080,7 +1186,10 @@ class DenseMatrixView : public detail::DenseMatrixBase * @param [in] rows Number of rows * @param [in] cols Number of columns */ - DenseMatrixView(double* const data, lapackInt_t* const pivot, int rows, int cols) CADET_NOEXCEPT : DenseMatrixBase(data, pivot, rows, cols) { } + DenseMatrixView(double* const data, lapackInt_t* const pivot, int rows, int cols) CADET_NOEXCEPT + : DenseMatrixBase(data, pivot, rows, cols) + { + } /** * @brief Initializes the view as a submatrix of the given DenseMatrix @@ -1088,14 +1197,20 @@ class DenseMatrixView : public detail::DenseMatrixBase * @param [in] startRow Index of the first row of this submatrix * @param [in] rows Number of rows */ - DenseMatrixView(detail::DenseMatrixBase& mat, int startRow, int rows) CADET_NOEXCEPT : DenseMatrixBase(mat.rowPtr(startRow), mat.pivotData(), rows, mat.columns()) + DenseMatrixView(detail::DenseMatrixBase& mat, int startRow, int rows) CADET_NOEXCEPT + : DenseMatrixBase(mat.rowPtr(startRow), mat.pivotData(), rows, mat.columns()) { cadet_assert(mat.rows() >= startRow + rows); } - ~DenseMatrixView() CADET_NOEXCEPT { } + ~DenseMatrixView() CADET_NOEXCEPT + { + } - DenseMatrixView(const DenseMatrixView& cpy) CADET_NOEXCEPT : DenseMatrixBase(cpy._data, cpy._pivot, cpy._rows, cpy._cols) { } + DenseMatrixView(const DenseMatrixView& cpy) CADET_NOEXCEPT + : DenseMatrixBase(cpy._data, cpy._pivot, cpy._rows, cpy._cols) + { + } DenseMatrixView(DenseMatrixView&& cpy) CADET_NOEXCEPT : DenseMatrixBase(cpy._data, cpy._pivot, cpy._rows, cpy._cols) { @@ -1151,7 +1266,7 @@ class DenseMatrixView : public detail::DenseMatrixBase * @details The matrix size is not checked. The user has to take care that the memory * is big enough to hold all elements (beware of access violations). * All data is lost during this operation. - * + * * @param [in] rows The number of rows * @param [in] cols The number of columns */ @@ -1166,4 +1281,4 @@ class DenseMatrixView : public detail::DenseMatrixBase } // namespace cadet -#endif // LIBCADET_DENSEMATRIX_HPP_ +#endif // LIBCADET_DENSEMATRIX_HPP_ diff --git a/src/libcadet/linalg/EigenSolverWrapper.cpp b/src/libcadet/linalg/EigenSolverWrapper.cpp index a29c00d34..cc78d16aa 100644 --- a/src/libcadet/linalg/EigenSolverWrapper.cpp +++ b/src/libcadet/linalg/EigenSolverWrapper.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -20,65 +20,66 @@ namespace cadet namespace linalg { - cadet::linalg::EigenSolverBase* setLinearSolver(const std::string solverName) - { - if (solverName.find("SparseLU", 0) == 0) - { - if (solverName.find("COLAMDOrdering", 0) != std::string::npos) - return new cadet::linalg::SparseLU>(); - - else if (solverName.find("AMD", 0) != std::string::npos) // requires symmetrical matrix - return new cadet::linalg::SparseLU>(); - - else if (solverName.find("NaturalOrdering", 0) != std::string::npos) - return new cadet::linalg::SparseLU>(); - - else - return new cadet::linalg::SparseLU>(); - } - else if (solverName.find("SparseQR", 0) == 0) - { - if (solverName.find("COLAMDOrdering", 0) != std::string::npos) - return new cadet::linalg::SparseQR>(); - - else if (solverName.find("AMD", 0) != std::string::npos) // requires symmetrical matrix - return new cadet::linalg::SparseQR>(); - - else if (solverName.find("NaturalOrdering", 0) != std::string::npos) - return new cadet::linalg::SparseQR>(); - - else - return new cadet::linalg::SparseQR>(); - } - else if (solverName.find("BiCGSTAB", 0) == 0) - { - if (solverName.find("IdentityPreconditioner", 0) != std::string::npos) - return new cadet::linalg::BiCGSTAB(); - - else if (solverName.find("DiagonalPreconditioner", 0) != std::string::npos) - return new cadet::linalg::BiCGSTAB>(); - - else if (solverName.find("IncompleteLUT", 0) != std::string::npos) - return new cadet::linalg::BiCGSTAB>(); - - else - return new cadet::linalg::BiCGSTAB>(); - } - else if (solverName.find("LeastSquaresConjugateGradient", 0) == 0) - { - if (solverName.find("IdentityPreconditioner", 0) != std::string::npos) - return new cadet::linalg::LeastSquaresConjugateGradient(); - - else if (solverName.find("LeastSquareDiagonalPreconditioner", 0) != std::string::npos) - return new cadet::linalg::LeastSquaresConjugateGradient>(); - - else - return new cadet::linalg::LeastSquaresConjugateGradient>(); - } - else { - throw std::invalid_argument("Unknown linear solver name: " + solverName); - } - } - +cadet::linalg::EigenSolverBase* setLinearSolver(const std::string solverName) +{ + if (solverName.find("SparseLU", 0) == 0) + { + if (solverName.find("COLAMDOrdering", 0) != std::string::npos) + return new cadet::linalg::SparseLU>(); + + else if (solverName.find("AMD", 0) != std::string::npos) // requires symmetrical matrix + return new cadet::linalg::SparseLU>(); + + else if (solverName.find("NaturalOrdering", 0) != std::string::npos) + return new cadet::linalg::SparseLU>(); + + else + return new cadet::linalg::SparseLU>(); + } + else if (solverName.find("SparseQR", 0) == 0) + { + if (solverName.find("COLAMDOrdering", 0) != std::string::npos) + return new cadet::linalg::SparseQR>(); + + else if (solverName.find("AMD", 0) != std::string::npos) // requires symmetrical matrix + return new cadet::linalg::SparseQR>(); + + else if (solverName.find("NaturalOrdering", 0) != std::string::npos) + return new cadet::linalg::SparseQR>(); + + else + return new cadet::linalg::SparseQR>(); + } + else if (solverName.find("BiCGSTAB", 0) == 0) + { + if (solverName.find("IdentityPreconditioner", 0) != std::string::npos) + return new cadet::linalg::BiCGSTAB(); + + else if (solverName.find("DiagonalPreconditioner", 0) != std::string::npos) + return new cadet::linalg::BiCGSTAB>(); + + else if (solverName.find("IncompleteLUT", 0) != std::string::npos) + return new cadet::linalg::BiCGSTAB>(); + + else + return new cadet::linalg::BiCGSTAB>(); + } + else if (solverName.find("LeastSquaresConjugateGradient", 0) == 0) + { + if (solverName.find("IdentityPreconditioner", 0) != std::string::npos) + return new cadet::linalg::LeastSquaresConjugateGradient(); + + else if (solverName.find("LeastSquareDiagonalPreconditioner", 0) != std::string::npos) + return new cadet::linalg::LeastSquaresConjugateGradient>(); + + else + return new cadet::linalg::LeastSquaresConjugateGradient>(); + } + else + { + throw std::invalid_argument("Unknown linear solver name: " + solverName); + } } -} \ No newline at end of file + +} // namespace linalg +} // namespace cadet diff --git a/src/libcadet/linalg/EigenSolverWrapper.hpp b/src/libcadet/linalg/EigenSolverWrapper.hpp index 789acc0a3..4d56ac3a9 100644 --- a/src/libcadet/linalg/EigenSolverWrapper.hpp +++ b/src/libcadet/linalg/EigenSolverWrapper.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -22,118 +22,135 @@ namespace cadet namespace linalg { -class EigenSolverBase { +class EigenSolverBase +{ public: - virtual ~EigenSolverBase() = default; + virtual ~EigenSolverBase() = default; - virtual void analyzePattern(const Eigen::SparseMatrix& mat) = 0; - virtual void factorize(const Eigen::SparseMatrix& mat) = 0; - virtual Eigen::VectorXd solve(const Eigen::VectorXd& b) = 0; - virtual Eigen::ComputationInfo info() const = 0; + virtual void analyzePattern(const Eigen::SparseMatrix& mat) = 0; + virtual void factorize(const Eigen::SparseMatrix& mat) = 0; + virtual Eigen::VectorXd solve(const Eigen::VectorXd& b) = 0; + virtual Eigen::ComputationInfo info() const = 0; }; -template -class SparseLU : public EigenSolverBase { +template class SparseLU : public EigenSolverBase +{ public: - void analyzePattern(const Eigen::SparseMatrix& mat) override { - solver.analyzePattern(mat); - } - - void factorize(const Eigen::SparseMatrix& mat) override { - solver.factorize(mat); - } - - Eigen::VectorXd solve(const Eigen::VectorXd& b) override { - return solver.solve(b); - } - - Eigen::ComputationInfo info() const override { - return solver.info(); - } + void analyzePattern(const Eigen::SparseMatrix& mat) override + { + solver.analyzePattern(mat); + } + + void factorize(const Eigen::SparseMatrix& mat) override + { + solver.factorize(mat); + } + + Eigen::VectorXd solve(const Eigen::VectorXd& b) override + { + return solver.solve(b); + } + + Eigen::ComputationInfo info() const override + { + return solver.info(); + } private: - Eigen::SparseLU, OrderingType> solver; + Eigen::SparseLU, OrderingType> solver; }; -template -class SparseQR : public EigenSolverBase { +template class SparseQR : public EigenSolverBase +{ public: - void analyzePattern(const Eigen::SparseMatrix& mat) override { - //solver.analyzePattern(mat); - } - - void factorize(const Eigen::SparseMatrix& mat) override { - solver.factorize(mat); - } - - Eigen::VectorXd solve(const Eigen::VectorXd& b) override { - return solver.solve(b); - } - - Eigen::ComputationInfo info() const override { - return solver.info(); - } + void analyzePattern(const Eigen::SparseMatrix& mat) override + { + // solver.analyzePattern(mat); + } + + void factorize(const Eigen::SparseMatrix& mat) override + { + solver.factorize(mat); + } + + Eigen::VectorXd solve(const Eigen::VectorXd& b) override + { + return solver.solve(b); + } + + Eigen::ComputationInfo info() const override + { + return solver.info(); + } private: - Eigen::SparseQR, OrderingType> solver; + Eigen::SparseQR, OrderingType> solver; }; -template -class BiCGSTAB : public EigenSolverBase { +template class BiCGSTAB : public EigenSolverBase +{ public: - void analyzePattern(const Eigen::SparseMatrix& mat) override { - solver.analyzePattern(mat); - } + void analyzePattern(const Eigen::SparseMatrix& mat) override + { + solver.analyzePattern(mat); + } - void factorize(const Eigen::SparseMatrix& mat) override { + void factorize(const Eigen::SparseMatrix& mat) override + { - solver.compute(mat); + solver.compute(mat); - if (solver.info() != Eigen::Success) - { - throw std::runtime_error("BiCGSTAB decomposition failed"); - } - } + if (solver.info() != Eigen::Success) + { + throw std::runtime_error("BiCGSTAB decomposition failed"); + } + } - Eigen::VectorXd solve(const Eigen::VectorXd& b) override { - return solver.solve(b); - } + Eigen::VectorXd solve(const Eigen::VectorXd& b) override + { + return solver.solve(b); + } - Eigen::ComputationInfo info() const override { - return solver.info(); - } + Eigen::ComputationInfo info() const override + { + return solver.info(); + } private: - Eigen::BiCGSTAB, PreConditioner> solver; + Eigen::BiCGSTAB, PreConditioner> solver; }; -template -class LeastSquaresConjugateGradient : public EigenSolverBase { +template class LeastSquaresConjugateGradient : public EigenSolverBase +{ public: - void analyzePattern(const Eigen::SparseMatrix& mat) override { - //solver.analyzePattern(mat); - } + void analyzePattern(const Eigen::SparseMatrix& mat) override + { + // solver.analyzePattern(mat); + } - void factorize(const Eigen::SparseMatrix& mat) override { + void factorize(const Eigen::SparseMatrix& mat) override + { - solver.compute(mat); + solver.compute(mat); - if (solver.info() != Eigen::Success) - { - throw std::runtime_error("LeastSquaresConjugateGradient decomposition failed"); - } - } + if (solver.info() != Eigen::Success) + { + throw std::runtime_error("LeastSquaresConjugateGradient decomposition failed"); + } + } - Eigen::VectorXd solve(const Eigen::VectorXd& b) override { - return solver.solve(b); - } + Eigen::VectorXd solve(const Eigen::VectorXd& b) override + { + return solver.solve(b); + } - Eigen::ComputationInfo info() const override { - return solver.info(); - } + Eigen::ComputationInfo info() const override + { + return solver.info(); + } private: - Eigen::LeastSquaresConjugateGradient, PreConditioner> solver; + Eigen::LeastSquaresConjugateGradient, PreConditioner> solver; }; cadet::linalg::EigenSolverBase* setLinearSolver(const std::string solverName); @@ -142,4 +159,4 @@ cadet::linalg::EigenSolverBase* setLinearSolver(const std::string solverName); } // namespace cadet -#endif // LIBCADET_EigenSolverWrapper_HPP_ \ No newline at end of file +#endif // LIBCADET_EigenSolverWrapper_HPP_ diff --git a/src/libcadet/linalg/Gmres.cpp b/src/libcadet/linalg/Gmres.cpp index 38bed6bf5..016119da1 100644 --- a/src/libcadet/linalg/Gmres.cpp +++ b/src/libcadet/linalg/Gmres.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -14,9 +14,9 @@ #include "linalg/Gmres.hpp" #if CADET_SUNDIALS_IFACE == 2 - #include +#include #elif CADET_SUNDIALS_IFACE == 3 - #include +#include #endif #include "SundialsVector.hpp" @@ -48,7 +48,11 @@ Gmres::Gmres() CADET_NOEXCEPT : #elif CADET_SUNDIALS_IFACE == 3 _linearSolver(nullptr), #endif - _ortho(Orthogonalization::ModifiedGramSchmidt), _maxRestarts(0), _matrixSize(0), _matVecMul(nullptr), _userData(nullptr) + _ortho(Orthogonalization::ModifiedGramSchmidt), + _maxRestarts(0), + _matrixSize(0), + _matVecMul(nullptr), + _userData(nullptr) { #ifdef CADET_BENCHMARK_MODE _numIter = 0; @@ -116,10 +120,8 @@ int Gmres::solve(double tolerance, double const* weight, double const* rhs, doub int nIter = 0; int nPrecondSolve = 0; double resNorm = -1.0; - const int flag = SpgmrSolve(_mem, this, NV_sol, NV_rhs, - PREC_NONE, gsType, tolerance, _maxRestarts, NULL, - NV_weight, NV_weight, &gmresCallback, NULL, - &resNorm, &nIter, &nPrecondSolve); + const int flag = SpgmrSolve(_mem, this, NV_sol, NV_rhs, PREC_NONE, gsType, tolerance, _maxRestarts, NULL, NV_weight, + NV_weight, &gmresCallback, NULL, &resNorm, &nIter, &nPrecondSolve); #elif CADET_SUNDIALS_IFACE == 3 SUNSPGMRSetGSType(_linearSolver, gsType); SUNSPGMRSetMaxRestarts(_linearSolver, _maxRestarts); @@ -142,60 +144,93 @@ int Gmres::solve(double tolerance, double const* weight, double const* rhs, doub } #if CADET_SUNDIALS_IFACE == 2 - const char* Gmres::getReturnFlagName(int flag) const CADET_NOEXCEPT +const char* Gmres::getReturnFlagName(int flag) const CADET_NOEXCEPT +{ + switch (flag) { - switch (flag) - { - case 0: return "SPGMR_SUCCESS"; // Converged - case 1: return "SPGMR_RES_REDUCED"; // Did not converge, but reduced - // norm of residual - - case 2: return "SPGMR_CONV_FAIL"; // Failed to converge - case 3: return "SPGMR_QRFACT_FAIL"; // QRfact found singular matrix - case 4: return "SPGMR_PSOLVE_FAIL_REC"; // psolve failed recoverably - case 5: return "SPGMR_ATIMES_FAIL_REC"; // atimes failed recoverably - case 6: return "SPGMR_PSET_FAIL_REC"; // pset faild recoverably - - case -1: return "SPGMR_MEM_NULL"; // mem argument is NULL - case -2: return "SPGMR_ATIMES_FAIL_UNREC"; // atimes returned failure flag - case -3: return "SPGMR_PSOLVE_FAIL_UNREC"; // psolve failed unrecoverably - case -4: return "SPGMR_GS_FAIL"; // Gram-Schmidt routine faiuled - case -5: return "SPGMR_QRSOL_FAIL"; // QRsol found singular R - case -6: return "SPGMR_PSET_FAIL_UNREC"; // pset failed unrecoverably - default: return "NO_VALID_FLAG"; - } + case 0: + return "SPGMR_SUCCESS"; // Converged + case 1: + return "SPGMR_RES_REDUCED"; // Did not converge, but reduced + // norm of residual + + case 2: + return "SPGMR_CONV_FAIL"; // Failed to converge + case 3: + return "SPGMR_QRFACT_FAIL"; // QRfact found singular matrix + case 4: + return "SPGMR_PSOLVE_FAIL_REC"; // psolve failed recoverably + case 5: + return "SPGMR_ATIMES_FAIL_REC"; // atimes failed recoverably + case 6: + return "SPGMR_PSET_FAIL_REC"; // pset faild recoverably + + case -1: + return "SPGMR_MEM_NULL"; // mem argument is NULL + case -2: + return "SPGMR_ATIMES_FAIL_UNREC"; // atimes returned failure flag + case -3: + return "SPGMR_PSOLVE_FAIL_UNREC"; // psolve failed unrecoverably + case -4: + return "SPGMR_GS_FAIL"; // Gram-Schmidt routine faiuled + case -5: + return "SPGMR_QRSOL_FAIL"; // QRsol found singular R + case -6: + return "SPGMR_PSET_FAIL_UNREC"; // pset failed unrecoverably + default: + return "NO_VALID_FLAG"; } +} #elif CADET_SUNDIALS_IFACE == 3 - const char* Gmres::getReturnFlagName(int flag) const CADET_NOEXCEPT +const char* Gmres::getReturnFlagName(int flag) const CADET_NOEXCEPT +{ + switch (flag) { - switch (flag) - { - case 0: return "SUNLS_SUCCESS"; // successful/converged - - case -1: return "SUNLS_MEM_NULL"; // mem argument is NULL - case -2: return "SUNLS_ILL_INPUT"; // illegal function input - case -3: return "SUNLS_MEM_FAIL"; // failed memory access - case -4: return "SUNLS_ATIMES_FAIL_UNREC"; // atimes unrecoverable failure - case -5: return "SUNLS_PSET_FAIL_UNREC"; // pset unrecoverable failure - case -6: return "SUNLS_PSOLVE_FAIL_UNREC"; // psolve unrecoverable failure - case -7: return "SUNLS_PACKAGE_FAIL_UNREC"; // external package unrec. fail - case -8: return "SUNLS_GS_FAIL"; // Gram-Schmidt failure - case -9: return "SUNLS_QRSOL_FAIL"; // QRsol found singular R - - case 1: return "SUNLS_RES_REDUCED"; // nonconv. solve, resid reduced - case 2: return "SUNLS_CONV_FAIL"; // nonconvergent solve - case 3: return "SUNLS_ATIMES_FAIL_REC"; // atimes failed recoverably - case 4: return "SUNLS_PSET_FAIL_REC"; // pset failed recoverably - case 5: return "SUNLS_PSOLVE_FAIL_REC"; // psolve failed recoverably - case 6: return "SUNLS_PACKAGE_FAIL_REC"; // external package recov. fail - case 7: return "SUNLS_QRFACT_FAIL"; // QRfact found singular matrix - case 8: return "SUNLS_LUFACT_FAIL"; // LUfact found singular matrix - - default: return "NO_VALID_FLAG"; - } + case 0: + return "SUNLS_SUCCESS"; // successful/converged + + case -1: + return "SUNLS_MEM_NULL"; // mem argument is NULL + case -2: + return "SUNLS_ILL_INPUT"; // illegal function input + case -3: + return "SUNLS_MEM_FAIL"; // failed memory access + case -4: + return "SUNLS_ATIMES_FAIL_UNREC"; // atimes unrecoverable failure + case -5: + return "SUNLS_PSET_FAIL_UNREC"; // pset unrecoverable failure + case -6: + return "SUNLS_PSOLVE_FAIL_UNREC"; // psolve unrecoverable failure + case -7: + return "SUNLS_PACKAGE_FAIL_UNREC"; // external package unrec. fail + case -8: + return "SUNLS_GS_FAIL"; // Gram-Schmidt failure + case -9: + return "SUNLS_QRSOL_FAIL"; // QRsol found singular R + + case 1: + return "SUNLS_RES_REDUCED"; // nonconv. solve, resid reduced + case 2: + return "SUNLS_CONV_FAIL"; // nonconvergent solve + case 3: + return "SUNLS_ATIMES_FAIL_REC"; // atimes failed recoverably + case 4: + return "SUNLS_PSET_FAIL_REC"; // pset failed recoverably + case 5: + return "SUNLS_PSOLVE_FAIL_REC"; // psolve failed recoverably + case 6: + return "SUNLS_PACKAGE_FAIL_REC"; // external package recov. fail + case 7: + return "SUNLS_QRFACT_FAIL"; // QRfact found singular matrix + case 8: + return "SUNLS_LUFACT_FAIL"; // LUfact found singular matrix + + default: + return "NO_VALID_FLAG"; } +} #endif -} // namespace linalg +} // namespace linalg -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/linalg/Gmres.hpp b/src/libcadet/linalg/Gmres.hpp index 175319645..dcc6e486d 100644 --- a/src/libcadet/linalg/Gmres.hpp +++ b/src/libcadet/linalg/Gmres.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides a GMRES (Generalized Minimal Residual) algorithm wrapper */ @@ -25,13 +25,12 @@ // Forward declare SUNDIALS types #if CADET_SUNDIALS_IFACE == 2 - typedef struct _SpgmrMemRec SpgmrMemRec; +typedef struct _SpgmrMemRec SpgmrMemRec; #elif CADET_SUNDIALS_IFACE == 3 - typedef struct _generic_SUNLinearSolver *SUNLinearSolver; +typedef struct _generic_SUNLinearSolver* SUNLinearSolver; #endif -typedef struct _generic_N_Vector *N_Vector; - +typedef struct _generic_N_Vector* N_Vector; namespace cadet { @@ -45,7 +44,7 @@ namespace linalg enum class Orthogonalization : unsigned int { ClassicalGramSchmidt = 0, - ModifiedGramSchmidt = 1, + ModifiedGramSchmidt = 1, }; /** @@ -55,12 +54,12 @@ enum class Orthogonalization : unsigned int */ inline Orthogonalization toOrthogonalization(unsigned int ortho) { - switch(ortho) + switch (ortho) { - case static_cast::type>(Orthogonalization::ClassicalGramSchmidt): - return Orthogonalization::ClassicalGramSchmidt; - case static_cast::type>(Orthogonalization::ModifiedGramSchmidt): - return Orthogonalization::ModifiedGramSchmidt; + case static_cast::type>(Orthogonalization::ClassicalGramSchmidt): + return Orthogonalization::ClassicalGramSchmidt; + case static_cast::type>(Orthogonalization::ModifiedGramSchmidt): + return Orthogonalization::ModifiedGramSchmidt; } throw InvalidParameterException("Unknown orthogonalization type"); } @@ -72,16 +71,15 @@ inline Orthogonalization toOrthogonalization(unsigned int ortho) class Gmres { public: - /** - * @brief Prototype of matrix-vector multiplication function provided to GMRES algorithm - * @details Performs a matrix vector multiplication @f$ z = Ax @f$. - * - * @param [in] userData User data - * @param [in] x Vector the matrix is multiplied with - * @param [out] z Result of the multiplication (memory is provided by the caller) - * @return @c 0 if successful, any other value in case of failure - */ + * @brief Prototype of matrix-vector multiplication function provided to GMRES algorithm + * @details Performs a matrix vector multiplication @f$ z = Ax @f$. + * + * @param [in] userData User data + * @param [in] x Vector the matrix is multiplied with + * @param [out] z Result of the multiplication (memory is provided by the caller) + * @return @c 0 if successful, any other value in case of failure + */ typedef std::function MatrixVectorMultFun; Gmres() CADET_NOEXCEPT; @@ -110,12 +108,12 @@ class Gmres * @details The GMRES method, begin a Krylov subspace method, only requires matrix-vector products * with the matrix @f$ A @f$. These products are provided by a user-defined function * specified in matrixVectorMultiplier(). - * + * * @param tolerance Threshold on the weighted l^2 norm of the residual which terminates the iteration * @param weight Weight vector used in the error norm * @param rhs Right hand side vector @f$ b @f$ * @param sol On entry the initial guess, on exit the solution if the method has converged - * @return @c 0 on success, a positive value on recoverable error, and a negative value on + * @return @c 0 on success, a positive value on recoverable error, and a negative value on * critical failure (use getReturnFlagName() to convert the return flag to a string) */ int solve(double tolerance, double const* weight, double const* rhs, double* sol); @@ -124,40 +122,61 @@ class Gmres * @brief Returns the orthogonalization method used by GMRES * @return Orthogonalization method used by GMRES */ - inline Orthogonalization orthoMethod() const CADET_NOEXCEPT { return _ortho; } + inline Orthogonalization orthoMethod() const CADET_NOEXCEPT + { + return _ortho; + } /** * @brief Sets the orthogonalization method used by GMRES * @param [in] om Orthogonalization method */ - inline void orthoMethod(Orthogonalization om) CADET_NOEXCEPT { _ortho = om; } + inline void orthoMethod(Orthogonalization om) CADET_NOEXCEPT + { + _ortho = om; + } /** * @brief Returns the maximum number of restarts * @return Maximum number of restarts */ - inline unsigned int maxRestarts() const CADET_NOEXCEPT { return _maxRestarts; } + inline unsigned int maxRestarts() const CADET_NOEXCEPT + { + return _maxRestarts; + } /** * @brief Sets the maximum number of restarts * @param [in] mr Maximum number of restarts */ - inline void maxRestarts(unsigned int mr) CADET_NOEXCEPT { _maxRestarts = mr; } + inline void maxRestarts(unsigned int mr) CADET_NOEXCEPT + { + _maxRestarts = mr; + } /** * @brief Returns the size of the square matrix to be solved * @return Number of rows or columns of the square matrix to be solved */ - inline unsigned int matrixSize() const CADET_NOEXCEPT { return _matrixSize; } + inline unsigned int matrixSize() const CADET_NOEXCEPT + { + return _matrixSize; + } /** * @brief Returns the matrix-vector multiplication function * @return Matrix-vector multiplication function */ - inline MatrixVectorMultFun matrixVectorMultiplier() const CADET_NOEXCEPT { return _matVecMul; } + inline MatrixVectorMultFun matrixVectorMultiplier() const CADET_NOEXCEPT + { + return _matVecMul; + } /** * @brief Sets the matrix-vector multiplication function * @param [in] mvm Matrix-vector multiplication function */ - inline void matrixVectorMultiplier(MatrixVectorMultFun mvm) CADET_NOEXCEPT { _matVecMul = mvm; } + inline void matrixVectorMultiplier(MatrixVectorMultFun mvm) CADET_NOEXCEPT + { + _matVecMul = mvm; + } /** * @brief Sets the matrix-vector multiplication function * @param [in] mvm Matrix-vector multiplication function @@ -173,12 +192,18 @@ class Gmres * @brief Returns the user data passed to the matrix-vector multiplication function * @return User data */ - inline void* userData() const CADET_NOEXCEPT { return _userData; } + inline void* userData() const CADET_NOEXCEPT + { + return _userData; + } /** * @brief Sets the user data passed to the matrix-vector multiplication function * @param [in] ud User data */ - inline void userData(void* ud) CADET_NOEXCEPT { _userData = ud; } + inline void userData(void* ud) CADET_NOEXCEPT + { + _userData = ud; + } /** * @brief Translates the return value of solve() to a human readable SUNDIALS error code @@ -192,21 +217,23 @@ class Gmres * @brief Returns the total number of iterations over all calls of solve() * @return Total number of iterations */ - inline int numIterations() const CADET_NOEXCEPT { return _numIter; } + inline int numIterations() const CADET_NOEXCEPT + { + return _numIter; + } #endif protected: - #if CADET_SUNDIALS_IFACE == 2 SpgmrMemRec* _mem; //!< SUNDIALS memory #elif CADET_SUNDIALS_IFACE == 3 SUNLinearSolver _linearSolver; //!< SUNDIALS linear solver object #endif - Orthogonalization _ortho; //!< Orthogonalization method - unsigned int _maxRestarts; //!< Maximum number of restarts - unsigned int _matrixSize; //!< Size of the square matrix + Orthogonalization _ortho; //!< Orthogonalization method + unsigned int _maxRestarts; //!< Maximum number of restarts + unsigned int _matrixSize; //!< Size of the square matrix MatrixVectorMultFun _matVecMul; //!< Matrix-vector multiplication function required for GMRES algorithm - void* _userData; //!< User data for matrix-vector multiplication function + void* _userData; //!< User data for matrix-vector multiplication function #ifdef CADET_BENCHMARK_MODE int _numIter; //!< Accumulated number of iterations @@ -218,4 +245,4 @@ class Gmres } // namespace cadet -#endif // LIBCADET_GMRES_HPP_ +#endif // LIBCADET_GMRES_HPP_ diff --git a/src/libcadet/linalg/Norms.hpp b/src/libcadet/linalg/Norms.hpp index 46ca4fb82..2dfe775d1 100644 --- a/src/libcadet/linalg/Norms.hpp +++ b/src/libcadet/linalg/Norms.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -19,162 +19,168 @@ #include "MathUtil.hpp" #include "common/CompilerSpecific.hpp" -namespace cadet +namespace cadet { namespace linalg { - /** - * @brief Computes the (discrete) @f$\ell^1@f$-norm of the given vector - * @details The (discrete) @f$\ell^1@f$-norm is given by @f$ \lVert v \rVert_{\ell^1} = \sum_{i=1}^N \abs{v_i} @f$. - * @param [in] x Pointer to vector whose norm is to be evaluated - * @param [in] size Number of elements in the vector - * @return The @f$\ell^1@f$-norm of the vector - */ - inline double l1Norm(double const* const x, int size) - { - double res = 0.0; - for (int i = 0; i < size; ++i) - res += std::abs(x[i]); - return res; - } +/** + * @brief Computes the (discrete) @f$\ell^1@f$-norm of the given vector + * @details The (discrete) @f$\ell^1@f$-norm is given by @f$ \lVert v \rVert_{\ell^1} = \sum_{i=1}^N \abs{v_i} @f$. + * @param [in] x Pointer to vector whose norm is to be evaluated + * @param [in] size Number of elements in the vector + * @return The @f$\ell^1@f$-norm of the vector + */ +inline double l1Norm(double const* const x, int size) +{ + double res = 0.0; + for (int i = 0; i < size; ++i) + res += std::abs(x[i]); + return res; +} - /** - * @brief Computes the squared (discrete) @f$\ell^2@f$-norm of the given vector - * @details The squared (discrete) @f$\ell^2@f$-norm is given by @f$ \lVert v \rVert_{\ell^2}^2 = \sum_{i=1}^N v_i^2 @f$. - * @param [in] x Pointer to vector whose norm is to be evaluated - * @param [in] size Number of elements in the vector - * @return The squared @f$\ell^2@f$-norm of the vector - */ - inline double l2NormSquared(double const* const x, int size) - { - double res = 0.0; - for (int i = 0; i < size; ++i) - res += sqr(x[i]); - return res; - } +/** + * @brief Computes the squared (discrete) @f$\ell^2@f$-norm of the given vector + * @details The squared (discrete) @f$\ell^2@f$-norm is given by @f$ \lVert v \rVert_{\ell^2}^2 = \sum_{i=1}^N v_i^2 + * @f$. + * @param [in] x Pointer to vector whose norm is to be evaluated + * @param [in] size Number of elements in the vector + * @return The squared @f$\ell^2@f$-norm of the vector + */ +inline double l2NormSquared(double const* const x, int size) +{ + double res = 0.0; + for (int i = 0; i < size; ++i) + res += sqr(x[i]); + return res; +} - /** - * @brief Computes the (discrete) @f$\ell^2@f$-norm of the given vector - * @details The (discrete) @f$\ell^2@f$-norm is given by @f$ \lVert v \rVert_{\ell^2} = \sqrt{ \sum_{i=1}^N v_i^2 } @f$. - * @param [in] x Pointer to vector whose norm is to be evaluated - * @param [in] size Number of elements in the vector - * @return The @f$\ell^2@f$-norm of the vector - */ - inline double l2Norm(double const* const x, int size) - { - return std::sqrt(l2NormSquared(x, size)); - } +/** + * @brief Computes the (discrete) @f$\ell^2@f$-norm of the given vector + * @details The (discrete) @f$\ell^2@f$-norm is given by @f$ \lVert v \rVert_{\ell^2} = \sqrt{ \sum_{i=1}^N v_i^2 } @f$. + * @param [in] x Pointer to vector whose norm is to be evaluated + * @param [in] size Number of elements in the vector + * @return The @f$\ell^2@f$-norm of the vector + */ +inline double l2Norm(double const* const x, int size) +{ + return std::sqrt(l2NormSquared(x, size)); +} - /** - * @brief Computes the (discrete) @f$\ell^\infty@f$-norm of the given vector - * @details The (discrete) @f$\ell^\infty@f$-norm is given by @f$ \lVert v \rVert_{\ell^\infty} = \max \{ \abs{v_i} : i = 1, \dots, N \} @f$. - * @param [in] x Pointer to vector whose norm is to be evaluated - * @param [in] size Number of elements in the vector - * @return The @f$\ell^\infty@f$-norm of the vector - */ - inline double linfNorm(double const* const x, int size) +/** + * @brief Computes the (discrete) @f$\ell^\infty@f$-norm of the given vector + * @details The (discrete) @f$\ell^\infty@f$-norm is given by @f$ \lVert v \rVert_{\ell^\infty} = \max \{ \abs{v_i} : i + * = 1, \dots, N \} @f$. + * @param [in] x Pointer to vector whose norm is to be evaluated + * @param [in] size Number of elements in the vector + * @return The @f$\ell^\infty@f$-norm of the vector + */ +inline double linfNorm(double const* const x, int size) +{ + double res = 0.0; + for (int i = 0; i < size; ++i) { - double res = 0.0; - for (int i = 0; i < size; ++i) - { -#ifdef CADET_DEBUG - if (cadet_unlikely(std::isnan(x[i]))) - return std::numeric_limits::quiet_NaN(); +#ifdef CADET_DEBUG + if (cadet_unlikely(std::isnan(x[i]))) + return std::numeric_limits::quiet_NaN(); #endif - res = std::max(std::abs(x[i]), res); - } - return res; + res = std::max(std::abs(x[i]), res); } + return res; +} - /** - * @brief Computes the weighted (discrete) @f$\lVert D^{-1} \cdot \rVert_{\ell^2}@f$-norm of the given vector - * @details The (discrete) weighted @f$\ell^2(w)@f$-norm is given by @f$ \lVert v \rVert_{\ell^2(w)} = \sqrt{ \sum_{i=1}^N \left(\frac{v_i}{w_i}\right)^2 } @f$, - * where @f$ w @f$ is a vector with weights and @f$ D = \operatorname{diag}(w) @f$. - * @param [in] x Pointer to vector whose norm is to be evaluated - * @param [in] weight Pointer to weight vector @f$ w @f$ - * @param [in] size Number of elements in the vector - * @return The weighted @f$\ell^2(w)@f$-norm of the vector - */ - inline double l2normWeighted(double const* const x, double const* const weight, int size) - { - double res = 0.0; - for (int i = 0; i < size; ++i) - res += sqr(x[i] / weight[i]); - return std::sqrt(res); - } - +/** + * @brief Computes the weighted (discrete) @f$\lVert D^{-1} \cdot \rVert_{\ell^2}@f$-norm of the given vector + * @details The (discrete) weighted @f$\ell^2(w)@f$-norm is given by @f$ \lVert v \rVert_{\ell^2(w)} = \sqrt{ + * \sum_{i=1}^N \left(\frac{v_i}{w_i}\right)^2 } @f$, where @f$ w @f$ is a vector with weights and @f$ D = + * \operatorname{diag}(w) @f$. + * @param [in] x Pointer to vector whose norm is to be evaluated + * @param [in] weight Pointer to weight vector @f$ w @f$ + * @param [in] size Number of elements in the vector + * @return The weighted @f$\ell^2(w)@f$-norm of the vector + */ +inline double l2normWeighted(double const* const x, double const* const weight, int size) +{ + double res = 0.0; + for (int i = 0; i < size; ++i) + res += sqr(x[i] / weight[i]); + return std::sqrt(res); +} - /** - * @brief Computes the (discrete) @f$\ell^1@f$-norm of the difference of the given vectors - * @details The (discrete) @f$\ell^1@f$-norm is given by @f$ \lVert x - y \rVert_{\ell^1} = \sum_{i=1}^N \abs{x_i - y_i} @f$. - * @param [in] x Pointer to vector @f$ x @f$ - * @param [in] y Pointer to vector @f$ y @f$ - * @param [in] size Number of elements in the vector - * @return The @f$\ell^1@f$-norm of the difference - */ - inline double l1NormDiff(double const* const x, double const* const y, int size) - { - double res = 0.0; - for (int i = 0; i < size; ++i) - res += std::abs(x[i] - y[i]); - return res; - } +/** + * @brief Computes the (discrete) @f$\ell^1@f$-norm of the difference of the given vectors + * @details The (discrete) @f$\ell^1@f$-norm is given by @f$ \lVert x - y \rVert_{\ell^1} = \sum_{i=1}^N \abs{x_i - y_i} + * @f$. + * @param [in] x Pointer to vector @f$ x @f$ + * @param [in] y Pointer to vector @f$ y @f$ + * @param [in] size Number of elements in the vector + * @return The @f$\ell^1@f$-norm of the difference + */ +inline double l1NormDiff(double const* const x, double const* const y, int size) +{ + double res = 0.0; + for (int i = 0; i < size; ++i) + res += std::abs(x[i] - y[i]); + return res; +} - /** - * @brief Computes the squared (discrete) @f$\ell^2@f$-norm of the difference of the given vectors - * @details The squared (discrete) @f$\ell^2@f$-norm is given by @f$ \lVert x - y \rVert_{\ell^2}^2 = \sum_{i=1}^N \left(x_i - y_i\right)^2 @f$. - * @param [in] x Pointer to vector @f$ x @f$ - * @param [in] y Pointer to vector @f$ y @f$ - * @param [in] size Number of elements in the vector - * @return The squared @f$\ell^2@f$-norm of the difference - */ - inline double l2NormSquaredDiff(double const* const x, double const* const y, int size) - { - double res = 0.0; - for (int i = 0; i < size; ++i) - res += sqr(x[i] - y[i]); - return res; - } +/** + * @brief Computes the squared (discrete) @f$\ell^2@f$-norm of the difference of the given vectors + * @details The squared (discrete) @f$\ell^2@f$-norm is given by @f$ \lVert x - y \rVert_{\ell^2}^2 = \sum_{i=1}^N + * \left(x_i - y_i\right)^2 @f$. + * @param [in] x Pointer to vector @f$ x @f$ + * @param [in] y Pointer to vector @f$ y @f$ + * @param [in] size Number of elements in the vector + * @return The squared @f$\ell^2@f$-norm of the difference + */ +inline double l2NormSquaredDiff(double const* const x, double const* const y, int size) +{ + double res = 0.0; + for (int i = 0; i < size; ++i) + res += sqr(x[i] - y[i]); + return res; +} - /** - * @brief Computes the (discrete) @f$\ell^2@f$-norm of the difference of the given vectors - * @details The (discrete) @f$\ell^2@f$-norm is given by @f$ \lVert x - y \rVert_{\ell^2} = \sqrt{ \sum_{i=1}^N \left(x_i - y_i\right)^2 } @f$. - * @param [in] x Pointer to vector @f$ x @f$ - * @param [in] y Pointer to vector @f$ y @f$ - * @param [in] size Number of elements in the vector - * @return The @f$\ell^2@f$-norm of the difference - */ - inline double l2NormDiff(double const* const x, double const* const y, int size) - { - return std::sqrt(l2NormSquaredDiff(x, y, size)); - } +/** + * @brief Computes the (discrete) @f$\ell^2@f$-norm of the difference of the given vectors + * @details The (discrete) @f$\ell^2@f$-norm is given by @f$ \lVert x - y \rVert_{\ell^2} = \sqrt{ \sum_{i=1}^N + * \left(x_i - y_i\right)^2 } @f$. + * @param [in] x Pointer to vector @f$ x @f$ + * @param [in] y Pointer to vector @f$ y @f$ + * @param [in] size Number of elements in the vector + * @return The @f$\ell^2@f$-norm of the difference + */ +inline double l2NormDiff(double const* const x, double const* const y, int size) +{ + return std::sqrt(l2NormSquaredDiff(x, y, size)); +} - /** - * @brief Computes the (discrete) @f$\ell^\infty@f$-norm of the difference of the given vectors - * @details The (discrete) @f$\ell^\infty@f$-norm is given by @f$ \lVert x - y \rVert_{\ell^\infty} = \max \{ \abs{x_i - y_i} : i = 1, \dots, N \} @f$. - * @param [in] x Pointer to vector @f$ x @f$ - * @param [in] y Pointer to vector @f$ y @f$ - * @param [in] size Number of elements in the vector - * @return The @f$\ell^\infty@f$-norm of the difference - */ - inline double linfNormDiff(double const* const x, double const* const y, int size) +/** + * @brief Computes the (discrete) @f$\ell^\infty@f$-norm of the difference of the given vectors + * @details The (discrete) @f$\ell^\infty@f$-norm is given by @f$ \lVert x - y \rVert_{\ell^\infty} = \max \{ \abs{x_i - + * y_i} : i = 1, \dots, N \} @f$. + * @param [in] x Pointer to vector @f$ x @f$ + * @param [in] y Pointer to vector @f$ y @f$ + * @param [in] size Number of elements in the vector + * @return The @f$\ell^\infty@f$-norm of the difference + */ +inline double linfNormDiff(double const* const x, double const* const y, int size) +{ + double res = 0.0; + for (int i = 0; i < size; ++i) { - double res = 0.0; - for (int i = 0; i < size; ++i) - { - const double diff = x[i] - y[i]; + const double diff = x[i] - y[i]; #ifdef CADET_DEBUG - if (cadet_unlikely(std::isnan(diff))) - return std::numeric_limits::quiet_NaN(); + if (cadet_unlikely(std::isnan(diff))) + return std::numeric_limits::quiet_NaN(); #endif - res = std::max(std::abs(diff), res); - } - return res; + res = std::max(std::abs(diff), res); } + return res; +} } // namespace linalg } // namespace cadet -#endif // LIBCADET_NORMS_HPP_ +#endif // LIBCADET_NORMS_HPP_ diff --git a/src/libcadet/linalg/SparseMatrix.cpp b/src/libcadet/linalg/SparseMatrix.cpp index 087e00340..1f421e43f 100644 --- a/src/libcadet/linalg/SparseMatrix.cpp +++ b/src/libcadet/linalg/SparseMatrix.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -63,7 +63,6 @@ std::ostream& operator<<(std::ostream& out, const DoubleSparseMatrix& sm) return out; } +} // namespace linalg -} // namespace linalg - -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/linalg/SparseMatrix.hpp b/src/libcadet/linalg/SparseMatrix.hpp index d37dd44ad..34ddd842c 100644 --- a/src/libcadet/linalg/SparseMatrix.hpp +++ b/src/libcadet/linalg/SparseMatrix.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines a sparse matrix */ @@ -36,28 +36,34 @@ namespace linalg * lookup is performed first. If the element is found, it is returned. Otherwise, a new element at the given * position is added. Contrary to operator(), addElement() will always add a new element and does not check if * it already exists. - * + * * This matrix format is meant as intermediate format for constructing a sparse matrix. Users are encouraged to * convert their SparseMatrix to a CompressedSparseMatrix, which requires significantly less storage. * @tparam real_t Type of the stored elements */ -template -class SparseMatrix +template class SparseMatrix { public: /** * @brief Creates an empty SparseMatrix with capacity @c 0 * @details Users have to call resize() prior to populating the matrix. */ - SparseMatrix() CADET_NOEXCEPT : _curIdx(0) { } + SparseMatrix() CADET_NOEXCEPT : _curIdx(0) + { + } /** * @brief Creates an empty SparseMatrix with the given capacity * @param [in] nnz Capacity, that is the maximum number of non-zero elements */ - SparseMatrix(unsigned int nnz) : _curIdx(0) { resize(nnz); } + SparseMatrix(unsigned int nnz) : _curIdx(0) + { + resize(nnz); + } - ~SparseMatrix() CADET_NOEXCEPT { } + ~SparseMatrix() CADET_NOEXCEPT + { + } // Default copy and assignment semantics SparseMatrix(const SparseMatrix& cpy) = default; @@ -70,7 +76,7 @@ class SparseMatrix #else SparseMatrix& operator=(SparseMatrix&& cpy) = default; #endif - + /** * @brief Copies a SparseMatrix of different type * @details The cast from @c otherReal_t to @c real_t has to be possible. @@ -103,8 +109,7 @@ class SparseMatrix * @param [in] src Source matrix to be copied * @tparam otherReal_t Element type of source matrix */ - template - inline void copyFrom(const SparseMatrix& src) + template inline void copyFrom(const SparseMatrix& src) { _rows = src.rows(); _cols = src.cols(); @@ -121,12 +126,15 @@ class SparseMatrix * @brief Resets all elements to @c 0 * @details The capacity of the SparseMatrix is not changed. */ - inline void clear() CADET_NOEXCEPT { _curIdx = 0; } + inline void clear() CADET_NOEXCEPT + { + _curIdx = 0; + } /** * @brief Resets the maximum number of non-zero elements / the capacity * @details The matrix is reset to an empty state. All previous content is lost. - * + * * @param [in] nnz Maximum number of non-zero elements */ inline void resize(unsigned int nnz) @@ -148,7 +156,10 @@ class SparseMatrix * @details Note that the capacity is not the current number of non-zero elements. * @return Maximum number of non-zero elements that can be stored in the matrix */ - inline unsigned int capacity() const CADET_NOEXCEPT { return _rows.size(); } + inline unsigned int capacity() const CADET_NOEXCEPT + { + return _rows.size(); + } /** * @brief Inserts a new element at the given position to the given value @@ -156,7 +167,7 @@ class SparseMatrix * a new element is created. As the capacity is not increased by * this method, it will fail when the capacity is exhausted and * a new element would have to be created. - * + * * @param [in] row Row index * @param [in] col Column index * @param [in] val Value of the element at the given position @@ -178,7 +189,7 @@ class SparseMatrix * a new element is created. As the capacity is not increased by * this method, it will fail when the capacity is exhausted and * a new element would have to be created. - * + * * @param [in] row Row index * @param [in] col Column index * @return Value of the element at the given position @@ -194,14 +205,14 @@ class SparseMatrix // Value not found, so add it cadet_assert(_curIdx < _rows.size()); - + _rows[_curIdx] = row; _cols[_curIdx] = col; _values[_curIdx] = 0.0; ++_curIdx; - return _values[_curIdx-1]; + return _values[_curIdx - 1]; } /** @@ -353,7 +364,8 @@ class SparseMatrix * @tparam result_t Type of the vector \f$ y \f$ */ template - inline void multiplySubtract(arg_t const* const x, result_t* const out, unsigned int startRow, unsigned int endRow) const + inline void multiplySubtract(arg_t const* const x, result_t* const out, unsigned int startRow, + unsigned int endRow) const { for (unsigned int i = 0; i < _curIdx; ++i) { @@ -369,7 +381,10 @@ class SparseMatrix * elements are used. * @return Vector with row indices */ - inline const std::vector& rows() const CADET_NOEXCEPT { return _rows; } + inline const std::vector& rows() const CADET_NOEXCEPT + { + return _rows; + } /** * @brief Returns a vector with column indices @@ -377,7 +392,10 @@ class SparseMatrix * elements are used. * @return Vector with column indices */ - inline const std::vector& cols() const CADET_NOEXCEPT { return _cols; } + inline const std::vector& cols() const CADET_NOEXCEPT + { + return _cols; + } /** * @brief Returns a vector with element values @@ -385,19 +403,25 @@ class SparseMatrix * elements are used. * @return Vector with element values */ - inline const std::vector& values() const CADET_NOEXCEPT { return _values; } + inline const std::vector& values() const CADET_NOEXCEPT + { + return _values; + } /** * @brief Returns the number of (structurally) non-zero elements in the matrix * @return Number of (structurally) non-zero elements in the matrix */ - inline unsigned int numNonZero() const CADET_NOEXCEPT { return _curIdx; } + inline unsigned int numNonZero() const CADET_NOEXCEPT + { + return _curIdx; + } private: - std::vector _rows; //!< List with row indices of elements - std::vector _cols; //!< List with column indices of elements + std::vector _rows; //!< List with row indices of elements + std::vector _cols; //!< List with column indices of elements std::vector _values; //!< List with values of elements - unsigned int _curIdx; //!< Index of the first unused element + unsigned int _curIdx; //!< Index of the first unused element }; typedef SparseMatrix DoubleSparseMatrix; @@ -408,4 +432,4 @@ std::ostream& operator<<(std::ostream& out, const DoubleSparseMatrix& sm); } // namespace cadet -#endif // LIBCADET_SPARSEMATRIX_HPP_ +#endif // LIBCADET_SPARSEMATRIX_HPP_ diff --git a/src/libcadet/linalg/SparseSolverInterface.hpp.in b/src/libcadet/linalg/SparseSolverInterface.hpp.in index 7bff48437..7fd87cdb4 100644 --- a/src/libcadet/linalg/SparseSolverInterface.hpp.in +++ b/src/libcadet/linalg/SparseSolverInterface.hpp.in @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Interface for the sparse direct solver */ diff --git a/src/libcadet/linalg/Subset.hpp b/src/libcadet/linalg/Subset.hpp index 1288a93bd..024fac8a8 100644 --- a/src/libcadet/linalg/Subset.hpp +++ b/src/libcadet/linalg/Subset.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines operations on subsets of vectors */ @@ -26,8 +26,8 @@ #include #ifdef ENABLE_DG - #include - #include +#include +#include #endif namespace cadet @@ -36,559 +36,595 @@ namespace cadet namespace linalg { - struct ConstIndexArray +struct ConstIndexArray +{ + int const* indices; + int len; + + inline int operator[](int idx) const { - int const* indices; - int len; + return indices[idx]; + } +}; - inline int operator[](int idx) const { return indices[idx]; } - }; +struct IndexArray +{ + int* indices; + int len; - struct IndexArray + inline operator ConstIndexArray() const + { + return ConstIndexArray{indices, len}; + } + inline int operator[](int idx) const + { + return indices[idx]; + } + inline int& operator[](int idx) { - int* indices; - int len; + return indices[idx]; + } +}; - inline operator ConstIndexArray() const { return ConstIndexArray{indices, len}; } - inline int operator[](int idx) const { return indices[idx]; } - inline int& operator[](int idx) { return indices[idx]; } - }; +struct ConstMaskArray +{ + int const* mask; + int len; - struct ConstMaskArray + inline int operator[](int idx) const { - int const* mask; - int len; + return mask[idx]; + } +}; - inline int operator[](int idx) const { return mask[idx]; } - }; +struct MaskArray +{ + int* mask; + int len; - struct MaskArray - { - int* mask; - int len; - - inline operator ConstMaskArray() const { return ConstMaskArray{mask, len}; } - inline int operator[](int idx) const { return mask[idx]; } - inline int& operator[](int idx) { return mask[idx]; } - }; - - /** - * @brief Converts a mask (boolean) array to an index array - * @details The index array contains the indices of the @c true items - * in the mask array. - * - * The memory of the given mask array is used for constructing the index array. - * That is, the MaskArray @p data is no longer available after the function call. - * @param [in] data Mask array - * @return Index array - */ - inline IndexArray maskToIndexArray(const MaskArray& data) + inline operator ConstMaskArray() const { - int idx = 0; - for (int i = 0; i < data.len; ++i) - { - if (data.mask[i]) - { - data.mask[idx] = i; - ++idx; - } - } - return IndexArray{data.mask, idx}; + return ConstMaskArray{mask, len}; } - - /** - * @brief Counts the number of active elements in a mask - * @param [in] mask Mask - * @return Number of active elements in mask - */ - inline int numMaskActive(const ConstMaskArray& mask) + inline int operator[](int idx) const { - return std::count_if(mask.mask, mask.mask + mask.len, [](const int i) -> bool { return i; }); + return mask[idx]; } - - /** - * @brief Counts the number of active elements in a mask up to a given index - * @param [in] mask Mask - * @param [in] len Length of the mask subset that is checked - * @return Number of active elements in mask subset - */ - inline int numMaskActive(const ConstMaskArray& mask, int len) + inline int& operator[](int idx) { - cadet_assert(len <= mask.len); - cadet_assert(len >= 0); - return std::count_if(mask.mask, mask.mask + len, [](const int i) -> bool { return i; }); + return mask[idx]; } +}; - /** - * @brief Counts the number of active elements in a subset of a mask - * @param [in] mask Mask - * @param [in] start Index of the first item in the subset of the mask - * @param [in] len Length of the mask subset - * @return Number of active elements in mask subset - */ - inline unsigned int numMaskActive(const ConstMaskArray& mask, int start, int len) +/** + * @brief Converts a mask (boolean) array to an index array + * @details The index array contains the indices of the @c true items + * in the mask array. + * + * The memory of the given mask array is used for constructing the index array. + * That is, the MaskArray @p data is no longer available after the function call. + * @param [in] data Mask array + * @return Index array + */ +inline IndexArray maskToIndexArray(const MaskArray& data) +{ + int idx = 0; + for (int i = 0; i < data.len; ++i) { - cadet_assert(start + len <= mask.len); - cadet_assert((start >= 0) && (len >= 0)); - return std::count_if(mask.mask + start, mask.mask + start + len, [](const int i) -> bool { return i; }); + if (data.mask[i]) + { + data.mask[idx] = i; + ++idx; + } } + return IndexArray{data.mask, idx}; +} + +/** + * @brief Counts the number of active elements in a mask + * @param [in] mask Mask + * @return Number of active elements in mask + */ +inline int numMaskActive(const ConstMaskArray& mask) +{ + return std::count_if(mask.mask, mask.mask + mask.len, [](const int i) -> bool { return i; }); +} + +/** + * @brief Counts the number of active elements in a mask up to a given index + * @param [in] mask Mask + * @param [in] len Length of the mask subset that is checked + * @return Number of active elements in mask subset + */ +inline int numMaskActive(const ConstMaskArray& mask, int len) +{ + cadet_assert(len <= mask.len); + cadet_assert(len >= 0); + return std::count_if(mask.mask, mask.mask + len, [](const int i) -> bool { return i; }); +} + +/** + * @brief Counts the number of active elements in a subset of a mask + * @param [in] mask Mask + * @param [in] start Index of the first item in the subset of the mask + * @param [in] len Length of the mask subset + * @return Number of active elements in mask subset + */ +inline unsigned int numMaskActive(const ConstMaskArray& mask, int start, int len) +{ + cadet_assert(start + len <= mask.len); + cadet_assert((start >= 0) && (len >= 0)); + return std::count_if(mask.mask + start, mask.mask + start + len, [](const int i) -> bool { return i; }); +} - /** - * @brief Counts the number of active partitions in a mask - * @details The mask is divided into partitions that have an irregular number of items. - * @param [in] mask Mask - * @param [in] partSize Array with number of items in each partition - * @param [in] nPart Number of partitions - * @return Number of active partitions in the mask - */ - inline unsigned int numMaskActivePartitions(const ConstMaskArray& mask, unsigned int const* const partSize, unsigned int nPart) +/** + * @brief Counts the number of active partitions in a mask + * @details The mask is divided into partitions that have an irregular number of items. + * @param [in] mask Mask + * @param [in] partSize Array with number of items in each partition + * @param [in] nPart Number of partitions + * @return Number of active partitions in the mask + */ +inline unsigned int numMaskActivePartitions(const ConstMaskArray& mask, unsigned int const* const partSize, + unsigned int nPart) +{ + unsigned int partStartIdx = 0; + unsigned int numActivePart = 0; + for (unsigned int part = 0; part < nPart; ++part) { - unsigned int partStartIdx = 0; - unsigned int numActivePart = 0; - for (unsigned int part = 0; part < nPart; ++part) + for (unsigned int i = 0; i < partSize[part]; ++i) { - for (unsigned int i = 0; i < partSize[part]; ++i) + if (mask.mask[partStartIdx + i]) { - if (mask.mask[partStartIdx + i]) - { - ++numActivePart; - break; - } + ++numActivePart; + break; } - - partStartIdx += partSize[part]; } - return numActivePart; + + partStartIdx += partSize[part]; } + return numActivePart; +} - inline void conservedMoietiesFromPartitionedMask(const ConstMaskArray& mask, unsigned int const* const partSize, unsigned int nPart, - double const* const src, double* const dest, double factorLiquid, double factorSolid) +inline void conservedMoietiesFromPartitionedMask(const ConstMaskArray& mask, unsigned int const* const partSize, + unsigned int nPart, double const* const src, double* const dest, + double factorLiquid, double factorSolid) +{ + unsigned int bndIdx = nPart; + unsigned int rIdx = 0; + for (unsigned int comp = 0; comp < nPart; ++comp) { - unsigned int bndIdx = nPart; - unsigned int rIdx = 0; - for (unsigned int comp = 0; comp < nPart; ++comp) + if (!mask.mask[comp]) { - if (!mask.mask[comp]) - { - bndIdx += partSize[comp]; - continue; - } - - dest[rIdx] = factorLiquid * src[comp]; + bndIdx += partSize[comp]; + continue; + } - for (unsigned int bnd = 0; bnd < partSize[comp]; ++bnd, ++bndIdx) - { - if (mask.mask[bndIdx]) - dest[rIdx] += factorSolid * src[bndIdx]; - } + dest[rIdx] = factorLiquid * src[comp]; - ++rIdx; + for (unsigned int bnd = 0; bnd < partSize[comp]; ++bnd, ++bndIdx) + { + if (mask.mask[bndIdx]) + dest[rIdx] += factorSolid * src[bndIdx]; } - } - /** - * @brief Selects a subset of a vector / array - * @details Copies the elements at the given indices to the beginning of the @p dest array. - * @param [in] src Source array - * @param [in] idx Index array - * @param [out] dest Destination array - */ - inline void selectVectorSubset(double const* const src, const ConstIndexArray& idx, double* const dest) - { - for (int i = 0; i < idx.len; ++i) - dest[i] = src[idx.indices[i]]; + ++rIdx; } +} - /** - * @brief Copies a vector / array to a subset of another array - * @details Copies the @p src array to the elements of the @p dest array at the given indices. - * @param [in] src Source array - * @param [in] idx Index array - * @param [out] dest Destination array - */ - inline void applyVectorSubset(double const* const src, const ConstIndexArray& idx, double* const dest) - { - for (int i = 0; i < idx.len; ++i) - dest[idx.indices[i]] = src[i]; - } +/** + * @brief Selects a subset of a vector / array + * @details Copies the elements at the given indices to the beginning of the @p dest array. + * @param [in] src Source array + * @param [in] idx Index array + * @param [out] dest Destination array + */ +inline void selectVectorSubset(double const* const src, const ConstIndexArray& idx, double* const dest) +{ + for (int i = 0; i < idx.len; ++i) + dest[i] = src[idx.indices[i]]; +} - /** - * @brief Selects a subset of a vector / array - * @details Copies the elements selected by the mask to the beginning of the @p dest array. - * @param [in] src Source array - * @param [in] mask Mask array - * @param [out] dest Destination array - */ - inline void selectVectorSubset(double const* const src, const ConstMaskArray& mask, double* const dest) +/** + * @brief Copies a vector / array to a subset of another array + * @details Copies the @p src array to the elements of the @p dest array at the given indices. + * @param [in] src Source array + * @param [in] idx Index array + * @param [out] dest Destination array + */ +inline void applyVectorSubset(double const* const src, const ConstIndexArray& idx, double* const dest) +{ + for (int i = 0; i < idx.len; ++i) + dest[idx.indices[i]] = src[i]; +} + +/** + * @brief Selects a subset of a vector / array + * @details Copies the elements selected by the mask to the beginning of the @p dest array. + * @param [in] src Source array + * @param [in] mask Mask array + * @param [out] dest Destination array + */ +inline void selectVectorSubset(double const* const src, const ConstMaskArray& mask, double* const dest) +{ + int k = 0; + for (int i = 0; i < mask.len; ++i) { - int k = 0; - for (int i = 0; i < mask.len; ++i) + if (mask.mask[i]) { - if (mask.mask[i]) - { - dest[k] = src[i]; - ++k; - } + dest[k] = src[i]; + ++k; } } +} - /** - * @brief Copies a vector / array to a subset of another array - * @details Copies the @p src array to the elements of the @p dest array selected by the mask. - * @param [in] src Source array - * @param [in] mask Mask array - * @param [out] dest Destination array - */ - inline void applyVectorSubset(double const* const src, const ConstMaskArray& mask, double* const dest) +/** + * @brief Copies a vector / array to a subset of another array + * @details Copies the @p src array to the elements of the @p dest array selected by the mask. + * @param [in] src Source array + * @param [in] mask Mask array + * @param [out] dest Destination array + */ +inline void applyVectorSubset(double const* const src, const ConstMaskArray& mask, double* const dest) +{ + int k = 0; + for (int i = 0; i < mask.len; ++i) { - int k = 0; - for (int i = 0; i < mask.len; ++i) + if (mask.mask[i]) { - if (mask.mask[i]) - { - dest[i] = src[k]; - ++k; - } + dest[i] = src[k]; + ++k; } } +} - /** - * @brief Copies a vector / array to a subset of another array - * @details Copies the @p src array to the elements of the @p dest array selected by the mask. - * @param [in] src Source array - * @param [in] mask Mask array - * @param [out] dest Destination array - */ - template - inline void applyVectorSubset(double const* const src, const ConstMaskArray& mask, T* const dest) +/** + * @brief Copies a vector / array to a subset of another array + * @details Copies the @p src array to the elements of the @p dest array selected by the mask. + * @param [in] src Source array + * @param [in] mask Mask array + * @param [out] dest Destination array + */ +template inline void applyVectorSubset(double const* const src, const ConstMaskArray& mask, T* const dest) +{ + int k = 0; + for (int i = 0; i < mask.len; ++i) { - int k = 0; - for (int i = 0; i < mask.len; ++i) + if (mask.mask[i]) { - if (mask.mask[i]) - { - dest[i].setValue(src[k]); - ++k; - } + dest[i].setValue(src[k]); + ++k; } } +} - /** - * @brief Fills the given subset of an array with the given value - * @param [in,out] data Array - * @param [in] idx Index array - * @param [in] value Value that is used to fill the array - */ - inline void fillVectorSubset(double* const data, const ConstIndexArray& idx, double value) - { - for (int i = 0; i < idx.len; ++i) - data[idx.indices[i]] = value; - } +/** + * @brief Fills the given subset of an array with the given value + * @param [in,out] data Array + * @param [in] idx Index array + * @param [in] value Value that is used to fill the array + */ +inline void fillVectorSubset(double* const data, const ConstIndexArray& idx, double value) +{ + for (int i = 0; i < idx.len; ++i) + data[idx.indices[i]] = value; +} - /** - * @brief Fills the given subset of an array with the given value - * @param [in,out] data Array - * @param [in] mask Mask array - * @param [in] value Value that is used to fill the array - */ - inline void fillVectorSubset(double* const data, const ConstMaskArray& mask, double value) +/** + * @brief Fills the given subset of an array with the given value + * @param [in,out] data Array + * @param [in] mask Mask array + * @param [in] value Value that is used to fill the array + */ +inline void fillVectorSubset(double* const data, const ConstMaskArray& mask, double value) +{ + for (int i = 0; i < mask.len; ++i) { - for (int i = 0; i < mask.len; ++i) - { - if (mask.mask[i]) - data[i] = value; - } + if (mask.mask[i]) + data[i] = value; } +} - /** - * @brief Computes @f$ y = \beta y + \alpha x_{\text{mask}} @f$ - * @param [in] x Array @f$ x @f$ - * @param [in] idx Index array - * @param [in] alpha Factor @f$ \alpha @f$ - * @param [in] beta Factor @f$ \beta @f$ - * @param [in,out] y Array @f$ y @f$ - */ - inline void vectorSubsetAdd(double const* const x, const ConstIndexArray& idx, double alpha, double beta, double* const y) - { - for (int i = 0; i < idx.len; ++i) - y[i] = beta * y[i] + alpha * x[idx.indices[i]]; - } +/** + * @brief Computes @f$ y = \beta y + \alpha x_{\text{mask}} @f$ + * @param [in] x Array @f$ x @f$ + * @param [in] idx Index array + * @param [in] alpha Factor @f$ \alpha @f$ + * @param [in] beta Factor @f$ \beta @f$ + * @param [in,out] y Array @f$ y @f$ + */ +inline void vectorSubsetAdd(double const* const x, const ConstIndexArray& idx, double alpha, double beta, + double* const y) +{ + for (int i = 0; i < idx.len; ++i) + y[i] = beta * y[i] + alpha * x[idx.indices[i]]; +} - /** - * @brief Computes @f$ y = \beta y + \alpha x_{\text{mask}} @f$ - * @param [in] x Array @f$ x @f$ - * @param [in] mask Mask array - * @param [in] alpha Factor @f$ \alpha @f$ - * @param [in] beta Factor @f$ \beta @f$ - * @param [in,out] y Array @f$ y @f$ - */ - inline void vectorSubsetAdd(double const* const x, const ConstMaskArray& mask, double alpha, double beta, double* const y) +/** + * @brief Computes @f$ y = \beta y + \alpha x_{\text{mask}} @f$ + * @param [in] x Array @f$ x @f$ + * @param [in] mask Mask array + * @param [in] alpha Factor @f$ \alpha @f$ + * @param [in] beta Factor @f$ \beta @f$ + * @param [in,out] y Array @f$ y @f$ + */ +inline void vectorSubsetAdd(double const* const x, const ConstMaskArray& mask, double alpha, double beta, + double* const y) +{ + int k = 0; + for (int i = 0; i < mask.len; ++i) { - int k = 0; - for (int i = 0; i < mask.len; ++i) + if (mask.mask[i]) { - if (mask.mask[i]) - { - y[k] = beta * y[k] + alpha * x[i]; - ++k; - } + y[k] = beta * y[k] + alpha * x[i]; + ++k; } } +} - /** - * @brief Copies a subset of a given dense matrix into this matrix - * @details Copies a submatrix indentified by row and column masks from a given source in dense - * storage into a destination matrix. The submatrix has to fully fit into the destination - * matrix and is placed in the top left corner. The rest of the matrix is left unchanged. - * @param [in] mat Source matrix in dense storage format - * @param [in] rowIdx Index array for rows - * @param [in] colIdx Index array for columns - * @param [in] dest Destination matrix in dense storage format - */ - inline void copyMatrixSubset(const detail::DenseMatrixBase& src, const ConstIndexArray& rowIdx, const ConstIndexArray& colIdx, detail::DenseMatrixBase& dest) - { - cadet_assert(dest.rows() >= rowIdx.len); - cadet_assert(dest.columns() >= colIdx.len); - cadet_assert(src.rows() > rowIdx.indices[rowIdx.len-1]); - cadet_assert(src.columns() > colIdx.indices[colIdx.len-1]); +/** + * @brief Copies a subset of a given dense matrix into this matrix + * @details Copies a submatrix indentified by row and column masks from a given source in dense + * storage into a destination matrix. The submatrix has to fully fit into the destination + * matrix and is placed in the top left corner. The rest of the matrix is left unchanged. + * @param [in] mat Source matrix in dense storage format + * @param [in] rowIdx Index array for rows + * @param [in] colIdx Index array for columns + * @param [in] dest Destination matrix in dense storage format + */ +inline void copyMatrixSubset(const detail::DenseMatrixBase& src, const ConstIndexArray& rowIdx, + const ConstIndexArray& colIdx, detail::DenseMatrixBase& dest) +{ + cadet_assert(dest.rows() >= rowIdx.len); + cadet_assert(dest.columns() >= colIdx.len); + cadet_assert(src.rows() > rowIdx.indices[rowIdx.len - 1]); + cadet_assert(src.columns() > colIdx.indices[colIdx.len - 1]); - double* ptrDest = dest.data(); - for (int r = 0; r < rowIdx.len; ++r, ptrDest += dest.stride()) - { - double const* ptrSrc = src.data() + src.stride() * rowIdx.indices[r]; - for (int c = 0; c < colIdx.len; ++c) - ptrDest[c] = ptrSrc[colIdx.indices[c]]; - } + double* ptrDest = dest.data(); + for (int r = 0; r < rowIdx.len; ++r, ptrDest += dest.stride()) + { + double const* ptrSrc = src.data() + src.stride() * rowIdx.indices[r]; + for (int c = 0; c < colIdx.len; ++c) + ptrDest[c] = ptrSrc[colIdx.indices[c]]; } +} - /** - * @brief Copies a subset of a given dense matrix into this matrix - * @details Copies a submatrix indentified by row and column masks from a given source in dense - * storage into a destination matrix. The submatrix has to fully fit into the destination - * matrix and is placed in the left corner identified by a row offset. The rest of the - * matrix is left unchanged. - * @param [in] mat Source matrix in dense storage format - * @param [in] rowIdx Index array for rows - * @param [in] colIdx Index array for columns - * @param [in] srcStartRow Offset applied to the row mask in the source matrix - * @param [in] srcStartCol Offset applied to the column mask in the source matrix - * @param [in] dest Destination matrix in dense storage format - * @param [in] destStartRow Index of the first row written in the target matrix - */ - inline void copyMatrixSubset(const detail::DenseMatrixBase& src, const ConstIndexArray& rowIdx, const ConstIndexArray& colIdx, int srcStartRow, int srcStartCol, detail::DenseMatrixBase& dest, int destStartRow) +/** + * @brief Copies a subset of a given dense matrix into this matrix + * @details Copies a submatrix indentified by row and column masks from a given source in dense + * storage into a destination matrix. The submatrix has to fully fit into the destination + * matrix and is placed in the left corner identified by a row offset. The rest of the + * matrix is left unchanged. + * @param [in] mat Source matrix in dense storage format + * @param [in] rowIdx Index array for rows + * @param [in] colIdx Index array for columns + * @param [in] srcStartRow Offset applied to the row mask in the source matrix + * @param [in] srcStartCol Offset applied to the column mask in the source matrix + * @param [in] dest Destination matrix in dense storage format + * @param [in] destStartRow Index of the first row written in the target matrix + */ +inline void copyMatrixSubset(const detail::DenseMatrixBase& src, const ConstIndexArray& rowIdx, + const ConstIndexArray& colIdx, int srcStartRow, int srcStartCol, + detail::DenseMatrixBase& dest, int destStartRow) +{ + cadet_assert(destStartRow >= 0); + cadet_assert(dest.rows() >= rowIdx.len + destStartRow); + cadet_assert(dest.columns() >= colIdx.len); + cadet_assert((srcStartRow >= 0) && (srcStartCol >= 0)); + cadet_assert(src.rows() > rowIdx.indices[rowIdx.len - 1] + srcStartRow); + cadet_assert(src.columns() > colIdx.indices[colIdx.len - 1] + srcStartCol); + + double* ptrDest = dest.data() + dest.stride() * destStartRow; + for (int r = 0; r < rowIdx.len; ++r, ptrDest += dest.stride()) { - cadet_assert(destStartRow >= 0); - cadet_assert(dest.rows() >= rowIdx.len + destStartRow); - cadet_assert(dest.columns() >= colIdx.len); - cadet_assert((srcStartRow >= 0) && (srcStartCol >= 0)); - cadet_assert(src.rows() > rowIdx.indices[rowIdx.len-1] + srcStartRow); - cadet_assert(src.columns() > colIdx.indices[colIdx.len-1] + srcStartCol); - - double* ptrDest = dest.data() + dest.stride() * destStartRow; - for (int r = 0; r < rowIdx.len; ++r, ptrDest += dest.stride()) - { - double const* ptrSrc = src.data() + src.stride() * (rowIdx.indices[r] + srcStartRow) + srcStartCol; - for (int c = 0; c < colIdx.len; ++c) - ptrDest[c] = ptrSrc[colIdx.indices[c]]; - } + double const* ptrSrc = src.data() + src.stride() * (rowIdx.indices[r] + srcStartRow) + srcStartCol; + for (int c = 0; c < colIdx.len; ++c) + ptrDest[c] = ptrSrc[colIdx.indices[c]]; } +} + +/** + * @brief Copies a subset of a given dense matrix into this matrix + * @details Copies a submatrix indentified by row and column masks from a given source in dense + * storage into a destination matrix. The submatrix has to fully fit into the destination + * matrix and is placed in the top left corner. The rest of the matrix is left unchanged. + * @param [in] mat Source matrix in dense storage format + * @param [in] rowMask Mask array for rows + * @param [in] colMask Mask array for columns + * @param [in] dest Destination matrix in dense storage format + */ +inline void copyMatrixSubset(const detail::DenseMatrixBase& src, const ConstMaskArray& rowMask, + const ConstMaskArray& colMask, detail::DenseMatrixBase& dest) +{ + cadet_assert(dest.rows() >= numMaskActive(rowMask)); + cadet_assert(dest.columns() >= numMaskActive(colMask)); + cadet_assert(src.rows() >= rowMask.len); + cadet_assert(src.columns() >= colMask.len); - /** - * @brief Copies a subset of a given dense matrix into this matrix - * @details Copies a submatrix indentified by row and column masks from a given source in dense - * storage into a destination matrix. The submatrix has to fully fit into the destination - * matrix and is placed in the top left corner. The rest of the matrix is left unchanged. - * @param [in] mat Source matrix in dense storage format - * @param [in] rowMask Mask array for rows - * @param [in] colMask Mask array for columns - * @param [in] dest Destination matrix in dense storage format - */ - inline void copyMatrixSubset(const detail::DenseMatrixBase& src, const ConstMaskArray& rowMask, const ConstMaskArray& colMask, detail::DenseMatrixBase& dest) + double* ptrDest = dest.data(); + for (int r = 0; r < rowMask.len; ++r) { - cadet_assert(dest.rows() >= numMaskActive(rowMask)); - cadet_assert(dest.columns() >= numMaskActive(colMask)); - cadet_assert(src.rows() >= rowMask.len); - cadet_assert(src.columns() >= colMask.len); + if (!rowMask.mask[r]) + continue; - double* ptrDest = dest.data(); - for (int r = 0; r < rowMask.len; ++r) + double const* const ptrSrc = src.data() + src.stride() * r; + int idx = 0; + for (int c = 0; c < colMask.len; ++c) { - if (!rowMask.mask[r]) + if (!colMask.mask[c]) continue; - double const* const ptrSrc = src.data() + src.stride() * r; - int idx = 0; - for (int c = 0; c < colMask.len; ++c) - { - if (!colMask.mask[c]) - continue; - - ptrDest[idx] = ptrSrc[c]; - ++idx; - } - - ptrDest += dest.stride(); + ptrDest[idx] = ptrSrc[c]; + ++idx; } + + ptrDest += dest.stride(); } +} - /** - * @brief Copies a subset of a given dense matrix into this matrix - * @details Copies a submatrix indentified by row and column masks from a given source in dense - * storage into a destination matrix. The submatrix has to fully fit into the destination - * matrix and is placed in the left corner identified by a row offset. The rest of the - * matrix is left unchanged. - * @param [in] mat Source matrix in dense storage format - * @param [in] rowMask Mask array for rows - * @param [in] colMask Mask array for columns - * @param [in] srcStartRow Offset applied to the row mask in the source matrix - * @param [in] srcStartCol Offset applied to the column mask in the source matrix - * @param [in] dest Destination matrix in dense storage format - * @param [in] destStartRow Index of the first row written in the target matrix - */ - inline void copyMatrixSubset(const detail::DenseMatrixBase& src, const ConstMaskArray& rowMask, const ConstMaskArray& colMask, int srcStartRow, int srcStartCol, detail::DenseMatrixBase& dest, int destStartRow) +/** + * @brief Copies a subset of a given dense matrix into this matrix + * @details Copies a submatrix indentified by row and column masks from a given source in dense + * storage into a destination matrix. The submatrix has to fully fit into the destination + * matrix and is placed in the left corner identified by a row offset. The rest of the + * matrix is left unchanged. + * @param [in] mat Source matrix in dense storage format + * @param [in] rowMask Mask array for rows + * @param [in] colMask Mask array for columns + * @param [in] srcStartRow Offset applied to the row mask in the source matrix + * @param [in] srcStartCol Offset applied to the column mask in the source matrix + * @param [in] dest Destination matrix in dense storage format + * @param [in] destStartRow Index of the first row written in the target matrix + */ +inline void copyMatrixSubset(const detail::DenseMatrixBase& src, const ConstMaskArray& rowMask, + const ConstMaskArray& colMask, int srcStartRow, int srcStartCol, + detail::DenseMatrixBase& dest, int destStartRow) +{ + cadet_assert(destStartRow >= 0); + cadet_assert(dest.rows() >= numMaskActive(rowMask) + destStartRow); + cadet_assert(dest.columns() >= numMaskActive(colMask)); + cadet_assert((srcStartRow >= 0) && (srcStartCol >= 0)); + cadet_assert(src.rows() >= rowMask.len + srcStartRow); + cadet_assert(src.columns() >= colMask.len + srcStartCol); + + double* ptrDest = dest.data() + dest.stride() * destStartRow; + for (int r = 0; r < rowMask.len; ++r) { - cadet_assert(destStartRow >= 0); - cadet_assert(dest.rows() >= numMaskActive(rowMask) + destStartRow); - cadet_assert(dest.columns() >= numMaskActive(colMask)); - cadet_assert((srcStartRow >= 0) && (srcStartCol >= 0)); - cadet_assert(src.rows() >= rowMask.len + srcStartRow); - cadet_assert(src.columns() >= colMask.len + srcStartCol); - - double* ptrDest = dest.data() + dest.stride() * destStartRow; - for (int r = 0; r < rowMask.len; ++r) + if (!rowMask.mask[r]) + continue; + + double const* const ptrSrc = src.data() + src.stride() * (r + srcStartRow) + srcStartCol; + int idx = 0; + for (int c = 0; c < colMask.len; ++c) { - if (!rowMask.mask[r]) + if (!colMask.mask[c]) continue; - double const* const ptrSrc = src.data() + src.stride() * (r + srcStartRow) + srcStartCol; - int idx = 0; - for (int c = 0; c < colMask.len; ++c) - { - if (!colMask.mask[c]) - continue; - - ptrDest[idx] = ptrSrc[c]; - ++idx; - } - - ptrDest += dest.stride(); + ptrDest[idx] = ptrSrc[c]; + ++idx; } + + ptrDest += dest.stride(); } +} - /** - * @brief Copies a subset of a given band matrix into this matrix - * @details Copies a submatrix indentified by row and column masks from a given source in banded - * storage into a destination matrix. The submatrix has to fully fit into the destination - * matrix and is placed in the top left corner. The rest of the matrix is left unchanged. - * @param [in] mat Source matrix in banded storage format - * @param [in] rowMask Mask array for rows - * @param [in] colMask Mask array for columns - * @param [in] rowOffset Offset to the first row to copy - * @param [in] diagOffset Offset to the first diagonal to copy - * @param [in] dest Destination matrix in dense storage format - */ - inline void copyMatrixSubset(const BandMatrix& src, const ConstMaskArray& rowMask, const ConstMaskArray& colMask, int rowOffset, int diagOffset, detail::DenseMatrixBase& dest) +/** + * @brief Copies a subset of a given band matrix into this matrix + * @details Copies a submatrix indentified by row and column masks from a given source in banded + * storage into a destination matrix. The submatrix has to fully fit into the destination + * matrix and is placed in the top left corner. The rest of the matrix is left unchanged. + * @param [in] mat Source matrix in banded storage format + * @param [in] rowMask Mask array for rows + * @param [in] colMask Mask array for columns + * @param [in] rowOffset Offset to the first row to copy + * @param [in] diagOffset Offset to the first diagonal to copy + * @param [in] dest Destination matrix in dense storage format + */ +inline void copyMatrixSubset(const BandMatrix& src, const ConstMaskArray& rowMask, const ConstMaskArray& colMask, + int rowOffset, int diagOffset, detail::DenseMatrixBase& dest) +{ + cadet_assert(dest.rows() >= numMaskActive(rowMask)); + cadet_assert(dest.columns() >= numMaskActive(colMask)); + cadet_assert(src.rows() >= rowMask.len); + cadet_assert(static_cast(src.upperBandwidth()) + 1 + diagOffset >= rowMask.len); + cadet_assert(-diagOffset <= static_cast(src.lowerBandwidth())); + + const int srcStride = src.stride(); + double const* ptrSrc = src.data() + srcStride * rowOffset + diagOffset + src.lowerBandwidth(); + double* ptrDest = dest.data(); + for (int r = 0; r < rowMask.len; ++r, ptrSrc += srcStride - 1) { - cadet_assert(dest.rows() >= numMaskActive(rowMask)); - cadet_assert(dest.columns() >= numMaskActive(colMask)); - cadet_assert(src.rows() >= rowMask.len); - cadet_assert(static_cast(src.upperBandwidth()) + 1 + diagOffset >= rowMask.len); - cadet_assert(-diagOffset <= static_cast(src.lowerBandwidth())); - - const int srcStride = src.stride(); - double const* ptrSrc = src.data() + srcStride * rowOffset + diagOffset + src.lowerBandwidth(); - double* ptrDest = dest.data(); - for (int r = 0; r < rowMask.len; ++r, ptrSrc += srcStride - 1) + if (!rowMask.mask[r]) + continue; + + int idx = 0; + for (int c = 0; c < colMask.len; ++c) { - if (!rowMask.mask[r]) + if (!colMask.mask[c]) continue; - int idx = 0; - for (int c = 0; c < colMask.len; ++c) - { - if (!colMask.mask[c]) - continue; - - ptrDest[idx] = ptrSrc[c]; - ++idx; - } - - ptrDest += dest.stride(); + ptrDest[idx] = ptrSrc[c]; + ++idx; } + + ptrDest += dest.stride(); } +} #ifdef ENABLE_DG - /** - * @brief Copies a subset of a given Eigen matrix into this matrix - * @details Copies a submatrix indentified by row and column masks from a given source in banded - * storage into a destination matrix. The submatrix has to fully fit into the destination - * matrix and is placed in the top left corner. The rest of the matrix is left unchanged. - * @param [in] mat Source matrix in banded storage format - * @param [in] rowMask Mask array for rows - * @param [in] colMask Mask array for columns - * @param [in] rowOffset Offset to the first row to copy - * @param [in] diagOffset Offset to the first diagonal to copy - * @param [in] dest Destination matrix in dense storage format - */ - inline void copyMatrixSubset(const Eigen::MatrixXd& src, const ConstMaskArray& rowMask, const ConstMaskArray& colMask, int rowOffset, int colOffset, detail::DenseMatrixBase& dest) +/** + * @brief Copies a subset of a given Eigen matrix into this matrix + * @details Copies a submatrix indentified by row and column masks from a given source in banded + * storage into a destination matrix. The submatrix has to fully fit into the destination + * matrix and is placed in the top left corner. The rest of the matrix is left unchanged. + * @param [in] mat Source matrix in banded storage format + * @param [in] rowMask Mask array for rows + * @param [in] colMask Mask array for columns + * @param [in] rowOffset Offset to the first row to copy + * @param [in] diagOffset Offset to the first diagonal to copy + * @param [in] dest Destination matrix in dense storage format + */ +inline void copyMatrixSubset(const Eigen::MatrixXd& src, const ConstMaskArray& rowMask, const ConstMaskArray& colMask, + int rowOffset, int colOffset, detail::DenseMatrixBase& dest) +{ + cadet_assert(dest.rows() >= numMaskActive(rowMask)); + cadet_assert(dest.columns() >= numMaskActive(colMask)); + cadet_assert(src.rows() >= rowMask.len); + cadet_assert(src.cols() >= colMask.len); + + double const* ptrSrc = src.data() + rowOffset * src.cols() + colOffset; + double* ptrDest = dest.data(); + for (int r = 0; r < rowMask.len; ++r, ptrSrc += src.cols()) { - cadet_assert(dest.rows() >= numMaskActive(rowMask)); - cadet_assert(dest.columns() >= numMaskActive(colMask)); - cadet_assert(src.rows() >= rowMask.len); - cadet_assert(src.cols() >= colMask.len); - - double const* ptrSrc = src.data() + rowOffset * src.cols() + colOffset; - double* ptrDest = dest.data(); - for (int r = 0; r < rowMask.len; ++r, ptrSrc+=src.cols()) + if (!rowMask.mask[r]) + continue; + + int idx = 0; + for (int c = 0; c < colMask.len; ++c) { - if (!rowMask.mask[r]) + if (!colMask.mask[c]) continue; - int idx = 0; - for (int c = 0; c < colMask.len; ++c) - { - if (!colMask.mask[c]) - continue; - - ptrDest[idx] = ptrSrc[c]; - ++idx; - } - - ptrDest += dest.stride(); + ptrDest[idx] = ptrSrc[c]; + ++idx; } + + ptrDest += dest.stride(); } - inline void copyMatrixSubset(const Eigen::SparseMatrix& src, const ConstMaskArray& rowMask, const ConstMaskArray& colMask, int rowOffset, int colOffset, detail::DenseMatrixBase& dest) - { - cadet_assert(dest.rows() >= numMaskActive(rowMask)); - cadet_assert(dest.columns() >= numMaskActive(colMask)); - cadet_assert(src.rows() >= rowMask.len); - cadet_assert(src.cols() >= colMask.len); +} +inline void copyMatrixSubset(const Eigen::SparseMatrix& src, const ConstMaskArray& rowMask, + const ConstMaskArray& colMask, int rowOffset, int colOffset, detail::DenseMatrixBase& dest) +{ + cadet_assert(dest.rows() >= numMaskActive(rowMask)); + cadet_assert(dest.columns() >= numMaskActive(colMask)); + cadet_assert(src.rows() >= rowMask.len); + cadet_assert(src.cols() >= colMask.len); + + double* ptrDest = dest.data(); - double* ptrDest = dest.data(); + for (int r = 0; r < rowMask.len; ++r) + { + if (!rowMask.mask[r]) + continue; - for (int r = 0; r < rowMask.len; ++r) + int idx = 0; + for (int c = 0; c < colMask.len; ++c) { - if (!rowMask.mask[r]) + if (!colMask.mask[c]) continue; - int idx = 0; - for (int c = 0; c < colMask.len; ++c) - { - if (!colMask.mask[c]) - continue; + ptrDest[idx] = src.coeff(rowOffset + r, colOffset + c); - ptrDest[idx] = src.coeff(rowOffset + r, colOffset + c); - - ++idx; - } - - ptrDest += dest.stride(); + ++idx; } + + ptrDest += dest.stride(); } +} #endif } // namespace linalg } // namespace cadet -#endif // LIBCADET_SUBSET_HPP_ +#endif // LIBCADET_SUBSET_HPP_ diff --git a/src/libcadet/linalg/SuperLUSparseMatrix.cpp b/src/libcadet/linalg/SuperLUSparseMatrix.cpp index ed2c347df..faef78eb0 100644 --- a/src/libcadet/linalg/SuperLUSparseMatrix.cpp +++ b/src/libcadet/linalg/SuperLUSparseMatrix.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -21,17 +21,22 @@ namespace cadet namespace linalg { -SuperLUSparseMatrix::SuperLUSparseMatrix() : CompressedSparseMatrix(), _permMat(nullptr), _firstFactorization(true), _permCols(nullptr), _permRows(nullptr), _eTree(nullptr) +SuperLUSparseMatrix::SuperLUSparseMatrix() + : CompressedSparseMatrix(), _permMat(nullptr), _firstFactorization(true), _permCols(nullptr), _permRows(nullptr), + _eTree(nullptr) #ifdef LIBCADET_SUPERLU_MANAGE_MEMORY - , _memory(0) + , + _memory(0) #endif { allocateMatrixStructs(); } -SuperLUSparseMatrix::SuperLUSparseMatrix(unsigned int numRows, unsigned int numNonZeros) : CompressedSparseMatrix(numRows, numNonZeros), _permMat(nullptr), _firstFactorization(true) +SuperLUSparseMatrix::SuperLUSparseMatrix(unsigned int numRows, unsigned int numNonZeros) + : CompressedSparseMatrix(numRows, numNonZeros), _permMat(nullptr), _firstFactorization(true) #ifdef LIBCADET_SUPERLU_MANAGE_MEMORY - , _memory(0) + , + _memory(0) #endif { allocateMatrixStructs(); @@ -127,7 +132,7 @@ void SuperLUSparseMatrix::prepare() /* * Get column permutation vector _permCols[], according to permc_spec: - * permc_spec = NATURAL: natural ordering + * permc_spec = NATURAL: natural ordering * permc_spec = MMD_AT_PLUS_A: minimum degree on structure of A'+A * permc_spec = MMD_ATA: minimum degree on structure of A'*A * permc_spec = COLAMD: approximate minimum degree column ordering @@ -153,7 +158,7 @@ void SuperLUSparseMatrix::prepare() bool SuperLUSparseMatrix::factorize() { - // TODO: Compare SamePattern vs SamePattern_SameRowPerm + // TODO: Compare SamePattern vs SamePattern_SameRowPerm const fact_t mode = SamePattern_SameRowPerm; // Set options @@ -171,7 +176,8 @@ bool SuperLUSparseMatrix::factorize() { // Guess memory in bytes int memSize = 0; - dgstrf(&options, _permMat, superNodeRelaxation, panelSize, _eTree, nullptr, -1, _permCols, _permRows, _lower, _upper, _globalLU, _stats, &memSize); + dgstrf(&options, _permMat, superNodeRelaxation, panelSize, _eTree, nullptr, -1, _permCols, _permRows, _lower, + _upper, _globalLU, _stats, &memSize); // SuperLU returns numer of bytes + rows() _memory.resize(memSize - rows()); @@ -180,7 +186,7 @@ bool SuperLUSparseMatrix::factorize() if (!_firstFactorization && (mode == SamePattern)) { Destroy_SuperNode_Matrix(_lower); - Destroy_CompCol_Matrix(_upper); + Destroy_CompCol_Matrix(_upper); } #endif @@ -190,9 +196,11 @@ bool SuperLUSparseMatrix::factorize() { #ifdef LIBCADET_SUPERLU_MANAGE_MEMORY - dgstrf(&options, _permMat, superNodeRelaxation, panelSize, _eTree, _memory.data(), _memory.size(), _permCols, _permRows, _lower, _upper, _globalLU, _stats, &info); + dgstrf(&options, _permMat, superNodeRelaxation, panelSize, _eTree, _memory.data(), _memory.size(), _permCols, + _permRows, _lower, _upper, _globalLU, _stats, &info); #else - dgstrf(&options, _permMat, superNodeRelaxation, panelSize, _eTree, nullptr, 0, _permCols, _permRows, _lower, _upper, _globalLU, _stats, &info); + dgstrf(&options, _permMat, superNodeRelaxation, panelSize, _eTree, nullptr, 0, _permCols, _permRows, _lower, + _upper, _globalLU, _stats, &info); #endif if (cadet_likely(info == 0)) @@ -200,11 +208,11 @@ bool SuperLUSparseMatrix::factorize() // Matrix factorized successfully _firstFactorization = false; -// mem_usage_t memInfo; -// dQuerySpace(_lower, _upper, &memInfo); + // mem_usage_t memInfo; + // dQuerySpace(_lower, _upper, &memInfo); return true; } -#ifdef LIBCADET_SUPERLU_MANAGE_MEMORY +#ifdef LIBCADET_SUPERLU_MANAGE_MEMORY else if (info > rows()) { // Insufficient memory @@ -232,7 +240,7 @@ bool SuperLUSparseMatrix::factorize() else if (info > 0) { // Diagonal element (info, info) is exactly 0.0 -// LOG(Debug) << "Diagonal element (" << (info-1) << ", " << (info-1) << ") is exactly 0.0"; + // LOG(Debug) << "Diagonal element (" << (info-1) << ", " << (info-1) << ") is exactly 0.0"; _firstFactorization = false; return false; } @@ -255,6 +263,6 @@ bool SuperLUSparseMatrix::solve(double* rhs) const return info == 0; } -} // namespace linalg +} // namespace linalg -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/linalg/SuperLUSparseMatrix.hpp b/src/libcadet/linalg/SuperLUSparseMatrix.hpp index 366ab7f80..91ace18f6 100644 --- a/src/libcadet/linalg/SuperLUSparseMatrix.hpp +++ b/src/libcadet/linalg/SuperLUSparseMatrix.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,18 +11,18 @@ // ============================================================================= /** - * @file + * @file * Interfaces the compressed sparse matrix with the SuperLU solver */ /* * By default, SuperLU allocates memory as necessary during factorization. * This will happen each time the matrix is factorized, without exception. - * + * * It is possible to preallocate memory (or grow a buffer over a short time) * for all factorizations with a constant pattern. In order to enable this * method, #define LIBCADET_SUPERLU_MANAGE_MEMORY. - * + * * Note that, as of now, when using preallocated memory, each call to * factorize() will leak 64 bytes somewhere in SuperLU according to LLVM ASAN. */ @@ -33,9 +33,9 @@ #define LIBCADET_SUPERLUSPARSEMATRIX_HPP_ #ifndef LIBCADET_SUPERLUSPARSEMATRIX_NOFORWARD - struct SuperMatrix; - struct SuperLUStat_t; - struct GlobalLU_t; +struct SuperMatrix; +struct SuperLUStat_t; +struct GlobalLU_t; #endif namespace cadet @@ -93,7 +93,8 @@ class SuperLUSparseMatrix : public CompressedSparseMatrix /** * @brief Uses the factorized matrix to solve the equation @f$ Ax = b @f$ with LAPACK * @details Before the equation can be solved, the matrix has to be factorized first by calling factorize(). - * @param [in,out] rhs On entry pointer to the right hand side vector @f$ b @f$ of the equation, on exit the solution @f$ x @f$ + * @param [in,out] rhs On entry pointer to the right hand side vector @f$ b @f$ of the equation, on exit the + * solution @f$ x @f$ * @return @c true if the solution process was successful, otherwise @c false */ bool solve(double* rhs) const; @@ -101,17 +102,17 @@ class SuperLUSparseMatrix : public CompressedSparseMatrix protected: void allocateMatrixStructs(); - SuperMatrix* _mat; //!< SuperLU matrix info for main matrix - SuperMatrix* _rhsMat; //!< SuperLU matrix info for right hand side - SuperMatrix* _permMat; //!< Permuted SuperLU matrix info for main matrix + SuperMatrix* _mat; //!< SuperLU matrix info for main matrix + SuperMatrix* _rhsMat; //!< SuperLU matrix info for right hand side + SuperMatrix* _permMat; //!< Permuted SuperLU matrix info for main matrix bool _firstFactorization; //!< Determines whether this is the first factorization with the sparsity pattern - SuperLUStat_t* _stats; //!< SuperLU statistics - GlobalLU_t* _globalLU; //!< Holds factorization info (row factors) - SuperMatrix* _lower; //!< Lower triangular matrix (no storage, just pointer) - SuperMatrix* _upper; //!< Upper triangular matrix (no storage, just pointer) - sparse_int_t* _permCols; //!< Column permutations (structurally determined from sparsity pattern) - sparse_int_t* _permRows; //!< Row permutations (due to pivoting during factorization) - sparse_int_t* _eTree; //!< SuperLU elimination tree + SuperLUStat_t* _stats; //!< SuperLU statistics + GlobalLU_t* _globalLU; //!< Holds factorization info (row factors) + SuperMatrix* _lower; //!< Lower triangular matrix (no storage, just pointer) + SuperMatrix* _upper; //!< Upper triangular matrix (no storage, just pointer) + sparse_int_t* _permCols; //!< Column permutations (structurally determined from sparsity pattern) + sparse_int_t* _permRows; //!< Row permutations (due to pivoting during factorization) + sparse_int_t* _eTree; //!< SuperLU elimination tree #ifdef LIBCADET_SUPERLU_MANAGE_MEMORY std::vector _memory; //!< SuperLU workspace memory #endif @@ -121,4 +122,4 @@ class SuperLUSparseMatrix : public CompressedSparseMatrix } // namespace cadet -#endif // LIBCADET_SUPERLUSPARSEMATRIX_HPP_ +#endif // LIBCADET_SUPERLUSPARSEMATRIX_HPP_ diff --git a/src/libcadet/linalg/UMFPackSparseMatrix.cpp b/src/libcadet/linalg/UMFPackSparseMatrix.cpp index e1a6f6e0d..a76b3e02b 100644 --- a/src/libcadet/linalg/UMFPackSparseMatrix.cpp +++ b/src/libcadet/linalg/UMFPackSparseMatrix.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -15,28 +15,46 @@ #include "umfpack.h" -namespace +namespace { - template - class UMFPackInterface {}; +template class UMFPackInterface +{ +}; - template <> - class UMFPackInterface +template <> class UMFPackInterface +{ +public: + static inline void defaults(double* opts) { - public: - static inline void defaults(double* opts) { umfpack_di_defaults(opts); } - static inline void free_symbolic(void** sym) { umfpack_di_free_symbolic(sym); } - static inline void free_numeric(void** sym) { umfpack_di_free_numeric(sym); } - static inline int symbolic(int n_row, int n_col, int const* Ap, int const* Ai, double const* Ax, void** sym, double const* control, double* info) { return umfpack_di_symbolic(n_row, n_col, Ap, Ai, Ax, sym, control, info); } - static inline int numeric(int const* Ap, int const* Ai, double const* Ax, void* sym, void** num, double const* control, double* info) { return umfpack_di_numeric(Ap, Ai, Ax, sym, num, control, info); } - static inline int wsolve(int sys, int const* Ap, int const* Ai, double const* Ax, double* x, double const* b, void* num, double const* control, double* info, int* wi, double* w) - { - return umfpack_di_wsolve(sys, Ap, Ai, Ax, x, b, num, control, info, wi, w); - } - }; - - typedef UMFPackInterface UMFPack; -} + umfpack_di_defaults(opts); + } + static inline void free_symbolic(void** sym) + { + umfpack_di_free_symbolic(sym); + } + static inline void free_numeric(void** sym) + { + umfpack_di_free_numeric(sym); + } + static inline int symbolic(int n_row, int n_col, int const* Ap, int const* Ai, double const* Ax, void** sym, + double const* control, double* info) + { + return umfpack_di_symbolic(n_row, n_col, Ap, Ai, Ax, sym, control, info); + } + static inline int numeric(int const* Ap, int const* Ai, double const* Ax, void* sym, void** num, + double const* control, double* info) + { + return umfpack_di_numeric(Ap, Ai, Ax, sym, num, control, info); + } + static inline int wsolve(int sys, int const* Ap, int const* Ai, double const* Ax, double* x, double const* b, + void* num, double const* control, double* info, int* wi, double* w) + { + return umfpack_di_wsolve(sys, Ap, Ai, Ax, x, b, num, control, info, wi, w); + } +}; + +typedef UMFPackInterface UMFPack; +} // namespace namespace cadet { @@ -44,15 +62,17 @@ namespace cadet namespace linalg { -UMFPackSparseMatrix::UMFPackSparseMatrix() : CompressedSparseMatrix(), - _symbolic(nullptr), _numeric(nullptr), _info(UMFPACK_INFO, 0.0), _options(UMFPACK_CONTROL, 0.0), _result(0), _workSpaceIdx(0, 0), _workSpace(0, 0.0) +UMFPackSparseMatrix::UMFPackSparseMatrix() + : CompressedSparseMatrix(), _symbolic(nullptr), _numeric(nullptr), _info(UMFPACK_INFO, 0.0), + _options(UMFPACK_CONTROL, 0.0), _result(0), _workSpaceIdx(0, 0), _workSpace(0, 0.0) { UMFPack::defaults(_options.data()); _options[UMFPACK_ORDERING] = UMFPACK_ORDERING_AMD; } -UMFPackSparseMatrix::UMFPackSparseMatrix(unsigned int numRows, unsigned int numNonZeros) : CompressedSparseMatrix(numRows, numNonZeros), - _symbolic(nullptr), _numeric(nullptr), _info(UMFPACK_INFO, 0.0), _options(UMFPACK_CONTROL, 0.0), _result(numRows, 0.0), _workSpaceIdx(numRows, 0), _workSpace(numRows, 0.0) +UMFPackSparseMatrix::UMFPackSparseMatrix(unsigned int numRows, unsigned int numNonZeros) + : CompressedSparseMatrix(numRows, numNonZeros), _symbolic(nullptr), _numeric(nullptr), _info(UMFPACK_INFO, 0.0), + _options(UMFPACK_CONTROL, 0.0), _result(numRows, 0.0), _workSpaceIdx(numRows, 0), _workSpace(numRows, 0.0) { UMFPack::defaults(_options.data()); _options[UMFPACK_ORDERING] = UMFPACK_ORDERING_AMD; @@ -85,11 +105,12 @@ void UMFPackSparseMatrix::prepare() UMFPack::free_symbolic(&_symbolic); } - const auto status = UMFPack::symbolic(rows(), rows(), _rowStart.data(), _colIdx.data(), _values.data(), &_symbolic, _options.data(), _info.data()); + const auto status = UMFPack::symbolic(rows(), rows(), _rowStart.data(), _colIdx.data(), _values.data(), &_symbolic, + _options.data(), _info.data()); if (status != UMFPACK_OK) { -// umfpack_di_report_info(_options.data(), _info.data()); -// umfpack_di_report_status(_options.data(), status); + // umfpack_di_report_info(_options.data(), _info.data()); + // umfpack_di_report_status(_options.data(), status); } } @@ -101,11 +122,12 @@ bool UMFPackSparseMatrix::factorize() UMFPack::free_numeric(&_numeric); } - const auto status = UMFPack::numeric(_rowStart.data(), _colIdx.data(), _values.data(), _symbolic, &_numeric, _options.data(), _info.data()); + const auto status = UMFPack::numeric(_rowStart.data(), _colIdx.data(), _values.data(), _symbolic, &_numeric, + _options.data(), _info.data()); if (status != UMFPACK_OK) { -// umfpack_di_report_info(_options.data(), _info.data()); -// umfpack_di_report_status(_options.data(), status); + // umfpack_di_report_info(_options.data(), _info.data()); + // umfpack_di_report_status(_options.data(), status); } return status == UMFPACK_OK; @@ -113,12 +135,14 @@ bool UMFPackSparseMatrix::factorize() bool UMFPackSparseMatrix::solve(double* rhs) const { - const auto status = UMFPack::wsolve(UMFPACK_Aat, _rowStart.data(), _colIdx.data(), _values.data(), _result.data(), rhs, _numeric, _options.data(), _info.data(), _workSpaceIdx.data(), _workSpace.data()); + const auto status = + UMFPack::wsolve(UMFPACK_Aat, _rowStart.data(), _colIdx.data(), _values.data(), _result.data(), rhs, _numeric, + _options.data(), _info.data(), _workSpaceIdx.data(), _workSpace.data()); std::copy(_result.begin(), _result.end(), rhs); return status == UMFPACK_OK; } -} // namespace linalg +} // namespace linalg -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/linalg/UMFPackSparseMatrix.hpp b/src/libcadet/linalg/UMFPackSparseMatrix.hpp index 8cb770594..42a5bb301 100644 --- a/src/libcadet/linalg/UMFPackSparseMatrix.hpp +++ b/src/libcadet/linalg/UMFPackSparseMatrix.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Interfaces the compressed sparse matrix with the UMFPACK solver */ @@ -75,23 +75,24 @@ class UMFPackSparseMatrix : public CompressedSparseMatrix /** * @brief Uses the factorized matrix to solve the equation @f$ Ax = b @f$ with LAPACK * @details Before the equation can be solved, the matrix has to be factorized first by calling factorize(). - * @param [in,out] rhs On entry pointer to the right hand side vector @f$ b @f$ of the equation, on exit the solution @f$ x @f$ + * @param [in,out] rhs On entry pointer to the right hand side vector @f$ b @f$ of the equation, on exit the + * solution @f$ x @f$ * @return @c true if the solution process was successful, otherwise @c false */ bool solve(double* rhs) const; protected: - void* _symbolic; //!< Symbolic info for UMFPACK (orderings) - void* _numeric; //!< Factorization from UMFPACK (L, U factors) - mutable std::vector _info; //!< UMFPACK statistics - std::vector _options; //!< UMFPACK options - mutable std::vector _result; //!< Result cache + void* _symbolic; //!< Symbolic info for UMFPACK (orderings) + void* _numeric; //!< Factorization from UMFPACK (L, U factors) + mutable std::vector _info; //!< UMFPACK statistics + std::vector _options; //!< UMFPACK options + mutable std::vector _result; //!< Result cache mutable std::vector _workSpaceIdx; //!< UMFPACK workspace for solving linear system - mutable std::vector _workSpace; //!< UMFPACK workspace for solving linear system + mutable std::vector _workSpace; //!< UMFPACK workspace for solving linear system }; } // namespace linalg } // namespace cadet -#endif // LIBCADET_UMFPACKSPARSEMATRIX_HPP_ +#endif // LIBCADET_UMFPACKSPARSEMATRIX_HPP_ diff --git a/src/libcadet/model/BindingModel.hpp b/src/libcadet/model/BindingModel.hpp index 2af5f378e..c63595a73 100644 --- a/src/libcadet/model/BindingModel.hpp +++ b/src/libcadet/model/BindingModel.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the BindingModel interface. */ @@ -27,7 +27,7 @@ #include "linalg/BandMatrix.hpp" #ifdef ENABLE_DG - #include "linalg/BandedEigenSparseRowIterator.hpp" +#include "linalg/BandedEigenSparseRowIterator.hpp" #endif #include "AutoDiff.hpp" @@ -51,22 +51,20 @@ namespace model * liquid and solid phase. There can be multiple bound states in the solid phase, * if the binding model supports it. Some binding models require a dedicated salt * component, which is always given by component 0. - * + * * Each binding reaction can be quasi-stationary, that is, it is always in equilibrium. * A dynamic reaction generates the terms * @f[\begin{align} - * \frac{\mathrm{d}c_{p,i}}{\mathrm{d}t} + \dots - \frac{1-\varepsilon_p}{\varepsilon_p} v_{i,j}\left( c_p, q \right) &= 0\\ - * \frac{\mathrm{d}q_{i,j}}{\mathrm{d}t} + \dots + v_{i,j}\left( c_p, q \right) &= 0 - * \end{align}@f] + * \frac{\mathrm{d}c_{p,i}}{\mathrm{d}t} + \dots - \frac{1-\varepsilon_p}{\varepsilon_p} v_{i,j}\left( c_p, + * q \right) &= 0\\ \frac{\mathrm{d}q_{i,j}}{\mathrm{d}t} + \dots + v_{i,j}\left( c_p, q \right) &= 0 \end{align}@f] * where @f$ v_{i,j} @f$ is the flux of the reaction between @f$ c_{p,i} @f$ and @f$ q_{i,j} @f$. * Here, @f$ q_{i,j} @f$ denotes bound state @f$ j @f$ of component @f$ i @f$. - * + * * A quasi-stationary reaction, on the other hand, results in * @f[\begin{align} - * \frac{\mathrm{d}c_{p,i}}{\mathrm{d}t} + \frac{1-\varepsilon_p}{\varepsilon_p} \frac{\mathrm{d}q_{i,j}}{\mathrm{d}t} + \dots &= 0 \\ - * v_{i,j}\left( c_p, q \right) &= 0. - * \end{align}@f] - * + * \frac{\mathrm{d}c_{p,i}}{\mathrm{d}t} + \frac{1-\varepsilon_p}{\varepsilon_p} + * \frac{\mathrm{d}q_{i,j}}{\mathrm{d}t} + \dots &= 0 \\ v_{i,j}\left( c_p, q \right) &= 0. \end{align}@f] + * * The ordering inside the solid phase is component-major, that means, all bound * phases for each component are listed one after the other. For a model having * 4 components with 1, 0, 3, 2 bound states, respectively, the ordering is @@ -78,8 +76,9 @@ namespace model class IBindingModel { public: - - virtual ~IBindingModel() CADET_NOEXCEPT { } + virtual ~IBindingModel() CADET_NOEXCEPT + { + } /** * @brief Returns the name of the binding model @@ -113,7 +112,7 @@ class IBindingModel * (e.g., rate constants) are configured by configure(), this function * sets structural parameters (e.g., number of components and bound states). * In particular, it determines whether each binding reaction is quasi-stationary. - * + * * Before this function is called, usesParamProviderInDiscretizationConfig() * is queried to determine whether the @p paramProvider is used by this * function. If it is used, the @p paramProvider is put in the corresponding @@ -122,18 +121,21 @@ class IBindingModel * @param [in] paramProvider Parameter provider * @param [in] nComp Number of components * @param [in] nBound Array of size @p nComp which contains the number of bound states for each component - * @param [in] boundOffset Array of size @p nComp with offsets to the first bound state of each component beginning from the solid phase + * @param [in] boundOffset Array of size @p nComp with offsets to the first bound state of each component beginning + * from the solid phase */ - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) = 0; + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) = 0; /** - * @brief Configures the model by extracting all non-structural parameters (e.g., model parameters) from the given @p paramProvider + * @brief Configures the model by extracting all non-structural parameters (e.g., model parameters) from the given + * @p paramProvider * @details The scope of the cadet::IParameterProvider is left unchanged on return. - * + * * The structure of the model is left unchanged, that is, the number of degrees of * freedom stays the same (e.g., number of bound states is left unchanged). Only * true (non-structural) model parameters are read and changed. - * + * * This function may only be called if configureModelDiscretization() has been called * in the past. Contrary to configureModelDiscretization(), it can be called multiple * times. @@ -151,12 +153,13 @@ class IBindingModel * @param [in] unitOpIdx Index of the unit operation this binding model belongs to * @param [in] parTypeIdx Index of the particle type this binding model belongs to */ - virtual void fillBoundPhaseInitialParameters(ParameterId* params, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) const CADET_NOEXCEPT = 0; + virtual void fillBoundPhaseInitialParameters(ParameterId* params, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx) const CADET_NOEXCEPT = 0; /** * @brief Sets external functions for this binding model * @details The external functions are not owned by this IBindingModel. - * + * * @param [in] extFuns Pointer to array of IExternalFunction objects of size @p size * @param [in] size Number of elements in the IExternalFunction array @p extFuns */ @@ -178,10 +181,10 @@ class IBindingModel /** * @brief Sets a parameter value * @details The parameter identified by its unique parameter is set to the given value. - * + * * @param [in] pId ParameterId that identifies the parameter uniquely * @param [in] value Value of the parameter - * + * * @return @c true if the parameter has been successfully set to the given value, * otherwise @c false (e.g., if the parameter is not available in this model) */ @@ -252,7 +255,8 @@ class IBindingModel * @param [in] nBoundStates Array with bound states for each component * @return Size of the workspace in bytes */ - virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT = 0; + virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT = 0; /** * @brief Returns the amount of required AD seed vectors / directions @@ -265,60 +269,76 @@ class IBindingModel /** * @brief Evaluates the fluxes * @details The binding model is responsible for calculating the flux from the mobile to the solid phase. - * + * * This function is called simultaneously from multiple threads. * It needs to overwrite all values of @p res as the result array @p res is not * zeroed on entry. * @param [in] t Current time point * @param [in] secIdx Index of the current section - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] y Pointer to first bound state of the first component in the current particle shell * @param [in] yCp Pointer to first component of the mobile phase in the current particle shell * @param [out] res Pointer to result array that is filled with the fluxes * @param [in,out] workSpace Memory work space * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, active const* yCp, active* res, LinearBufferAllocator workSpace, WithParamSensitivity) const = 0; - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, active const* yCp, active* res, LinearBufferAllocator workSpace, WithoutParamSensitivity) const = 0; - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, active* res, LinearBufferAllocator workSpace) const = 0; - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, double* res, LinearBufferAllocator workSpace) const = 0; + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, active const* yCp, + active* res, LinearBufferAllocator workSpace, WithParamSensitivity) const = 0; + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, active const* yCp, + active* res, LinearBufferAllocator workSpace, WithoutParamSensitivity) const = 0; + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + active* res, LinearBufferAllocator workSpace) const = 0; + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + double* res, LinearBufferAllocator workSpace) const = 0; /** * @brief Evaluates the Jacobian of the fluxes analytically * @details This function is called simultaneously from multiple threads. * It can be left out (empty implementation) if AD is used to evaluate the Jacobian. - * + * * The Jacobian matrix is assumed to be zeroed out by the caller. * @param [in] t Current time point * @param [in] secIdx Index of the current section - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] y Pointer to first bound state of the first component in the current particle shell * @param [in] offsetCp Offset from the first component of the mobile phase in the current particle shell to @p y - * @param [in,out] jac Row iterator pointing to the first bound states row of the underlying matrix in which the Jacobian is stored + * @param [in,out] jac Row iterator pointing to the first bound states row of the underlying matrix in which the + * Jacobian is stored * @param [in,out] workSpace Memory work space */ - virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, int offsetCp, linalg::BandMatrix::RowIterator jac, LinearBufferAllocator workSpace) const = 0; - virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, int offsetCp, linalg::DenseBandedRowIterator jac, LinearBufferAllocator workSpace) const = 0; + virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + int offsetCp, linalg::BandMatrix::RowIterator jac, + LinearBufferAllocator workSpace) const = 0; + virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + int offsetCp, linalg::DenseBandedRowIterator jac, + LinearBufferAllocator workSpace) const = 0; #ifdef ENABLE_DG - virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, int offsetCp, linalg::BandedEigenSparseRowIterator jac, LinearBufferAllocator workSpace) const = 0; + virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + int offsetCp, linalg::BandedEigenSparseRowIterator jac, + LinearBufferAllocator workSpace) const = 0; #endif /** * @brief Calculates the time derivative of the quasi-stationary bound state equations - * @details Calculates @f$ \frac{\partial \text{flux}_{\text{qs}}}{\partial t} @f$ for the quasi-stationary equations - * in the flux. - * + * @details Calculates @f$ \frac{\partial \text{flux}_{\text{qs}}}{\partial t} @f$ for the quasi-stationary + * equations in the flux. + * * This function is called simultaneously from multiple threads. * It can be left out (empty implementation) which leads to slightly incorrect initial conditions * when using externally dependent binding models. * @param [in] t Current time point * @param [in] secIdx Index of the current section - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] yCp Pointer to first component in the liquid phase of the current particle shell * @param [in] y Pointer to first bound state of the first component in the current particle shell * @param [out] dResDt Pointer to array that stores the time derivative * @param [in,out] workSpace Memory work space */ - virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yCp, double const* y, double* dResDt, LinearBufferAllocator workSpace) const = 0; + virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yCp, double const* y, double* dResDt, + LinearBufferAllocator workSpace) const = 0; /** * @brief Returns an array that determines whether each binding reaction is quasi-stationary @@ -334,17 +354,19 @@ class IBindingModel * equations. If consistent initialization can be performed using analytic / direct * formulas (i.e., without running a nonlinear solver), this function would be the * correct place to do it. - * + * * This function is called simultaneously from multiple threads. * @param [in] t Current time point * @param [in] secIdx Index of the current section - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in,out] y Pointer to first bound state of the first component in the current particle shell * @param [in] yCp Pointer to first component of the mobile phase in the current particle shell * @param [in,out] workSpace Memory work space * @return @c true if a nonlinear solver is required for consistent initialization, otherwise @c false */ - virtual bool preConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const = 0; + virtual bool preConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, + double const* yCp, LinearBufferAllocator workSpace) const = 0; /** * @brief Called after a nonlinear solver has attempted consistent initialization @@ -352,16 +374,18 @@ class IBindingModel * Depending on the return value, a nonlinear solver is applied to the algebraic * equations and, afterwards, this function is called to clean up or refine the * solution. - * + * * This function is called simultaneously from multiple threads. * @param [in] t Current time point * @param [in] secIdx Index of the current section - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in,out] y Pointer to first bound state of the first component in the current particle shell * @param [in] yCp Pointer to first component of the mobile phase in the current particle shell * @param [in,out] workSpace Memory work space */ - virtual void postConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const = 0; + virtual void postConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, + double const* yCp, LinearBufferAllocator workSpace) const = 0; protected: }; @@ -369,4 +393,4 @@ class IBindingModel } // namespace model } // namespace cadet -#endif // LIBCADET_BINDINGMODELINTERFACE_HPP_ +#endif // LIBCADET_BINDINGMODELINTERFACE_HPP_ diff --git a/src/libcadet/model/ExternalFunctionSupport.hpp b/src/libcadet/model/ExternalFunctionSupport.hpp index 0675fe432..cb3b53c58 100644 --- a/src/libcadet/model/ExternalFunctionSupport.hpp +++ b/src/libcadet/model/ExternalFunctionSupport.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides support functions for defining externally dependent models. */ @@ -20,7 +20,7 @@ #include "cadet/ExternalFunction.hpp" #include "cadet/Exceptions.hpp" - + #include "LoggingUtils.hpp" #include "Logging.hpp" @@ -34,182 +34,208 @@ namespace cadet { template -inline bool readScalarParameterOrArray(std::vector& dest, IParameterProvider& paramProvider, const std::string& dataSet, unsigned int nExpand); +inline bool readScalarParameterOrArray(std::vector& dest, IParameterProvider& paramProvider, + const std::string& dataSet, unsigned int nExpand); namespace model { +/** + * @brief Base class of model parameter storage classes that do not depend on external functions + */ +struct ConstParamHandlerBase +{ + /** + * @brief Sets external functions for this model + * @param [in] extFuns Pointer to array of IExternalFunction objects of size @p size + * @param [in] size Number of elements in the IExternalFunction array @p extFuns + */ + inline void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) + { + } + + /** + * @brief Returns whether the model parameters depend on time + * @details Model parameters that do not use external functions do not depend on time. + * @return @c true if the model parameters depends on time, otherwise @c false + */ + static bool dependsOnTime() CADET_NOEXCEPT + { + return false; + } + + /** + * @brief Returns whether workspace is required for the parameters + * @details Model parameters that do not use external functions do not require a workspace for parameters. + * @return @c true if the model parameters require a workspace, otherwise @c false + */ + static bool requiresWorkspace() CADET_NOEXCEPT + { + return false; + } + /** - * @brief Base class of model parameter storage classes that do not depend on external functions + * @brief Returns how much memory is required for caching in bytes + * @details Memory size in bytes. + * @param [in] nComp Number of components + * @param [in] totalNumBoundStates Total number of bound states + * @param [in] nBoundStates Array with bound states for each component + * @return Memory size in bytes */ - struct ConstParamHandlerBase + inline std::size_t cacheSize(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT { - /** - * @brief Sets external functions for this model - * @param [in] extFuns Pointer to array of IExternalFunction objects of size @p size - * @param [in] size Number of elements in the IExternalFunction array @p extFuns - */ - inline void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) { } - - /** - * @brief Returns whether the model parameters depend on time - * @details Model parameters that do not use external functions do not depend on time. - * @return @c true if the model parameters depends on time, otherwise @c false - */ - static bool dependsOnTime() CADET_NOEXCEPT { return false; } - - /** - * @brief Returns whether workspace is required for the parameters - * @details Model parameters that do not use external functions do not require a workspace for parameters. - * @return @c true if the model parameters require a workspace, otherwise @c false - */ - static bool requiresWorkspace() CADET_NOEXCEPT { return false; } - - /** - * @brief Returns how much memory is required for caching in bytes - * @details Memory size in bytes. - * @param [in] nComp Number of components - * @param [in] totalNumBoundStates Total number of bound states - * @param [in] nBoundStates Array with bound states for each component - * @return Memory size in bytes - */ - inline std::size_t cacheSize(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT { return 0; } - - /** - * @brief Returns how much memory is required for caching in bytes - * @details Memory size in bytes. - * @param [in] nReactions Number of reactions - * @param [in] nComp Number of components - * @param [in] totalNumBoundStates Total number of bound states - * @return Memory size in bytes - */ - inline std::size_t cacheSize(unsigned int nReactions, unsigned int nComp, unsigned int totalNumBoundStates) const CADET_NOEXCEPT { return 0; } - }; + return 0; + } /** - * @brief Base class for externally dependent model parameter classes - * @details Configures and stores the external function used for the model parameters. + * @brief Returns how much memory is required for caching in bytes + * @details Memory size in bytes. + * @param [in] nReactions Number of reactions + * @param [in] nComp Number of components + * @param [in] totalNumBoundStates Total number of bound states + * @return Memory size in bytes + */ + inline std::size_t cacheSize(unsigned int nReactions, unsigned int nComp, + unsigned int totalNumBoundStates) const CADET_NOEXCEPT + { + return 0; + } +}; + +/** + * @brief Base class for externally dependent model parameter classes + * @details Configures and stores the external function used for the model parameters. + */ +struct ExternalParamHandlerBase +{ +public: + /** + * @brief Sets external functions for this model + * @param [in] extFuns Pointer to array of IExternalFunction objects of size @p size + * @param [in] size Number of elements in the IExternalFunction array @p extFuns */ - struct ExternalParamHandlerBase - { - public: - - /** - * @brief Sets external functions for this model - * @param [in] extFuns Pointer to array of IExternalFunction objects of size @p size - * @param [in] size Number of elements in the IExternalFunction array @p extFuns - */ - inline void setExternalFunctions(IExternalFunction** extFuns, int size) + inline void setExternalFunctions(IExternalFunction** extFuns, int size) + { + _extFun.clear(); + _extFun.resize(_extFunIndex.size(), nullptr); + for (std::size_t i = 0; i < _extFunIndex.size(); ++i) { - _extFun.clear(); - _extFun.resize(_extFunIndex.size(), nullptr); - for (std::size_t i = 0; i < _extFunIndex.size(); ++i) + if ((_extFunIndex[i] >= 0) && (_extFunIndex[i] < size)) + _extFun[i] = extFuns[_extFunIndex[i]]; + else { - if ((_extFunIndex[i] >= 0) && (_extFunIndex[i] < size)) - _extFun[i] = extFuns[_extFunIndex[i]]; - else - { - _extFun[i] = nullptr; - LOG(Warning) << "Index " << _extFunIndex[i] << " exceeds number of passed external functions (" << size << "), external dependence is ignored"; - } + _extFun[i] = nullptr; + LOG(Warning) << "Index " << _extFunIndex[i] << " exceeds number of passed external functions (" << size + << "), external dependence is ignored"; } } + } - /** - * @brief Returns whether the model parameters depend on time - * @details Model parameters that do not use external functions do not depend on time. - * @return @c true if the model parameters depends on time, otherwise @c false - */ - static bool dependsOnTime() CADET_NOEXCEPT { return true; } - - /** - * @brief Returns whether workspace is required for the parameters - * @details Model parameters that do not use external functions do not require a workspace for parameters. - * @return @c true if the model parameters require a workspace, otherwise @c false - */ - static bool requiresWorkspace() CADET_NOEXCEPT { return true; } - - protected: - - std::vector _extFun; //!< Pointer to the external function - std::vector _extFunIndex; //!< Index to the external function - - ExternalParamHandlerBase() : _extFun(), _extFunIndex() { } - - /** - * @brief Configures the external data source of this externally dependent parameter set - * @param [in] paramProvider Parameter provider - * @param [in] nParams Number of externally dependent parameters (also size of buffer) - */ - inline void configure(IParameterProvider& paramProvider, unsigned int nParams) - { - std::vector idx; - if (paramProvider.exists("EXTFUN")) - idx = paramProvider.getIntArray("EXTFUN"); - - if (idx.size() >= nParams) - _extFunIndex = idx; + /** + * @brief Returns whether the model parameters depend on time + * @details Model parameters that do not use external functions do not depend on time. + * @return @c true if the model parameters depends on time, otherwise @c false + */ + static bool dependsOnTime() CADET_NOEXCEPT + { + return true; + } + + /** + * @brief Returns whether workspace is required for the parameters + * @details Model parameters that do not use external functions do not require a workspace for parameters. + * @return @c true if the model parameters require a workspace, otherwise @c false + */ + static bool requiresWorkspace() CADET_NOEXCEPT + { + return true; + } + +protected: + std::vector _extFun; //!< Pointer to the external function + std::vector _extFunIndex; //!< Index to the external function + + ExternalParamHandlerBase() : _extFun(), _extFunIndex() + { + } + + /** + * @brief Configures the external data source of this externally dependent parameter set + * @param [in] paramProvider Parameter provider + * @param [in] nParams Number of externally dependent parameters (also size of buffer) + */ + inline void configure(IParameterProvider& paramProvider, unsigned int nParams) + { + std::vector idx; + if (paramProvider.exists("EXTFUN")) + idx = paramProvider.getIntArray("EXTFUN"); + + if (idx.size() >= nParams) + _extFunIndex = idx; + else + { + _extFunIndex.resize(nParams); + if (!idx.empty()) + { + // Use one external function for all parameters + std::fill(_extFunIndex.begin(), _extFunIndex.end(), idx[0]); + } else { - _extFunIndex.resize(nParams); - if (!idx.empty()) - { - // Use one external function for all parameters - std::fill(_extFunIndex.begin(), _extFunIndex.end(), idx[0]); - } - else - { - // There is no external dependence configured - std::fill(_extFunIndex.begin(), _extFunIndex.end(), -1); - } + // There is no external dependence configured + std::fill(_extFunIndex.begin(), _extFunIndex.end(), -1); } } + } - /** - * @brief Evaluates the external functions for the different parameters - * @param [in] t Current time - * @param [in] z Axial coordinate in the column - * @param [in] r Radial coordinate in the bead - * @param [in] secIdx Index of the current section - * @param [in] nParams Number of externally dependent parameters (also size of buffer) - * @param [out] buffer Buffer that holds function evaluations - */ - inline void evaluateExternalFunctions(double t, unsigned int secIdx, const ColumnPosition& colPos, unsigned int nParams, double* buffer) const + /** + * @brief Evaluates the external functions for the different parameters + * @param [in] t Current time + * @param [in] z Axial coordinate in the column + * @param [in] r Radial coordinate in the bead + * @param [in] secIdx Index of the current section + * @param [in] nParams Number of externally dependent parameters (also size of buffer) + * @param [out] buffer Buffer that holds function evaluations + */ + inline void evaluateExternalFunctions(double t, unsigned int secIdx, const ColumnPosition& colPos, + unsigned int nParams, double* buffer) const + { + for (unsigned int i = 0; i < nParams; ++i) { - for (unsigned int i = 0; i < nParams; ++i) - { - IExternalFunction* const fun = _extFun[i]; - if (fun) - buffer[i] = fun->externalProfile(t, colPos.axial, colPos.radial, colPos.particle, secIdx); - else - buffer[i] = 0.0; - } + IExternalFunction* const fun = _extFun[i]; + if (fun) + buffer[i] = fun->externalProfile(t, colPos.axial, colPos.radial, colPos.particle, secIdx); + else + buffer[i] = 0.0; } + } - /** - * @brief Evaluates the time derivative of the external functions for the different parameters - * @param [in] t Current time - * @param [in] z Axial coordinate in the column - * @param [in] r Radial coordinate in the bead - * @param [in] secIdx Index of the current section - * @param [in] nParams Number of externally dependent parameters (also size of buffer) - * @param [out] buffer Buffer that holds time derivatives of each external function - */ - inline void evaluateTimeDerivativeExternalFunctions(double t, unsigned int secIdx, const ColumnPosition& colPos, unsigned int nParams, double* buffer) const + /** + * @brief Evaluates the time derivative of the external functions for the different parameters + * @param [in] t Current time + * @param [in] z Axial coordinate in the column + * @param [in] r Radial coordinate in the bead + * @param [in] secIdx Index of the current section + * @param [in] nParams Number of externally dependent parameters (also size of buffer) + * @param [out] buffer Buffer that holds time derivatives of each external function + */ + inline void evaluateTimeDerivativeExternalFunctions(double t, unsigned int secIdx, const ColumnPosition& colPos, + unsigned int nParams, double* buffer) const + { + for (unsigned int i = 0; i < nParams; ++i) { - for (unsigned int i = 0; i < nParams; ++i) - { - IExternalFunction* const fun = _extFun[i]; - if (fun) - buffer[i] = fun->timeDerivative(t, colPos.axial, colPos.radial, colPos.particle, secIdx); - else - buffer[i] = 0.0; - } + IExternalFunction* const fun = _extFun[i]; + if (fun) + buffer[i] = fun->timeDerivative(t, colPos.axial, colPos.radial, colPos.particle, secIdx); + else + buffer[i] = 0.0; } - }; + } +}; -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet -#endif // LIBCADET_EXTFUNSUPPORT_HPP_ +#endif // LIBCADET_EXTFUNSUPPORT_HPP_ diff --git a/src/libcadet/model/GeneralRateModel-InitialConditions.cpp b/src/libcadet/model/GeneralRateModel-InitialConditions.cpp index c79c9e45a..88464a231 100644 --- a/src/libcadet/model/GeneralRateModel-InitialConditions.cpp +++ b/src/libcadet/model/GeneralRateModel-InitialConditions.cpp @@ -29,7 +29,7 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include +#include #endif namespace cadet @@ -39,11 +39,14 @@ namespace model { template -int GeneralRateModel::multiplexInitialConditions(const cadet::ParameterId& pId, unsigned int adDirection, double adValue) +int GeneralRateModel::multiplexInitialConditions(const cadet::ParameterId& pId, + unsigned int adDirection, double adValue) { if (_singleBinding) { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { _sensParams.insert(&_initCp[pId.component]); for (unsigned int t = 0; t < _disc.nParType; ++t) @@ -52,18 +55,23 @@ int GeneralRateModel::multiplexInitialConditions(const cadet:: else if (pId.name == hashString("INIT_CP")) return -1; - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { _sensParams.insert(&_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState]); for (unsigned int t = 0; t < _disc.nParType; ++t) - _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState].setADValue(adDirection, adValue); + _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState] + .setADValue(adDirection, adValue); } else if (pId.name == hashString("INIT_Q")) return -1; } else { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { _sensParams.insert(&_initCp[pId.particleType * _disc.nComp + pId.component]); _initCp[pId.particleType * _disc.nComp + pId.component].setADValue(adDirection, adValue); @@ -71,10 +79,16 @@ int GeneralRateModel::multiplexInitialConditions(const cadet:: else if (pId.name == hashString("INIT_CP")) return -1; - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { - _sensParams.insert(&_initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState]); - _initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState].setADValue(adDirection, adValue); + _sensParams.insert( + &_initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState]); + _initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState] + .setADValue(adDirection, adValue); } else if (pId.name == hashString("INIT_Q")) return -1; @@ -83,11 +97,14 @@ int GeneralRateModel::multiplexInitialConditions(const cadet:: } template -int GeneralRateModel::multiplexInitialConditions(const cadet::ParameterId& pId, double val, bool checkSens) +int GeneralRateModel::multiplexInitialConditions(const cadet::ParameterId& pId, double val, + bool checkSens) { if (_singleBinding) { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { if (checkSens && !contains(_sensParams, &_initCp[pId.component])) return -1; @@ -98,20 +115,27 @@ int GeneralRateModel::multiplexInitialConditions(const cadet:: else if (pId.name == hashString("INIT_CP")) return -1; - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { - if (checkSens && !contains(_sensParams, &_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState])) + if (checkSens && + !contains(_sensParams, + &_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState])) return -1; for (unsigned int t = 0; t < _disc.nParType; ++t) - _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState].setValue(val); + _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState] + .setValue(val); } else if (pId.name == hashString("INIT_Q")) return -1; } else { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { if (checkSens && !contains(_sensParams, &_initCp[pId.particleType * _disc.nComp + pId.component])) return -1; @@ -121,12 +145,19 @@ int GeneralRateModel::multiplexInitialConditions(const cadet:: else if (pId.name == hashString("INIT_CP")) return -1; - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { - if (checkSens && !contains(_sensParams, &_initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState])) + if (checkSens && + !contains(_sensParams, + &_initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState])) return -1; - _initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState].setValue(val); + _initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState] + .setValue(val); } else if (pId.name == hashString("INIT_Q")) return -1; @@ -148,7 +179,8 @@ void GeneralRateModel::applyInitialCondition(const SimulationS if (!_initStateDot.empty()) { std::fill(simState.vecStateYdot, simState.vecStateYdot + idxr.offsetC(), 0.0); - std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), simState.vecStateYdot + idxr.offsetC()); + std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), + simState.vecStateYdot + idxr.offsetC()); } else std::fill(simState.vecStateYdot, simState.vecStateYdot + numDofs(), 0.0); @@ -205,7 +237,8 @@ void GeneralRateModel::readInitialCondition(IParameterProvider // Check if INIT_STATE contains the full state and its time derivative if (initState.size() >= 2 * numPureDofs()) - _initStateDot = std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); + _initStateDot = + std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); return; } @@ -221,7 +254,8 @@ void GeneralRateModel::readInitialCondition(IParameterProvider { const std::vector initCp = paramProvider.getDoubleArray("INIT_CP"); - if (((initCp.size() < _disc.nComp) && _singleBinding) || ((initCp.size() < _disc.nComp * _disc.nParType) && !_singleBinding)) + if (((initCp.size() < _disc.nComp) && _singleBinding) || + ((initCp.size() < _disc.nComp * _disc.nParType) && !_singleBinding)) throw InvalidParameterException("INIT_CP does not contain enough values for all components"); if (!_singleBinding) @@ -245,7 +279,9 @@ void GeneralRateModel::readInitialCondition(IParameterProvider if (initQ.empty() || (_disc.strideBound[_disc.nParType] == 0)) return; - if ((_disc.strideBound[_disc.nParType] > 0) && (((initQ.size() < _disc.strideBound[_disc.nParType]) && !_singleBinding) || ((initQ.size() < _disc.strideBound[0]) && _singleBinding))) + if ((_disc.strideBound[_disc.nParType] > 0) && + (((initQ.size() < _disc.strideBound[_disc.nParType]) && !_singleBinding) || + ((initQ.size() < _disc.strideBound[0]) && _singleBinding))) throw InvalidParameterException("INIT_Q does not contain enough values for all bound states"); if (!_singleBinding) @@ -307,7 +343,9 @@ void GeneralRateModel::readInitialCondition(IParameterProvider * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ template -void GeneralRateModel::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModel::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -357,7 +395,8 @@ void GeneralRateModel::consistentInitialState(const Simulation LinearBufferAllocator tlmAlloc = threadLocalMem.get(); // Reuse memory of band matrix for dense matrix - linalg::DenseMatrixView fullJacobianMatrix(_jacPdisc[type * _disc.nCol + pblk].data(), nullptr, mask.len, mask.len); + linalg::DenseMatrixView fullJacobianMatrix(_jacPdisc[type * _disc.nCol + pblk].data(), nullptr, mask.len, + mask.len); // Midpoint of current column cell (z coordinate) - needed in externally dependent adsorption kinetic const double z = _convDispOp.relativeCoordinate(pblk); @@ -381,24 +420,34 @@ void GeneralRateModel::consistentInitialState(const Simulation BufferedArray conservedQuantsBuffer = tlmAlloc.array(numActiveComp); double* const conservedQuants = static_cast(conservedQuantsBuffer); - linalg::DenseMatrixView jacobianMatrix(jacobianMem, _jacPdisc[type * _disc.nCol + pblk].pivot(), probSize, probSize); + linalg::DenseMatrixView jacobianMatrix(jacobianMem, _jacPdisc[type * _disc.nCol + pblk].pivot(), probSize, + probSize); const parts::cell::CellParameters cellResParams = makeCellResidualParams(type, mask.mask + _disc.nComp); - // This loop cannot be run in parallel without creating a Jacobian matrix for each thread which would increase memory usage - const int localOffsetToParticle = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); - for(std::size_t shell = 0; shell < static_cast(_disc.nParCell[type]); ++shell) + // This loop cannot be run in parallel without creating a Jacobian matrix for each thread which would + // increase memory usage + const int localOffsetToParticle = + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); + for (std::size_t shell = 0; shell < static_cast(_disc.nParCell[type]); ++shell) { const int localOffsetInParticle = static_cast(shell) * idxr.strideParShell(type); // Get pointer to q variables in a shell of particle pblk - double* const qShell = vecStateY + localOffsetToParticle + localOffsetInParticle + idxr.strideParLiquid(); - active* const localAdRes = adJac.adRes ? adJac.adRes + localOffsetToParticle + localOffsetInParticle : nullptr; - active* const localAdY = adJac.adY ? adJac.adY + localOffsetToParticle + localOffsetInParticle : nullptr; - - const ColumnPosition colPos{z, 0.0, static_cast(_parCenterRadius[_disc.nParCellsBeforeType[type] + shell]) / static_cast(_parRadius[type])}; + double* const qShell = + vecStateY + localOffsetToParticle + localOffsetInParticle + idxr.strideParLiquid(); + active* const localAdRes = + adJac.adRes ? adJac.adRes + localOffsetToParticle + localOffsetInParticle : nullptr; + active* const localAdY = + adJac.adY ? adJac.adY + localOffsetToParticle + localOffsetInParticle : nullptr; + + const ColumnPosition colPos{ + z, 0.0, + static_cast(_parCenterRadius[_disc.nParCellsBeforeType[type] + shell]) / + static_cast(_parRadius[type])}; // Determine whether nonlinear solver is required - if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - idxr.strideParLiquid(), tlmAlloc)) + if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - idxr.strideParLiquid(), tlmAlloc)) continue; // Extract initial values from current state @@ -406,15 +455,16 @@ void GeneralRateModel::consistentInitialState(const Simulation // Save values of conserved moieties const double epsQ = 1.0 - static_cast(_parPorosity[type]); - linalg::conservedMoietiesFromPartitionedMask(mask, _disc.nBound + type * _disc.nComp, _disc.nComp, qShell - _disc.nComp, conservedQuants, static_cast(_parPorosity[type]), epsQ); + linalg::conservedMoietiesFromPartitionedMask(mask, _disc.nBound + type * _disc.nComp, _disc.nComp, + qShell - _disc.nComp, conservedQuants, + static_cast(_parPorosity[type]), epsQ); std::function jacFunc; if (localAdY && localAdRes) { - jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) - { - // Copy over state vector to AD state vector (without changing directional values to keep seed vectors) - // and initialize residuals with zero (also resetting directional values) + jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) { + // Copy over state vector to AD state vector (without changing directional values to keep seed + // vectors) and initialize residuals with zero (also resetting directional values) ad::copyToAd(qShell - _disc.nComp, localAdY, mask.len); // @todo Check if this is necessary ad::resetAd(localAdRes, mask.len); @@ -423,32 +473,34 @@ void GeneralRateModel::consistentInitialState(const Simulation linalg::applyVectorSubset(x, mask, localAdY); // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, localAdY, nullptr, localAdRes, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, localAdY, nullptr, localAdRes, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); #ifdef CADET_CHECK_ANALYTIC_JACOBIAN std::copy_n(qShell - _disc.nComp, mask.len, fullX); linalg::applyVectorSubset(x, mask, fullX); // Compute analytic Jacobian - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); // Compare const double diff = ad::compareDenseJacobianWithBandedAd( - localAdRes - localOffsetInParticle, localOffsetInParticle, adJac.adDirOffset, _jacP[type * _disc.nCol].lowerBandwidth(), - _jacP[type * _disc.nCol].lowerBandwidth(), _jacP[type * _disc.nCol].upperBandwidth(), fullJacobianMatrix - ); + localAdRes - localOffsetInParticle, localOffsetInParticle, adJac.adDirOffset, + _jacP[type * _disc.nCol].lowerBandwidth(), _jacP[type * _disc.nCol].lowerBandwidth(), + _jacP[type * _disc.nCol].upperBandwidth(), fullJacobianMatrix); LOG(Debug) << "MaxDiff: " << diff; #endif // Extract Jacobian from AD ad::extractDenseJacobianFromBandedAd( - localAdRes - localOffsetInParticle, localOffsetInParticle, adJac.adDirOffset, _jacP[type * _disc.nCol].lowerBandwidth(), - _jacP[type * _disc.nCol].lowerBandwidth(), _jacP[type * _disc.nCol].upperBandwidth(), fullJacobianMatrix - ); + localAdRes - localOffsetInParticle, localOffsetInParticle, adJac.adDirOffset, + _jacP[type * _disc.nCol].lowerBandwidth(), _jacP[type * _disc.nCol].lowerBandwidth(), + _jacP[type * _disc.nCol].upperBandwidth(), fullJacobianMatrix); // Extract Jacobian from full Jacobian mat.setAll(0.0); @@ -487,16 +539,16 @@ void GeneralRateModel::consistentInitialState(const Simulation } else { - jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) - { + jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) { // Prepare input vector by overwriting masked items std::copy_n(qShell - _disc.nComp, mask.len, fullX); linalg::applyVectorSubset(x, mask, fullX); // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); // Extract Jacobian from full Jacobian mat.setAll(0.0); @@ -536,16 +588,16 @@ void GeneralRateModel::consistentInitialState(const Simulation // Apply nonlinear solver _nonlinearSolver->solve( - [&](double const* const x, double* const r) - { + [&](double const* const x, double* const r) { // Prepare input vector by overwriting masked items std::copy_n(qShell - _disc.nComp, mask.len, fullX); linalg::applyVectorSubset(x, mask, fullX); // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); // Extract values from residual linalg::selectVectorSubset(fullResidual, mask, r); @@ -585,7 +637,8 @@ void GeneralRateModel::consistentInitialState(const Simulation linalg::applyVectorSubset(solution, mask, qShell - idxr.strideParLiquid()); // Refine / correct solution - _binding[type]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - idxr.strideParLiquid(), tlmAlloc); + _binding[type]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - idxr.strideParLiquid(), tlmAlloc); } } CADET_PARFOR_END; } @@ -644,10 +697,14 @@ void GeneralRateModel::consistentInitialState(const Simulation * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] vecStateY Consistently initialized state vector - * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent state time derivatives. + * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent + * state time derivatives. */ template -void GeneralRateModel::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModel::consistentInitialTimeDerivative(const SimulationTime& simTime, + double const* vecStateY, + double* const vecStateYdot, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -696,18 +753,24 @@ void GeneralRateModel::consistentInitialTimeDerivative(const S continue; // Get iterators to beginning of solid phase - linalg::BandMatrix::RowIterator jacSolidOrig = _jacP[pblk].row(j * static_cast(idxr.strideParShell(type)) + static_cast(idxr.strideParLiquid())); + linalg::BandMatrix::RowIterator jacSolidOrig = + _jacP[pblk].row(j * static_cast(idxr.strideParShell(type)) + + static_cast(idxr.strideParLiquid())); linalg::FactorizableBandMatrix::RowIterator jacSolid = jac - idxr.strideParBound(type); int const* const mask = _binding[type]->reactionQuasiStationarity(); - double* const qShellDot = vecStateYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}) + static_cast(j) * idxr.strideParShell(type) + idxr.strideParLiquid(); + double* const qShellDot = vecStateYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}) + + static_cast(j) * idxr.strideParShell(type) + idxr.strideParLiquid(); // Obtain derivative of fluxes wrt. time std::fill_n(dFluxDt, _disc.strideBound[type], 0.0); if (_binding[type]->dependsOnTime()) { - _binding[type]->timeDerivativeQuasiStationaryFluxes(simTime.t, simTime.secIdx, - ColumnPosition{z, 0.0, static_cast(_parCenterRadius[_disc.nParCellsBeforeType[type] + j]) / static_cast(_parRadius[type])}, + _binding[type]->timeDerivativeQuasiStationaryFluxes( + simTime.t, simTime.secIdx, + ColumnPosition{z, 0.0, + static_cast(_parCenterRadius[_disc.nParCellsBeforeType[type] + j]) / + static_cast(_parRadius[type])}, qShellDot - _disc.nComp, qShellDot, dFluxDt, tlmAlloc); } @@ -731,11 +794,13 @@ void GeneralRateModel::consistentInitialTimeDerivative(const S const bool result = fbm.factorize(); if (!result) { - LOG(Error) << "Factorize() failed for par block " << pblk << " (type " << type << " col " << par << ")\n" << fbm; + LOG(Error) << "Factorize() failed for par block " << pblk << " (type " << type << " col " << par << ")\n" + << fbm; } // Solve - const bool result2 = fbm.solve(scaleFactors, vecStateYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par})); + const bool result2 = + fbm.solve(scaleFactors, vecStateYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par})); if (!result2) { LOG(Error) << "Solve() failed for par block " << pblk << " (type " << type << " col " << par << ")"; @@ -755,8 +820,6 @@ void GeneralRateModel::consistentInitialTimeDerivative(const S solveForFluxes(vecStateYdot, idxr); } - - /** * @brief Computes approximately / partially consistent initial values (state variables without their time derivatives) * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have @@ -800,10 +863,14 @@ void GeneralRateModel::consistentInitialTimeDerivative(const S * @param [in] errorTol Error tolerance for algebraic equations */ template -void GeneralRateModel::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModel::leanConsistentInitialState(const SimulationTime& simTime, + double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) { if (isSectionDependent(_parDiffusionMode) || isSectionDependent(_parSurfDiffusionMode)) - LOG(Warning) << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; + LOG(Warning) + << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; BENCH_SCOPE(_timerConsistentInit); @@ -828,17 +895,24 @@ void GeneralRateModel::leanConsistentInitialState(const Simula // Midpoint of current column cell (z coordinate) - needed in externally dependent adsorption kinetic const double z = _convDispOp.relativeCoordinate(pblk); - const int localOffsetToParticle = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); - for(std::size_t shell = 0; shell < static_cast(_disc.nParCell[type]); ++shell) + const int localOffsetToParticle = + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); + for (std::size_t shell = 0; shell < static_cast(_disc.nParCell[type]); ++shell) { // Get pointer to q variables in a shell of particle pblk - const int localOffsetInParticle = static_cast(shell) * idxr.strideParShell(type) + idxr.strideParLiquid(); + const int localOffsetInParticle = + static_cast(shell) * idxr.strideParShell(type) + idxr.strideParLiquid(); double* const qShell = vecStateY + localOffsetToParticle + localOffsetInParticle; - const ColumnPosition colPos{z, 0.0, static_cast(_parCenterRadius[_disc.nParCellsBeforeType[type] + shell]) / static_cast(_parRadius[type])}; + const ColumnPosition colPos{ + z, 0.0, + static_cast(_parCenterRadius[_disc.nParCellsBeforeType[type] + shell]) / + static_cast(_parRadius[type])}; - // Perform consistent initialization that does not require a full fledged nonlinear solver (that may fail or damage the current state vector) - if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - idxr.strideParLiquid(), tlmAlloc)) + // Perform consistent initialization that does not require a full fledged nonlinear solver (that may + // fail or damage the current state vector) + if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - idxr.strideParLiquid(), tlmAlloc)) continue; } } CADET_PARFOR_END; @@ -893,14 +967,20 @@ void GeneralRateModel::leanConsistentInitialState(const Simula * * @param [in] t Current time point * @param [in] vecStateY (Lean) consistently initialized state vector - * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time derivatives. - * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during execution of the function. + * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time + * derivatives. + * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during + * execution of the function. */ template -void GeneralRateModel::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModel::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, + double* const res, + util::ThreadLocalStorage& threadLocalMem) { if (isSectionDependent(_parDiffusionMode) || isSectionDependent(_parSurfDiffusionMode)) - LOG(Warning) << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; + LOG(Warning) + << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; BENCH_SCOPE(_timerConsistentInit); @@ -980,25 +1060,26 @@ void GeneralRateModel::initializeSensitivityStates(const std:: * @brief Computes consistent initial values and time derivatives of sensitivity subsystems * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, * the sensitivity system for a parameter @f$ p @f$ reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of this linear DAE, @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ - * have to be consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the time - * derivative \f$ \dot{s}_0 \f$ such that they are consistent. + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of this linear DAE, @f$ s_0 = + * \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ have to be + * consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the + * time derivative \f$ \dot{s}_0 \f$ such that they are consistent. * - * The process follows closely the one of consistentInitialConditions() and, in fact, is a linearized version of it. - * This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ are - * related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them - * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p}. @f$ - *
    - *
  1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria). - * Once all @f$ c_i @f$, @f$ c_{p,i} @f$, and @f$ q_i^{(j)} @f$ have been computed, solve for the - * fluxes @f$ j_{f,i} @f$. Let @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at this point, we have - * \f[ \left( \frac{\partial F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, y_0, \dot{y}_0) \right)_{\mathcal{I}_a} = 0. \f]
  2. - *
  3. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. - * However, because of the algebraic equations, we need additional conditions to fully determine + * The process follows closely the one of consistentInitialConditions() and, in fact, is a linearized version + * of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ + * are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them + * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial + * \dot{y}_0}{\partial p}. @f$
    1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, + * reaction equilibria). Once all @f$ c_i @f$, @f$ c_{p,i} @f$, and @f$ q_i^{(j)} @f$ have been computed, solve for the + * fluxes @f$ j_{f,i} @f$. Let @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at + * this point, we have \f[ \left( \frac{\partial F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, + * y_0, \dot{y}_0) \right)_{\mathcal{I}_a} = 0. \f]
    2. Compute the time derivatives of the sensitivity @f$ \dot{s} + * @f$ such that the differential equations hold. However, because of the algebraic equations, we need additional + * conditions to fully determine * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the - * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting system - * has a similar structure as the system Jacobian. + * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting + * system has a similar structure as the system Jacobian. * @f[ \begin{align} * \left[\begin{array}{c|ccc|c} * \dot{J}_0 & & & & \\ @@ -1014,8 +1095,9 @@ void GeneralRateModel::initializeSensitivityStates(const std:: * @f$ J_{i,f} @f$ matrices in the right column are missing. * * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. - * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] - * which is 0 for algebraic equations (@f$ -\frac{\partial^2 F}{\partial t \partial p}@f$, to be more precise). + * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - + * \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] which is 0 for algebraic equations (@f$ -\frac{\partial^2 + * F}{\partial t \partial p}@f$, to be more precise). * * The linear system is solved by backsubstitution. First, the diagonal blocks are solved in parallel. * Then, the equations for the fluxes @f$ j_f @f$ are solved by substituting in the solution of the @@ -1030,8 +1112,9 @@ void GeneralRateModel::initializeSensitivityStates(const std:: * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ template -void GeneralRateModel::consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModel::consistentInitialSensitivity( + const SimulationTime& simTime, const ConstSimulationState& simState, std::vector& vecSensY, + std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -1066,7 +1149,8 @@ void GeneralRateModel::consistentInitialSensitivity(const Simu #endif { // Reuse memory of band matrix for dense matrix - linalg::DenseMatrixView jacobianMatrix(_jacPdisc[type * _disc.nCol + pblk].data(), _jacPdisc[type * _disc.nCol + pblk].pivot(), probSize, probSize); + linalg::DenseMatrixView jacobianMatrix(_jacPdisc[type * _disc.nCol + pblk].data(), + _jacPdisc[type * _disc.nCol + pblk].pivot(), probSize, probSize); // Get workspace memory LinearBufferAllocator tlmAlloc = threadLocalMem.get(); @@ -1077,27 +1161,35 @@ void GeneralRateModel::consistentInitialSensitivity(const Simu BufferedArray rhsUnmaskedBuffer = tlmAlloc.array(idxr.strideParBound(type)); double* const rhsUnmasked = static_cast(rhsUnmaskedBuffer); - double* const maskedMultiplier = _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); - double* const scaleFactors = _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); + double* const maskedMultiplier = + _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); + double* const scaleFactors = + _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); for (unsigned int shell = 0; shell < _disc.nParCell[type]; ++shell) { const int jacRowOffset = static_cast(shell) * idxr.strideParShell(type) + _disc.nComp; - const int localQOffset = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}) + static_cast(shell) * idxr.strideParShell(type) + idxr.strideParLiquid(); + const int localQOffset = + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}) + + static_cast(shell) * idxr.strideParShell(type) + idxr.strideParLiquid(); // Extract subproblem Jacobian from full Jacobian jacobianMatrix.setAll(0.0); - linalg::copyMatrixSubset(_jacP[type * _disc.nCol + pblk], mask, mask, jacRowOffset, 0, jacobianMatrix); + linalg::copyMatrixSubset(_jacP[type * _disc.nCol + pblk], mask, mask, jacRowOffset, 0, + jacobianMatrix); // Construct right hand side linalg::selectVectorSubset(sensYdot + localQOffset, mask, rhs); // Zero out masked elements - std::copy_n(sensY + localQOffset - idxr.strideParLiquid(), idxr.strideParShell(type), maskedMultiplier); + std::copy_n(sensY + localQOffset - idxr.strideParLiquid(), idxr.strideParShell(type), + maskedMultiplier); linalg::fillVectorSubset(maskedMultiplier + _disc.nComp, mask, 0.0); // Assemble right hand side - _jacP[type * _disc.nCol + pblk].submatrixMultiplyVector(maskedMultiplier, jacRowOffset, -static_cast(_disc.nComp), _disc.strideBound[type], idxr.strideParShell(type), rhsUnmasked); + _jacP[type * _disc.nCol + pblk].submatrixMultiplyVector( + maskedMultiplier, jacRowOffset, -static_cast(_disc.nComp), _disc.strideBound[type], + idxr.strideParShell(type), rhsUnmasked); linalg::vectorSubsetAdd(rhsUnmasked, mask, -1.0, 1.0, rhs); // Precondition @@ -1157,11 +1249,14 @@ void GeneralRateModel::consistentInitialSensitivity(const Simu if (_binding[type]->hasQuasiStationaryReactions()) { // Get iterators to beginning of solid phase - linalg::BandMatrix::RowIterator jacSolidOrig = _jacP[pblk].row(j * static_cast(idxr.strideParShell(type)) + static_cast(idxr.strideParLiquid())); + linalg::BandMatrix::RowIterator jacSolidOrig = + _jacP[pblk].row(j * static_cast(idxr.strideParShell(type)) + + static_cast(idxr.strideParLiquid())); linalg::FactorizableBandMatrix::RowIterator jacSolid = jac - idxr.strideParBound(type); int const* const mask = _binding[type]->reactionQuasiStationarity(); - double* const qShellDot = sensYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}) + static_cast(j) * idxr.strideParShell(type) + idxr.strideParLiquid(); + double* const qShellDot = sensYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}) + + static_cast(j) * idxr.strideParShell(type) + idxr.strideParLiquid(); // Copy row from original Jacobian and set right hand side for (int i = 0; i < idxr.strideParBound(type); ++i, ++jacSolid, ++jacSolidOrig) @@ -1192,7 +1287,8 @@ void GeneralRateModel::consistentInitialSensitivity(const Simu } // Solve - const bool result2 = fbm.solve(scaleFactors, sensYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par})); + const bool result2 = + fbm.solve(scaleFactors, sensYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par})); if (!result2) { LOG(Error) << "Solve() failed for par block " << pblk << " (type " << type << " col " << par << ")"; @@ -1216,20 +1312,20 @@ void GeneralRateModel::consistentInitialSensitivity(const Simu * @brief Computes approximately / partially consistent initial values and time derivatives of sensitivity subsystems * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, * the sensitivity system for a parameter @f$ p @f$ reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of this linear DAE, @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ - * have to be consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the time - * derivative \f$ \dot{s}_0 \f$ such that they are consistent. + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of this linear DAE, @f$ s_0 = + * \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ have to be + * consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the + * time derivative \f$ \dot{s}_0 \f$ such that they are consistent. * - * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized version of it. - * This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ are - * related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them - * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p}. @f$ - *
        - *
      1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations). - * Only solve for the fluxes @f$ j_{f,i} @f$ (only linear equations).
      2. - *
      3. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. - * However, because of the algebraic equations, we need additional conditions to fully determine + * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized + * version of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ + * \dot{s}_0 \f$ are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by + * differentiating them with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = + * \frac{\partial \dot{y}_0}{\partial p}. @f$
        1. Keep state and time derivative vectors as they are (i.e., do not + * solve algebraic equations). Only solve for the fluxes @f$ j_{f,i} @f$ (only linear equations).
        2. Compute the + * time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. However, because of + * the algebraic equations, we need additional conditions to fully determine * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting * equations are stated below: @@ -1243,8 +1339,9 @@ void GeneralRateModel::consistentInitialSensitivity(const Simu * where @f$ \dot{J}_0 @f$ denotes the bulk block Jacobian with respect to @f$ \dot{y}@f$. * * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. - * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] - * which is 0 for algebraic equations (@f$ -\frac{\partial^2 F}{\partial t \partial p}@f$, to be more precise). + * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - + * \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] which is 0 for algebraic equations (@f$ -\frac{\partial^2 + * F}{\partial t \partial p}@f$, to be more precise). * * The linear system is solved by backsubstitution. First, the bulk block is solved. * Then, the equations for the fluxes @f$ j_f @f$ are solved by substituting in the solution of the @@ -1259,11 +1356,13 @@ void GeneralRateModel::consistentInitialSensitivity(const Simu * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ template -void GeneralRateModel::leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModel::leanConsistentInitialSensitivity( + const SimulationTime& simTime, const ConstSimulationState& simState, std::vector& vecSensY, + std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) { if (isSectionDependent(_parDiffusionMode) || isSectionDependent(_parSurfDiffusionMode)) - LOG(Warning) << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; + LOG(Warning) + << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; BENCH_SCOPE(_timerConsistentInit); @@ -1331,10 +1430,11 @@ void GeneralRateModel::solveForFluxes(double* const vecState, { linalg::DoubleSparseMatrix const* const jacFPtype = _jacFP + type * _disc.nCol; for (unsigned int pblk = 0; pblk < _disc.nCol; ++pblk) - jacFPtype[pblk].multiplySubtract(vecState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), jf); + jacFPtype[pblk].multiplySubtract(vecState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), + jf); } } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/GeneralRateModel-LinearSolver.cpp b/src/libcadet/model/GeneralRateModel-LinearSolver.cpp index e0dd75e12..691cd7309 100644 --- a/src/libcadet/model/GeneralRateModel-LinearSolver.cpp +++ b/src/libcadet/model/GeneralRateModel-LinearSolver.cpp @@ -26,11 +26,11 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include - #include +#include +#include - typedef tbb::flow::continue_node< tbb::flow::continue_msg > node_t; - typedef const tbb::flow::continue_msg & msg_t; +typedef tbb::flow::continue_node node_t; +typedef const tbb::flow::continue_msg& msg_t; #endif namespace cadet @@ -41,12 +41,16 @@ namespace model /** * @brief Computes the solution of the linear system involving the system Jacobian - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + b \f] * has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the - * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) \f$, - * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p rhs. + * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) + \f$, + * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p + rhs. * - * The full Jacobian @f$ J = \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) @f$ is given by + * The full Jacobian @f$ J = \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} + \right) @f$ is given by * @f[ \begin{align} J = \left[\begin{array}{c|ccc|c} @@ -87,13 +91,16 @@ namespace model * Note that @f$ J_f = I @f$ is the identity matrix and that the off-diagonal blocks @f$ J_{i,f} @f$ * and @f$ J_{f,i} @f$ for @f$ i = 0, \dots, N_{z} @f$ are sparse. * - * Exploiting the decomposition, the solution procedure @f$ x = J^{-1}b = \left( LU \right)^{-1}b = U^{-1} L^{-1} b @f$ + * Exploiting the decomposition, the solution procedure @f$ x = J^{-1}b = \left( LU \right)^{-1}b = U^{-1} + L^{-1} b @f$ * works as follows: * -# Factorize the diagonal blocks @f$ J_0, \dots, J_{N_z} @f$ - * -# Solve @f$ y = L^{-1} b @f$ by forward substitution. This is accomplished by first solving the diagonal + * -# Solve @f$ y = L^{-1} b @f$ by forward substitution. This is accomplished by first solving the + diagonal * blocks independently, that is, * @f[ y_i = J_{i}^{-1} b_i. @f] - * Then, calculate the flux-part @f$ y_f @f$ by substituting in the already calculated solutions @f$ y_i @f$: + * Then, calculate the flux-part @f$ y_f @f$ by substituting in the already calculated solutions @f$ y_i + @f$: * @f[ y_f = b_f - \sum_{i=0}^{N_z} J_{f,i} y_i. @f] * -# Solve the Schur-complement @f$ S x_f = y_f @f$ using an iterative method that only requires * matrix-vector products. The already inverted diagonal blocks @f$ J_i^{-1} @f$ come in handy here. @@ -106,12 +113,13 @@ namespace model * @param [in] outerTol Error tolerance for the solution of the linear system from outer Newton iteration * @param [in,out] rhs On entry the right hand side of the linear equation system, on exit the solution * @param [in] weight Vector with error weights - * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is evaluated + * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is + evaluated * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ template -int GeneralRateModel::linearSolve(double t, double alpha, double outerTol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) +int GeneralRateModel::linearSolve(double t, double alpha, double outerTol, double* const rhs, + double const* const weight, const ConstSimulationState& simState) { BENCH_SCOPE(_timerLinearSolve); @@ -132,15 +140,15 @@ int GeneralRateModel::linearSolve(double t, double alpha, doub node_t A(g, [&](msg_t) #endif { - // Assemble and factorize discretized bulk Jacobian - const bool result = _convDispOp.assembleAndFactorizeDiscretizedJacobian(alpha); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Factorize() failed for bulk block"; - } + // Assemble and factorize discretized bulk Jacobian + const bool result = _convDispOp.assembleAndFactorizeDiscretizedJacobian(alpha); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Factorize() failed for bulk block"; + } } CADET_PARNODE_END; - // Process the particle blocks + // Process the particle blocks #ifdef CADET_PARALLELIZE node_t B(g, [&](msg_t) #endif @@ -151,54 +159,53 @@ int GeneralRateModel::linearSolve(double t, double alpha, doub for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nParType; ++pblk) #endif { - const unsigned int type = pblk / _disc.nCol; - const unsigned int par = pblk % _disc.nCol; + const unsigned int type = pblk / _disc.nCol; + const unsigned int par = pblk % _disc.nCol; - // Assemble - assembleDiscretizedJacobianParticleBlock(type, par, alpha, idxr); + // Assemble + assembleDiscretizedJacobianParticleBlock(type, par, alpha, idxr); - // Factorize - const bool result = _jacPdisc[pblk].factorize(); - if (cadet_unlikely(!result)) + // Factorize + const bool result = _jacPdisc[pblk].factorize(); + if (cadet_unlikely(!result)) + { { - { - LOG(Error) << "Factorize() failed for par block " << pblk; - } + LOG(Error) << "Factorize() failed for par block " << pblk; } + } } CADET_PARFOR_END; } CADET_PARNODE_END; #ifndef CADET_PARALLELIZE // Do not factorize again at next call without changed Jacobians _factorizeJacobian = false; - } // if (_factorizeJacobian) +} // if (_factorizeJacobian) #endif - // ====== Step 1.5: Solve J c_uo = b_uo - A * c_in = b_uo - A*b_in +// ====== Step 1.5: Solve J c_uo = b_uo - A * c_in = b_uo - A*b_in - // rhs is passed twice but due to the values in jacA the writes happen to a different area of the rhs than the reads. +// rhs is passed twice but due to the values in jacA the writes happen to a different area of the rhs than the reads. #ifdef CADET_PARALLELIZE node_t C(g, [&](msg_t) #endif { - _jacInlet.multiplySubtract(rhs, rhs + idxr.offsetC()); + _jacInlet.multiplySubtract(rhs, rhs + idxr.offsetC()); } CADET_PARNODE_END; // ==== Step 2: Solve diagonal Jacobian blocks J_i to get y_i = J_i^{-1} b_i // The result is stored in rhs (in-place solution) - // Threads that are done with solving the bulk column blocks can proceed // to solving the particle blocks #ifdef CADET_PARALLELIZE node_t D(g, [&](msg_t) #endif { - const bool result = _convDispOp.solveDiscretizedJacobian(rhs + idxr.offsetC()); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Solve() failed for bulk block"; - } + const bool result = _convDispOp.solveDiscretizedJacobian(rhs + idxr.offsetC()); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Solve() failed for bulk block"; + } } CADET_PARNODE_END; #ifdef CADET_PARALLELIZE @@ -208,16 +215,16 @@ int GeneralRateModel::linearSolve(double t, double alpha, doub #ifdef CADET_PARALLELIZE tbb::parallel_for(std::size_t(0), static_cast(_disc.nCol * _disc.nParType), [&](std::size_t pblk) #else - for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nParType; ++pblk) + for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nParType; ++pblk) #endif { - const unsigned int type = pblk / _disc.nCol; - const unsigned int par = pblk % _disc.nCol; - const bool result = _jacPdisc[pblk].solve(rhs + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par})); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Solve() failed for par block " << pblk; - } + const unsigned int type = pblk / _disc.nCol; + const unsigned int par = pblk % _disc.nCol; + const bool result = _jacPdisc[pblk].solve(rhs + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par})); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Solve() failed for par block " << pblk; + } } CADET_PARFOR_END; } CADET_PARNODE_END; @@ -229,46 +236,47 @@ int GeneralRateModel::linearSolve(double t, double alpha, doub node_t F(g, [&](msg_t) #endif { - _jacFC.multiplySubtract(rhs + idxr.offsetC(), rhs + idxr.offsetJf()); + _jacFC.multiplySubtract(rhs + idxr.offsetC(), rhs + idxr.offsetJf()); - for (unsigned int type = 0; type < _disc.nParType; ++type) + for (unsigned int type = 0; type < _disc.nParType; ++type) + { + for (unsigned int par = 0; par < _disc.nCol; ++par) { - for (unsigned int par = 0; par < _disc.nCol; ++par) - { - _jacFP[type * _disc.nCol + par].multiplySubtract(rhs + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}), rhs + idxr.offsetJf()); - } + _jacFP[type * _disc.nCol + par].multiplySubtract( + rhs + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}), rhs + idxr.offsetJf()); } + } - // Now, rhs contains the full intermediate solution y = L^{-1} b + // Now, rhs contains the full intermediate solution y = L^{-1} b - // Initialize temporary storage by copying over the fluxes - // Note that the rest of _tempState is zeroed out in schurComplementMatrixVector() - std::copy(rhs + idxr.offsetJf(), rhs + numDofs(), _tempState + idxr.offsetJf()); + // Initialize temporary storage by copying over the fluxes + // Note that the rest of _tempState is zeroed out in schurComplementMatrixVector() + std::copy(rhs + idxr.offsetJf(), rhs + numDofs(), _tempState + idxr.offsetJf()); - // ==== Step 3: Solve Schur-complement to get x_f = S^{-1} y_f - // Column and particle parts remain unchanged. - // The only thing to be done is the iterative (and approximate) - // solution of the Schur complement system: - // S * x_f = y_f + // ==== Step 3: Solve Schur-complement to get x_f = S^{-1} y_f + // Column and particle parts remain unchanged. + // The only thing to be done is the iterative (and approximate) + // solution of the Schur complement system: + // S * x_f = y_f - // Note that rhs is updated in-place with the solution of the Schur-complement - // The temporary storage is only needed to hold the right hand side of the Schur-complement - const double tolerance = std::sqrt(static_cast(_gmres.matrixSize())) * outerTol * _schurSafety; + // Note that rhs is updated in-place with the solution of the Schur-complement + // The temporary storage is only needed to hold the right hand side of the Schur-complement + const double tolerance = std::sqrt(static_cast(_gmres.matrixSize())) * outerTol * _schurSafety; - BENCH_START(_timerGmres); - _gmres.solve(tolerance, weight + idxr.offsetJf(), _tempState + idxr.offsetJf(), rhs + idxr.offsetJf()); - BENCH_STOP(_timerGmres); + BENCH_START(_timerGmres); + _gmres.solve(tolerance, weight + idxr.offsetJf(), _tempState + idxr.offsetJf(), rhs + idxr.offsetJf()); + BENCH_STOP(_timerGmres); - // Remove temporary results that are leftovers from schurComplementMatrixVector() - std::fill(_tempState + idxr.offsetC(), _tempState + idxr.offsetJf(), 0.0); + // Remove temporary results that are leftovers from schurComplementMatrixVector() + std::fill(_tempState + idxr.offsetC(), _tempState + idxr.offsetJf(), 0.0); - // At this point, rhs contains the intermediate solution [y_0, ..., y_{N_z}, x_f] + // At this point, rhs contains the intermediate solution [y_0, ..., y_{N_z}, x_f] - // ==== Step 4: Solve U * x = y by backward substitution - // The fluxes are already solved and remain unchanged + // ==== Step 4: Solve U * x = y by backward substitution + // The fluxes are already solved and remain unchanged - // Compute tempState_0 = J_{0,f} * y_f - _jacCF.multiplyAdd(rhs + idxr.offsetJf(), _tempState + idxr.offsetC()); + // Compute tempState_0 = J_{0,f} * y_f + _jacCF.multiplyAdd(rhs + idxr.offsetJf(), _tempState + idxr.offsetC()); } CADET_PARNODE_END; // Threads that are done with solving the bulk column blocks can proceed @@ -277,19 +285,19 @@ int GeneralRateModel::linearSolve(double t, double alpha, doub node_t G(g, [&](msg_t) #endif { - double* const localCol = _tempState + idxr.offsetC(); - double* const rhsCol = rhs + idxr.offsetC(); + double* const localCol = _tempState + idxr.offsetC(); + double* const rhsCol = rhs + idxr.offsetC(); - // Apply J_0^{-1} to tempState_0 - const bool result = _convDispOp.solveDiscretizedJacobian(localCol); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Solve() failed for bulk block"; - } + // Apply J_0^{-1} to tempState_0 + const bool result = _convDispOp.solveDiscretizedJacobian(localCol); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Solve() failed for bulk block"; + } - // Compute rhs_0 = y_0 - J_0^{-1} * J_{0,f} * y_f = y_0 - tempState_0 - for (unsigned int i = 0; i < _disc.nCol * _disc.nComp; ++i) - rhsCol[i] -= localCol[i]; + // Compute rhs_0 = y_0 - J_0^{-1} * J_{0,f} * y_f = y_0 - tempState_0 + for (unsigned int i = 0; i < _disc.nCol * _disc.nComp; ++i) + rhsCol[i] -= localCol[i]; } CADET_PARNODE_END; #ifdef CADET_PARALLELIZE @@ -299,27 +307,27 @@ int GeneralRateModel::linearSolve(double t, double alpha, doub #ifdef CADET_PARALLELIZE tbb::parallel_for(std::size_t(0), static_cast(_disc.nCol * _disc.nParType), [&](std::size_t pblk) #else - for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nParType; ++pblk) + for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nParType; ++pblk) #endif { - const unsigned int type = pblk / _disc.nCol; - const unsigned int par = pblk % _disc.nCol; + const unsigned int type = pblk / _disc.nCol; + const unsigned int par = pblk % _disc.nCol; - double* const localPar = _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); - double* const rhsPar = rhs + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); + double* const localPar = _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); + double* const rhsPar = rhs + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); - // Compute tempState_i = J_{i,f} * y_f - _jacPF[pblk].multiplyAdd(rhs + idxr.offsetJf(), localPar); - // Apply J_i^{-1} to tempState_i - const bool result = _jacPdisc[pblk].solve(localPar); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Solve() failed for par block " << pblk; - } + // Compute tempState_i = J_{i,f} * y_f + _jacPF[pblk].multiplyAdd(rhs + idxr.offsetJf(), localPar); + // Apply J_i^{-1} to tempState_i + const bool result = _jacPdisc[pblk].solve(localPar); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Solve() failed for par block " << pblk; + } - // Compute rhs_i = y_i - J_i^{-1} * J_{i,f} * y_f = y_i - tempState_i - for (int i = 0; i < idxr.strideParBlock(type); ++i) - rhsPar[i] -= localPar[i]; + // Compute rhs_i = y_i - J_i^{-1} * J_{i,f} * y_f = y_i - tempState_i + for (int i = 0; i < idxr.strideParBlock(type); ++i) + rhsPar[i] -= localPar[i]; } CADET_PARFOR_END; } CADET_PARNODE_END; @@ -327,8 +335,8 @@ int GeneralRateModel::linearSolve(double t, double alpha, doub // Create TBB dependency graph if (_factorizeJacobian) { - make_edge(A, C); - make_edge(B, C); + make_edge(A, C); + make_edge(B, C); } make_edge(C, D); @@ -341,11 +349,11 @@ int GeneralRateModel::linearSolve(double t, double alpha, doub // Start the graph running if (_factorizeJacobian) { - // Do not factorize again at next call without changed Jacobians - _factorizeJacobian = false; + // Do not factorize again at next call without changed Jacobians + _factorizeJacobian = false; - A.try_put(tbb::flow::continue_msg()); - B.try_put(tbb::flow::continue_msg()); + A.try_put(tbb::flow::continue_msg()); + B.try_put(tbb::flow::continue_msg()); } else C.try_put(tbb::flow::continue_msg()); @@ -356,45 +364,46 @@ int GeneralRateModel::linearSolve(double t, double alpha, doub // The full solution is now stored in rhs return 0; -} + } -/** - * @brief Performs the matrix-vector product @f$ z = Sx @f$ with the Schur-complement @f$ S @f$ from the Jacobian - * @details The Schur-complement @f$ S @f$ is given by - * @f[ \begin{align} - S &= J_f - J_{f,0} \, J_0^{-1} \, J_{0,f} - \sum_{p=1}^{N_z}{J_{f,p} \, J_p^{-1} \, J_{p,f}} \\ - &= I - \sum_{p=0}^{N_z}{J_{f,p} \, J_p^{-1} \, J_{p,f}}, - \end{align} @f] - * where @f$ J_f = I @f$ is the identity matrix and the off-diagonal blocks @f$ J_{i,f} @f$ - * and @f$ J_{f,i} @f$ for @f$ i = 0, \dots, N_{z} @f$ are sparse. - * - * The matrix-vector multiplication is executed in parallel as follows: - * -# Compute @f$ J_{f,i} \, J_i^{-1} \, J_{i,f} @f$ independently (in parallel with respect to index @f$ i @f$) - * -# Subtract the result from @f$ z @f$ - * - * @param [in] x Vector @f$ x @f$ the matrix @f$ S @f$ is multiplied with - * @param [out] z Result of the matrix-vector multiplication - * @return @c 0 if successful, any other value in case of failure - */ -template -int GeneralRateModel::schurComplementMatrixVector(double const* x, double* z) const -{ - BENCH_SCOPE(_timerMatVec); + /** + * @brief Performs the matrix-vector product @f$ z = Sx @f$ with the Schur-complement @f$ S @f$ from the Jacobian + * @details The Schur-complement @f$ S @f$ is given by + * @f[ \begin{align} + S &= J_f - J_{f,0} \, J_0^{-1} \, J_{0,f} - \sum_{p=1}^{N_z}{J_{f,p} \, J_p^{-1} \, J_{p,f}} \\ + &= I - \sum_{p=0}^{N_z}{J_{f,p} \, J_p^{-1} \, J_{p,f}}, + \end{align} @f] + * where @f$ J_f = I @f$ is the identity matrix and the off-diagonal blocks @f$ J_{i,f} @f$ + * and @f$ J_{f,i} @f$ for @f$ i = 0, \dots, N_{z} @f$ are sparse. + * + * The matrix-vector multiplication is executed in parallel as follows: + * -# Compute @f$ J_{f,i} \, J_i^{-1} \, J_{i,f} @f$ independently (in parallel with respect to index + @f$ i @f$) + * -# Subtract the result from @f$ z @f$ + * + * @param [in] x Vector @f$ x @f$ the matrix @f$ S @f$ is multiplied with + * @param [out] z Result of the matrix-vector multiplication + * @return @c 0 if successful, any other value in case of failure + */ + template + int GeneralRateModel::schurComplementMatrixVector(double const* x, double* z) const + { + BENCH_SCOPE(_timerMatVec); - // Copy x over to result z, which corresponds to the application of the identity matrix - std::copy(x, x + _disc.nCol * _disc.nComp * _disc.nParType, z); + // Copy x over to result z, which corresponds to the application of the identity matrix + std::copy(x, x + _disc.nCol * _disc.nComp * _disc.nParType, z); - Indexer idxr(_disc); - std::fill(_tempState + idxr.offsetC(), _tempState + idxr.offsetJf(), 0.0); + Indexer idxr(_disc); + std::fill(_tempState + idxr.offsetC(), _tempState + idxr.offsetJf(), 0.0); #ifdef CADET_PARALLELIZE - tbb::flow::graph g; + tbb::flow::graph g; #endif - // Solve bulk column block first + // Solve bulk column block first - // Apply J_{0,f} - _jacCF.multiplyAdd(x, _tempState + idxr.offsetC()); + // Apply J_{0,f} + _jacCF.multiplyAdd(x, _tempState + idxr.offsetC()); #ifdef CADET_PARALLELIZE node_t A(g, [&](msg_t) @@ -412,11 +421,11 @@ int GeneralRateModel::schurComplementMatrixVector(double const node_t B(g, [&](msg_t) #endif { - // Handle particle blocks + // Handle particle blocks #ifdef CADET_PARALLELIZE tbb::parallel_for(std::size_t(0), static_cast(_disc.nCol * _disc.nParType), [&](std::size_t pblk) #else - for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nParType; ++pblk) + for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nParType; ++pblk) #endif { const unsigned int type = pblk / _disc.nCol; @@ -448,7 +457,8 @@ int GeneralRateModel::schurComplementMatrixVector(double const for (unsigned int par = 0; par < _disc.nCol; ++par) { // Apply J_{f,i} and subtract results from z - _jacFP[type * _disc.nCol + par].multiplySubtract(_tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}), z); + _jacFP[type * _disc.nCol + par].multiplySubtract( + _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}), z); } } } CADET_PARNODE_END; @@ -466,62 +476,63 @@ int GeneralRateModel::schurComplementMatrixVector(double const #endif return 0; -} + } -/** - * @brief Assembles a particle Jacobian block @f$ J_i @f$ (@f$ i > 0 @f$) of the time-discretized equations - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The system Jacobian of the original equations, - * \f[ \frac{\partial F}{\partial y}, \f] - * is already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible - * for adding - * \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] - * to the system Jacobian, which yields the Jacobian of the time-discretized equations - * \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] - * when a BDF method is used. The time integrator needs to solve this equation for @f$ y_0 @f$, which requires - * the solution of the linear system mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). - * - * @param [in] parType Index of the particle type - * @param [in] pblk Index of the particle block within a type - * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) - * @param [in] idxr Indexer - */ -template -void GeneralRateModel::assembleDiscretizedJacobianParticleBlock(unsigned int parType, unsigned int pblk, double alpha, const Indexer& idxr) -{ - linalg::FactorizableBandMatrix& fbm = _jacPdisc[_disc.nCol * parType + pblk]; - const linalg::BandMatrix& bm = _jacP[_disc.nCol * parType + pblk]; + /** + * @brief Assembles a particle Jacobian block @f$ J_i @f$ (@f$ i > 0 @f$) of the time-discretized equations + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) + * x = b \f] has to be solved. The system Jacobian of the original equations, \f[ \frac{\partial F}{\partial y}, \f] + * is already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is + * responsible for adding \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] to the system Jacobian, which yields + * the Jacobian of the time-discretized equations \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] when + * a BDF method is used. The time integrator needs to solve this equation for @f$ y_0 @f$, which requires the + * solution of the linear system mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). + * + * @param [in] parType Index of the particle type + * @param [in] pblk Index of the particle block within a type + * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) + * @param [in] idxr Indexer + */ + template + void GeneralRateModel::assembleDiscretizedJacobianParticleBlock(unsigned int parType, + unsigned int pblk, double alpha, + const Indexer& idxr) + { + linalg::FactorizableBandMatrix& fbm = _jacPdisc[_disc.nCol * parType + pblk]; + const linalg::BandMatrix& bm = _jacP[_disc.nCol * parType + pblk]; - // Copy normal matrix over to factorizable matrix - fbm.copyOver(bm); + // Copy normal matrix over to factorizable matrix + fbm.copyOver(bm); - // Add time derivatives to particle shells - linalg::FactorizableBandMatrix::RowIterator jac = fbm.row(0); - for (unsigned int j = 0; j < _disc.nParCell[parType]; ++j) - { - addTimeDerivativeToJacobianParticleShell(jac, idxr, alpha, parType); - // Iterator jac has already been advanced to next shell + // Add time derivatives to particle shells + linalg::FactorizableBandMatrix::RowIterator jac = fbm.row(0); + for (unsigned int j = 0; j < _disc.nParCell[parType]; ++j) + { + addTimeDerivativeToJacobianParticleShell(jac, idxr, alpha, parType); + // Iterator jac has already been advanced to next shell + } } -} - -/** - * @brief Adds Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ to bead rows of system Jacobian - * @details Actually adds @f$ \alpha \frac{\partial F}{\partial \dot{y}} @f$, which is useful - * for constructing the linear system in BDF time discretization. - * @param [in,out] jac On entry, RowIterator of the particle block pointing to the beginning of a bead shell; - * on exit, the iterator points to the end of the bead shell - * @param [in] idxr Indexer - * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) - * @param [in] parType Index of the particle type - */ -template -void GeneralRateModel::addTimeDerivativeToJacobianParticleShell(linalg::FactorizableBandMatrix::RowIterator& jac, const Indexer& idxr, double alpha, unsigned int parType) -{ - parts::cell::addTimeDerivativeToJacobianParticleShell(jac, alpha, static_cast(_parPorosity[parType]), _disc.nComp, _disc.nBound + _disc.nComp * parType, - _poreAccessFactor.data() + _disc.nComp * parType, _disc.strideBound[parType], _disc.boundOffset + _disc.nComp * parType, _binding[parType]->reactionQuasiStationarity()); -} + /** + * @brief Adds Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ to bead rows of system Jacobian + * @details Actually adds @f$ \alpha \frac{\partial F}{\partial \dot{y}} @f$, which is useful + * for constructing the linear system in BDF time discretization. + * @param [in,out] jac On entry, RowIterator of the particle block pointing to the beginning of a bead shell; + * on exit, the iterator points to the end of the bead shell + * @param [in] idxr Indexer + * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) + * @param [in] parType Index of the particle type + */ + template + void GeneralRateModel::addTimeDerivativeToJacobianParticleShell( + linalg::FactorizableBandMatrix::RowIterator& jac, const Indexer& idxr, double alpha, unsigned int parType) + { + parts::cell::addTimeDerivativeToJacobianParticleShell( + jac, alpha, static_cast(_parPorosity[parType]), _disc.nComp, _disc.nBound + _disc.nComp * parType, + _poreAccessFactor.data() + _disc.nComp * parType, _disc.strideBound[parType], + _disc.boundOffset + _disc.nComp * parType, _binding[parType]->reactionQuasiStationarity()); + } -} // namespace model + } // namespace model -} // namespace cadet + } // namespace cadet diff --git a/src/libcadet/model/GeneralRateModel.cpp b/src/libcadet/model/GeneralRateModel.cpp index b99dbfda3..0109a1cc3 100644 --- a/src/libcadet/model/GeneralRateModel.cpp +++ b/src/libcadet/model/GeneralRateModel.cpp @@ -43,7 +43,7 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include +#include #endif namespace cadet @@ -56,25 +56,22 @@ constexpr double SurfVolRatioSphere = 3.0; constexpr double SurfVolRatioCylinder = 2.0; constexpr double SurfVolRatioSlab = 1.0; -template -int schurComplementMultiplierGRM(void* userData, double const* x, double* z) +template int schurComplementMultiplierGRM(void* userData, double const* x, double* z) { GeneralRateModel* const grm = static_cast*>(userData); return grm->schurComplementMatrixVector(x, z); } - template -GeneralRateModel::GeneralRateModel(UnitOpIdx unitOpIdx) : UnitOperationBase(unitOpIdx), - _hasSurfaceDiffusion(0, false), _dynReactionBulk(nullptr), - _jacP(nullptr), _jacPdisc(nullptr), _jacPF(nullptr), _jacFP(nullptr), _jacInlet(), _hasParDepSurfDiffusion(false), - _analyticJac(true), _jacobianAdDirs(0), _factorizeJacobian(false), _tempState(nullptr), - _initC(0), _initCp(0), _initQ(0), _initState(0), _initStateDot(0) +GeneralRateModel::GeneralRateModel(UnitOpIdx unitOpIdx) + : UnitOperationBase(unitOpIdx), _hasSurfaceDiffusion(0, false), _dynReactionBulk(nullptr), _jacP(nullptr), + _jacPdisc(nullptr), _jacPF(nullptr), _jacFP(nullptr), _jacInlet(), _hasParDepSurfDiffusion(false), + _analyticJac(true), _jacobianAdDirs(0), _factorizeJacobian(false), _tempState(nullptr), _initC(0), _initCp(0), + _initQ(0), _initState(0), _initStateDot(0) { } -template -GeneralRateModel::~GeneralRateModel() CADET_NOEXCEPT +template GeneralRateModel::~GeneralRateModel() CADET_NOEXCEPT { delete[] _tempState; @@ -97,8 +94,7 @@ GeneralRateModel::~GeneralRateModel() CADET_NOEXCEPT delete[] _disc.nBoundBeforeType; } -template -unsigned int GeneralRateModel::numDofs() const CADET_NOEXCEPT +template unsigned int GeneralRateModel::numDofs() const CADET_NOEXCEPT { // Column bulk DOFs: nCol * nComp // Particle DOFs: nCol * nParType particles each having nComp (liquid phase) + sum boundStates (solid phase) DOFs @@ -108,8 +104,7 @@ unsigned int GeneralRateModel::numDofs() const CADET_NOEXCEPT return _disc.nCol * (_disc.nComp * (1 + _disc.nParType)) + _disc.parTypeOffset[_disc.nParType] + _disc.nComp; } -template -unsigned int GeneralRateModel::numPureDofs() const CADET_NOEXCEPT +template unsigned int GeneralRateModel::numPureDofs() const CADET_NOEXCEPT { // Column bulk DOFs: nCol * nComp // Particle DOFs: nCol particles each having nComp (liquid phase) + sum boundStates (solid phase) DOFs @@ -118,9 +113,7 @@ unsigned int GeneralRateModel::numPureDofs() const CADET_NOEXC return _disc.nCol * (_disc.nComp * (1 + _disc.nParType)) + _disc.parTypeOffset[_disc.nParType]; } - -template -bool GeneralRateModel::usesAD() const CADET_NOEXCEPT +template bool GeneralRateModel::usesAD() const CADET_NOEXCEPT { #ifdef CADET_CHECK_ANALYTIC_JACOBIAN // We always need AD if we want to check the analytical Jacobian @@ -131,8 +124,7 @@ bool GeneralRateModel::usesAD() const CADET_NOEXCEPT #endif } -template -void GeneralRateModel::clearParDepSurfDiffusion() +template void GeneralRateModel::clearParDepSurfDiffusion() { if (_singleParDepSurfDiffusion) { @@ -149,7 +141,8 @@ void GeneralRateModel::clearParDepSurfDiffusion() } template -bool GeneralRateModel::configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper) +bool GeneralRateModel::configureModelDiscretization(IParameterProvider& paramProvider, + const IConfigHelper& helper) { // ==== Read discretization _disc.nComp = paramProvider.getInt("NCOMP"); @@ -160,7 +153,8 @@ bool GeneralRateModel::configureModelDiscretization(IParameter paramProvider.pushScope("discretization"); - if (!newNBoundInterface && paramProvider.exists("NBOUND")) // done here and in this order for backwards compatibility + if (!newNBoundInterface && + paramProvider.exists("NBOUND")) // done here and in this order for backwards compatibility nBound = paramProvider.getIntArray("NBOUND"); else { @@ -169,13 +163,15 @@ bool GeneralRateModel::configureModelDiscretization(IParameter paramProvider.pushScope("discretization"); } if (nBound.size() < _disc.nComp) - throw InvalidParameterException("Field NBOUND contains too few elements (NCOMP = " + std::to_string(_disc.nComp) + " required)"); + throw InvalidParameterException( + "Field NBOUND contains too few elements (NCOMP = " + std::to_string(_disc.nComp) + " required)"); _disc.nCol = paramProvider.getInt("NCOL"); const std::vector nParCell = paramProvider.getIntArray("NPAR"); - if (!newNPartypeInterface && paramProvider.exists("NPARTYPE")) // done here and in this order for backwards compatibility + if (!newNPartypeInterface && + paramProvider.exists("NPARTYPE")) // done here and in this order for backwards compatibility _disc.nParType = paramProvider.getInt("NPARTYPE"); else if (newNPartypeInterface) { @@ -190,7 +186,8 @@ bool GeneralRateModel::configureModelDiscretization(IParameter } if ((nParCell.size() > 1) && (nParCell.size() < _disc.nParType)) - throw InvalidParameterException("Field NPAR must have 1 or NPARTYPE (" + std::to_string(_disc.nParType) + ") entries"); + throw InvalidParameterException("Field NPAR must have 1 or NPARTYPE (" + std::to_string(_disc.nParType) + + ") entries"); _disc.nParCell = new unsigned int[_disc.nParType]; if (nParCell.size() < _disc.nParType) @@ -203,7 +200,9 @@ bool GeneralRateModel::configureModelDiscretization(IParameter std::copy_n(nParCell.begin(), _disc.nParType, _disc.nParCell); if ((nBound.size() > _disc.nComp) && (nBound.size() < _disc.nComp * _disc.nParType)) - throw InvalidParameterException("Field NBOUND must have NCOMP (" + std::to_string(_disc.nComp) + ") or NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + ") entries"); + throw InvalidParameterException("Field NBOUND must have NCOMP (" + std::to_string(_disc.nComp) + + ") or NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + + ") entries"); _disc.nBound = new unsigned int[_disc.nComp * _disc.nParType]; if (nBound.size() < _disc.nComp * _disc.nParType) @@ -247,9 +246,10 @@ bool GeneralRateModel::configureModelDiscretization(IParameter unsigned int nTotalParCells = 0; for (unsigned int j = 1; j < _disc.nParType + 1; ++j) { - _disc.parTypeOffset[j] = _disc.parTypeOffset[j-1] + (_disc.nComp + _disc.strideBound[j-1]) * _disc.nParCell[j-1] * _disc.nCol; - _disc.nParCellsBeforeType[j] = _disc.nParCellsBeforeType[j-1] + _disc.nParCell[j-1]; - nTotalParCells += _disc.nParCell[j-1]; + _disc.parTypeOffset[j] = + _disc.parTypeOffset[j - 1] + (_disc.nComp + _disc.strideBound[j - 1]) * _disc.nParCell[j - 1] * _disc.nCol; + _disc.nParCellsBeforeType[j] = _disc.nParCellsBeforeType[j - 1] + _disc.nParCell[j - 1]; + nTotalParCells += _disc.nParCell[j - 1]; } _disc.nParCellsBeforeType[_disc.nParType] = nTotalParCells; @@ -268,7 +268,8 @@ bool GeneralRateModel::configureModelDiscretization(IParameter pdt.resize(_disc.nParType, pdt[0]); } else if (pdt.size() < _disc.nParType) - throw InvalidParameterException("Field PAR_DISC_TYPE contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field PAR_DISC_TYPE contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); for (unsigned int i = 0; i < _disc.nParType; ++i) { @@ -290,7 +291,8 @@ bool GeneralRateModel::configureModelDiscretization(IParameter pg.resize(_disc.nParType, pg[0]); } else if (pg.size() < _disc.nParType) - throw InvalidParameterException("Field PAR_GEOM contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field PAR_GEOM contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); for (unsigned int i = 0; i < _disc.nParType; ++i) { @@ -301,7 +303,8 @@ bool GeneralRateModel::configureModelDiscretization(IParameter else if (pg[i] == "SLAB") _parGeomSurfToVol[i] = SurfVolRatioSlab; else - throw InvalidParameterException("Unknown particle geometry type \"" + pg[i] + "\" at index " + std::to_string(i) + " of field PAR_GEOM"); + throw InvalidParameterException("Unknown particle geometry type \"" + pg[i] + "\" at index " + + std::to_string(i) + " of field PAR_GEOM"); } } paramProvider.pushScope("discretization"); @@ -310,7 +313,8 @@ bool GeneralRateModel::configureModelDiscretization(IParameter { _parDiscVector = paramProvider.getDoubleArray("PAR_DISC_VECTOR"); if (_parDiscVector.size() < nTotalParCells + _disc.nParType) - throw InvalidParameterException("Field PAR_DISC_VECTOR contains too few elements (Sum [NPAR + 1] = " + std::to_string(nTotalParCells + _disc.nParType) + " required)"); + throw InvalidParameterException("Field PAR_DISC_VECTOR contains too few elements (Sum [NPAR + 1] = " + + std::to_string(nTotalParCells + _disc.nParType) + " required)"); } // Determine whether analytic Jacobian should be used but don't set it right now. @@ -332,7 +336,9 @@ bool GeneralRateModel::configureModelDiscretization(IParameter } // Initialize and configure GMRES for solving the Schur-complement - _gmres.initialize(_disc.nCol * _disc.nComp * _disc.nParType, paramProvider.getInt("MAX_KRYLOV"), linalg::toOrthogonalization(paramProvider.getInt("GS_TYPE")), paramProvider.getInt("MAX_RESTARTS")); + _gmres.initialize(_disc.nCol * _disc.nComp * _disc.nParType, paramProvider.getInt("MAX_KRYLOV"), + linalg::toOrthogonalization(paramProvider.getInt("GS_TYPE")), + paramProvider.getInt("MAX_RESTARTS")); _gmres.matrixVectorMultiplier(&schurComplementMultiplierGRM, this); _schurSafety = paramProvider.getDouble("SCHUR_SAFETY"); @@ -342,7 +348,8 @@ bool GeneralRateModel::configureModelDiscretization(IParameter _initQ.resize(nTotalBound); // Determine whether surface diffusion optimization is applied (decreases Jacobian size) - const bool optimizeParticleJacobianBandwidth = paramProvider.exists("OPTIMIZE_PAR_BANDWIDTH") ? paramProvider.getBool("OPTIMIZE_PAR_BANDWIDTH") : true; + const bool optimizeParticleJacobianBandwidth = + paramProvider.exists("OPTIMIZE_PAR_BANDWIDTH") ? paramProvider.getBool("OPTIMIZE_PAR_BANDWIDTH") : true; // Create nonlinear solver for consistent initialization configureNonlinearSolver(paramProvider); @@ -359,7 +366,8 @@ bool GeneralRateModel::configureModelDiscretization(IParameter _singleParDepSurfDiffusion = true; if (!_singleParDepSurfDiffusion && (psdDepNames.size() < _disc.nParType)) - throw InvalidParameterException("Field PAR_SURFDIFFUSION_DEP contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field PAR_SURFDIFFUSION_DEP contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); else if (_singleParDepSurfDiffusion && (psdDepNames.size() != 1)) throw InvalidParameterException("Field PAR_SURFDIFFUSION_DEP requires (only) 1 element"); @@ -378,7 +386,8 @@ bool GeneralRateModel::configureModelDiscretization(IParameter throw InvalidParameterException("Unknown parameter dependence " + psdDepNames[0]); _parDepSurfDiffusion = std::vector(_disc.nParType, pd); - parSurfDiffDepConfSuccess = pd->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound, _disc.boundOffset); + parSurfDiffDepConfSuccess = + pd->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound, _disc.boundOffset); _hasParDepSurfDiffusion = true; } } @@ -395,10 +404,14 @@ bool GeneralRateModel::configureModelDiscretization(IParameter if (!_parDepSurfDiffusion[i]) throw InvalidParameterException("Unknown parameter dependence " + psdDepNames[i]); - parSurfDiffDepConfSuccess = _parDepSurfDiffusion[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, _disc.boundOffset + i * _disc.nComp) && parSurfDiffDepConfSuccess; + parSurfDiffDepConfSuccess = _parDepSurfDiffusion[i]->configureModelDiscretization( + paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, + _disc.boundOffset + i * _disc.nComp) && + parSurfDiffDepConfSuccess; } - _hasParDepSurfDiffusion = std::any_of(_parDepSurfDiffusion.cbegin(), _parDepSurfDiffusion.cend(), [](IParameterStateDependence const* pd) -> bool { return pd; }); + _hasParDepSurfDiffusion = std::any_of(_parDepSurfDiffusion.cbegin(), _parDepSurfDiffusion.cend(), + [](IParameterStateDependence const* pd) -> bool { return pd; }); } } else @@ -444,13 +457,14 @@ bool GeneralRateModel::configureModelDiscretization(IParameter _hasSurfaceDiffusion = std::vector(_disc.nParType, true); } - const bool transportSuccess = _convDispOp.configureModelDiscretization(paramProvider, helper, _disc.nComp, _disc.nCol); + const bool transportSuccess = + _convDispOp.configureModelDiscretization(paramProvider, helper, _disc.nComp, _disc.nCol); // ==== Construct and configure binding model clearBindingModels(); _binding = std::vector(_disc.nParType, nullptr); - std::vector bindModelNames = { "NONE" }; + std::vector bindModelNames = {"NONE"}; if (paramProvider.exists("ADSORPTION_MODEL")) bindModelNames = paramProvider.getStringArray("ADSORPTION_MODEL"); @@ -463,7 +477,8 @@ bool GeneralRateModel::configureModelDiscretization(IParameter } if (!_singleBinding && (bindModelNames.size() < _disc.nParType)) - throw InvalidParameterException("Field ADSORPTION_MODEL contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field ADSORPTION_MODEL contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); else if (_singleBinding && (bindModelNames.size() != 1)) throw InvalidParameterException("Field ADSORPTION_MODEL requires (only) 1 element"); @@ -481,8 +496,12 @@ bool GeneralRateModel::configureModelDiscretization(IParameter if (!_binding[i]) throw InvalidParameterException("Unknown binding model " + bindModelNames[i]); - MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", _singleBinding, i, _disc.nParType == 1, _binding[i]->usesParamProviderInDiscretizationConfig()); - bindingConfSuccess = _binding[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, _disc.boundOffset + i * _disc.nComp) && bindingConfSuccess; + MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", _singleBinding, i, _disc.nParType == 1, + _binding[i]->usesParamProviderInDiscretizationConfig()); + bindingConfSuccess = + _binding[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, + _disc.boundOffset + i * _disc.nComp) && + bindingConfSuccess; } } @@ -500,7 +519,8 @@ bool GeneralRateModel::configureModelDiscretization(IParameter if (_dynReactionBulk->usesParamProviderInDiscretizationConfig()) paramProvider.pushScope("reaction_bulk"); - reactionConfSuccess = _dynReactionBulk->configureModelDiscretization(paramProvider, _disc.nComp, nullptr, nullptr); + reactionConfSuccess = + _dynReactionBulk->configureModelDiscretization(paramProvider, _disc.nComp, nullptr, nullptr); if (_dynReactionBulk->usesParamProviderInDiscretizationConfig()) paramProvider.popScope(); @@ -522,7 +542,8 @@ bool GeneralRateModel::configureModelDiscretization(IParameter } if (!_singleDynReaction && (dynReactModelNames.size() < _disc.nParType)) - throw InvalidParameterException("Field REACTION_MODEL_PARTICLES contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field REACTION_MODEL_PARTICLES contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); else if (_singleDynReaction && (dynReactModelNames.size() != 1)) throw InvalidParameterException("Field REACTION_MODEL_PARTICLES requires (only) 1 element"); @@ -539,8 +560,13 @@ bool GeneralRateModel::configureModelDiscretization(IParameter if (!_dynReaction[i]) throw InvalidParameterException("Unknown dynamic reaction model " + dynReactModelNames[i]); - MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", _singleDynReaction, i, _disc.nParType == 1, _dynReaction[i]->usesParamProviderInDiscretizationConfig()); - reactionConfSuccess = _dynReaction[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, _disc.boundOffset + i * _disc.nComp) && reactionConfSuccess; + MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", _singleDynReaction, i, + _disc.nParType == 1, + _dynReaction[i]->usesParamProviderInDiscretizationConfig()); + reactionConfSuccess = _dynReaction[i]->configureModelDiscretization( + paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, + _disc.boundOffset + i * _disc.nComp) && + reactionConfSuccess; } } } @@ -652,12 +678,15 @@ bool GeneralRateModel::configure(IParameterProvider& paramProv // Read geometry parameters _colPorosity = paramProvider.getDouble("COL_POROSITY"); - _singleParRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parRadius, "PAR_RADIUS", _disc.nParType, _unitOpIdx); - _singleParPorosity = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parPorosity, "PAR_POROSITY", _disc.nParType, _unitOpIdx); + _singleParRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parRadius, "PAR_RADIUS", + _disc.nParType, _unitOpIdx); + _singleParPorosity = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parPorosity, "PAR_POROSITY", + _disc.nParType, _unitOpIdx); // Let PAR_CORERADIUS default to 0.0 for backwards compatibility if (paramProvider.exists("PAR_CORERADIUS")) - _singleParCoreRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parCoreRadius, "PAR_CORERADIUS", _disc.nParType, _unitOpIdx); + _singleParCoreRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parCoreRadius, + "PAR_CORERADIUS", _disc.nParType, _unitOpIdx); else { _singleParCoreRadius = true; @@ -679,7 +708,8 @@ bool GeneralRateModel::configure(IParameterProvider& paramProv // Expand to all axial cells _parTypeVolFrac.resize(_disc.nCol * _disc.nParType, 1.0); for (unsigned int i = 1; i < _disc.nCol; ++i) - std::copy(_parTypeVolFrac.begin(), _parTypeVolFrac.begin() + _disc.nParType, _parTypeVolFrac.begin() + _disc.nParType * i); + std::copy(_parTypeVolFrac.begin(), _parTypeVolFrac.begin() + _disc.nParType, + _parTypeVolFrac.begin() + _disc.nParType * i); } else _axiallyConstantParTypeVolFrac = false; @@ -692,29 +722,39 @@ bool GeneralRateModel::configure(IParameterProvider& paramProv // Check whether all sizes are matched if (_disc.nParType != _parRadius.size()) - throw InvalidParameterException("Number of elements in field PAR_RADIUS does not match number of particle types"); + throw InvalidParameterException( + "Number of elements in field PAR_RADIUS does not match number of particle types"); if (_disc.nParType * _disc.nCol != _parTypeVolFrac.size()) - throw InvalidParameterException("Number of elements in field PAR_TYPE_VOLFRAC does not match number of particle types times number of axial cells"); + throw InvalidParameterException("Number of elements in field PAR_TYPE_VOLFRAC does not match number of " + "particle types times number of axial cells"); if (_disc.nParType != _parPorosity.size()) - throw InvalidParameterException("Number of elements in field PAR_POROSITY does not match number of particle types"); + throw InvalidParameterException( + "Number of elements in field PAR_POROSITY does not match number of particle types"); if (_disc.nParType != _parCoreRadius.size()) - throw InvalidParameterException("Number of elements in field PAR_CORERADIUS does not match number of particle types"); + throw InvalidParameterException( + "Number of elements in field PAR_CORERADIUS does not match number of particle types"); // Check that particle volume fractions sum to 1.0 for (unsigned int i = 0; i < _disc.nCol; ++i) { - const double volFracSum = std::accumulate(_parTypeVolFrac.begin() + i * _disc.nParType, _parTypeVolFrac.begin() + (i+1) * _disc.nParType, 0.0, + const double volFracSum = std::accumulate( + _parTypeVolFrac.begin() + i * _disc.nParType, _parTypeVolFrac.begin() + (i + 1) * _disc.nParType, 0.0, [](double a, const active& b) -> double { return a + static_cast(b); }); if (std::abs(1.0 - volFracSum) > 1e-10) - throw InvalidParameterException("Sum of field PAR_TYPE_VOLFRAC differs from 1.0 (is " + std::to_string(volFracSum) + ") in axial cell " + std::to_string(i)); + throw InvalidParameterException("Sum of field PAR_TYPE_VOLFRAC differs from 1.0 (is " + + std::to_string(volFracSum) + ") in axial cell " + std::to_string(i)); } // Read vectorial parameters (which may also be section dependent; transport) - _filmDiffusionMode = readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _filmDiffusion, "FILM_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); - _parDiffusionMode = readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _parDiffusion, "PAR_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); + _filmDiffusionMode = readAndRegisterMultiplexCompTypeSecParam( + paramProvider, _parameters, _filmDiffusion, "FILM_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); + _parDiffusionMode = readAndRegisterMultiplexCompTypeSecParam( + paramProvider, _parameters, _parDiffusion, "PAR_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); if (paramProvider.exists("PAR_SURFDIFFUSION")) - _parSurfDiffusionMode = readAndRegisterMultiplexBndCompTypeSecParam(paramProvider, _parameters, _parSurfDiffusion, "PAR_SURFDIFFUSION", _disc.nParType, _disc.nComp, _disc.strideBound, _disc.nBound, _unitOpIdx); + _parSurfDiffusionMode = readAndRegisterMultiplexBndCompTypeSecParam( + paramProvider, _parameters, _parSurfDiffusion, "PAR_SURFDIFFUSION", _disc.nParType, _disc.nComp, + _disc.strideBound, _disc.nBound, _unitOpIdx); else { _parSurfDiffusionMode = MultiplexMode::Component; @@ -726,7 +766,8 @@ bool GeneralRateModel::configure(IParameterProvider& paramProv { if (_singleParDepSurfDiffusion && _parDepSurfDiffusion[0]) { - parSurfDiffDepConfSuccess = _parDepSurfDiffusion[0]->configure(paramProvider, _unitOpIdx, ParTypeIndep, "PAR_SURFDIFFUSION"); + parSurfDiffDepConfSuccess = + _parDepSurfDiffusion[0]->configure(paramProvider, _unitOpIdx, ParTypeIndep, "PAR_SURFDIFFUSION"); } else if (!_singleParDepSurfDiffusion) { @@ -735,20 +776,34 @@ bool GeneralRateModel::configure(IParameterProvider& paramProv if (!_parDepSurfDiffusion[i]) continue; - parSurfDiffDepConfSuccess = _parDepSurfDiffusion[i]->configure(paramProvider, _unitOpIdx, i, "PAR_SURFDIFFUSION") && parSurfDiffDepConfSuccess; + parSurfDiffDepConfSuccess = + _parDepSurfDiffusion[i]->configure(paramProvider, _unitOpIdx, i, "PAR_SURFDIFFUSION") && + parSurfDiffDepConfSuccess; } } } - if ((_filmDiffusion.size() < _disc.nComp * _disc.nParType) || (_filmDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) - throw InvalidParameterException("Number of elements in field FILM_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + ")"); - if ((_parDiffusion.size() < _disc.nComp * _disc.nParType) || (_parDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) - throw InvalidParameterException("Number of elements in field PAR_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + ")"); - if ((_parSurfDiffusion.size() < _disc.strideBound[_disc.nParType]) || ((_disc.strideBound[_disc.nParType] > 0) && (_parSurfDiffusion.size() % _disc.strideBound[_disc.nParType] != 0))) - throw InvalidParameterException("Number of elements in field PAR_SURFDIFFUSION is not a positive multiple of NTOTALBND (" + std::to_string(_disc.strideBound[_disc.nParType]) + ")"); + if ((_filmDiffusion.size() < _disc.nComp * _disc.nParType) || + (_filmDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) + throw InvalidParameterException( + "Number of elements in field FILM_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + + std::to_string(_disc.nComp * _disc.nParType) + ")"); + if ((_parDiffusion.size() < _disc.nComp * _disc.nParType) || + (_parDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) + throw InvalidParameterException( + "Number of elements in field PAR_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + + std::to_string(_disc.nComp * _disc.nParType) + ")"); + if ((_parSurfDiffusion.size() < _disc.strideBound[_disc.nParType]) || + ((_disc.strideBound[_disc.nParType] > 0) && + (_parSurfDiffusion.size() % _disc.strideBound[_disc.nParType] != 0))) + throw InvalidParameterException( + "Number of elements in field PAR_SURFDIFFUSION is not a positive multiple of NTOTALBND (" + + std::to_string(_disc.strideBound[_disc.nParType]) + ")"); if (paramProvider.exists("PORE_ACCESSIBILITY")) - _poreAccessFactorMode = readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _poreAccessFactor, "PORE_ACCESSIBILITY", _disc.nParType, _disc.nComp, _unitOpIdx); + _poreAccessFactorMode = + readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _poreAccessFactor, + "PORE_ACCESSIBILITY", _disc.nParType, _disc.nComp, _unitOpIdx); else { _poreAccessFactorMode = MultiplexMode::ComponentType; @@ -756,34 +811,53 @@ bool GeneralRateModel::configure(IParameterProvider& paramProv } if (_disc.nComp * _disc.nParType != _poreAccessFactor.size()) - throw InvalidParameterException("Number of elements in field PORE_ACCESSIBILITY differs from NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + ")"); + throw InvalidParameterException( + "Number of elements in field PORE_ACCESSIBILITY differs from NCOMP * NPARTYPE (" + + std::to_string(_disc.nComp * _disc.nParType) + ")"); // Add parameters to map - _parameters[makeParamId(hashString("COL_POROSITY"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_colPorosity; + _parameters[makeParamId(hashString("COL_POROSITY"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_colPorosity; if (_axiallyConstantParTypeVolFrac) { // Register only the first nParType items for (unsigned int i = 0; i < _disc.nParType; ++i) - _parameters[makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, i, BoundStateIndep, ReactionIndep, SectionIndep)] = &_parTypeVolFrac[i]; + _parameters[makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, i, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_parTypeVolFrac[i]; } else - registerParam2DArray(_parameters, _parTypeVolFrac, [=](bool multi, unsigned cell, unsigned int type) { return makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, type, BoundStateIndep, ReactionIndep, cell); }, _disc.nParType); + registerParam2DArray( + _parameters, _parTypeVolFrac, + [=](bool multi, unsigned cell, unsigned int type) { + return makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, type, BoundStateIndep, + ReactionIndep, cell); + }, + _disc.nParType); // Calculate the particle radial discretization variables (_parCellSize, _parCenterRadius, etc.) updateRadialDisc(); // Register initial conditions parameters - registerParam1DArray(_parameters, _initC, [=](bool multi, unsigned int comp) { return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep); }); + registerParam1DArray(_parameters, _initC, [=](bool multi, unsigned int comp) { + return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep); + }); if (_singleBinding) { for (unsigned int c = 0; c < _disc.nComp; ++c) - _parameters[makeParamId(hashString("INIT_CP"), _unitOpIdx, c, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_initCp[c]; + _parameters[makeParamId(hashString("INIT_CP"), _unitOpIdx, c, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep)] = &_initCp[c]; } else - registerParam2DArray(_parameters, _initCp, [=](bool multi, unsigned int type, unsigned int comp) { return makeParamId(hashString("INIT_CP"), _unitOpIdx, comp, type, BoundStateIndep, ReactionIndep, SectionIndep); }, _disc.nComp); - + registerParam2DArray( + _parameters, _initCp, + [=](bool multi, unsigned int type, unsigned int comp) { + return makeParamId(hashString("INIT_CP"), _unitOpIdx, comp, type, BoundStateIndep, ReactionIndep, + SectionIndep); + }, + _disc.nComp); if (!_binding.empty()) { @@ -827,8 +901,8 @@ bool GeneralRateModel::configure(IParameterProvider& paramProv { for (unsigned int type = 0; type < _disc.nParType; ++type) { - if (!_binding[type] || !_binding[type]->requiresConfiguration()) - continue; + if (!_binding[type] || !_binding[type]->requiresConfiguration()) + continue; MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", type, _disc.nParType == 1, true); bindingConfSuccess = _binding[type]->configure(paramProvider, _unitOpIdx, type) && bindingConfSuccess; @@ -850,18 +924,20 @@ bool GeneralRateModel::configure(IParameterProvider& paramProv if (_dynReaction[0] && _dynReaction[0]->requiresConfiguration()) { MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", true); - dynReactionConfSuccess = _dynReaction[0]->configure(paramProvider, _unitOpIdx, ParTypeIndep) && dynReactionConfSuccess; + dynReactionConfSuccess = + _dynReaction[0]->configure(paramProvider, _unitOpIdx, ParTypeIndep) && dynReactionConfSuccess; } } else { for (unsigned int type = 0; type < _disc.nParType; ++type) { - if (!_dynReaction[type] || !_dynReaction[type]->requiresConfiguration()) - continue; + if (!_dynReaction[type] || !_dynReaction[type]->requiresConfiguration()) + continue; MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", type, _disc.nParType == 1, true); - dynReactionConfSuccess = _dynReaction[type]->configure(paramProvider, _unitOpIdx, type) && dynReactionConfSuccess; + dynReactionConfSuccess = + _dynReaction[type]->configure(paramProvider, _unitOpIdx, type) && dynReactionConfSuccess; } } @@ -880,7 +956,8 @@ unsigned int GeneralRateModel::threadLocalMemorySize() const C lms.fitBlock(_binding[i]->workspaceSize(_disc.nComp, _disc.strideBound[i], _disc.nBound + i * _disc.nComp)); if (_dynReaction[i] && _dynReaction[i]->requiresWorkspace()) - lms.fitBlock(_dynReaction[i]->workspaceSize(_disc.nComp, _disc.strideBound[i], _disc.nBound + i * _disc.nComp)); + lms.fitBlock( + _dynReaction[i]->workspaceSize(_disc.nComp, _disc.strideBound[i], _disc.nBound + i * _disc.nComp)); } if (_dynReactionBulk && _dynReactionBulk->requiresWorkspace()) @@ -946,11 +1023,14 @@ void GeneralRateModel::useAnalyticJacobian(const bool analytic } template -void GeneralRateModel::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) +void GeneralRateModel::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac) { // Setup flux Jacobian blocks at the beginning of the simulation or in case of // section dependent film or particle diffusion coefficients - if ((secIdx == 0) || isSectionDependent(_filmDiffusionMode) || isSectionDependent(_parDiffusionMode) || isSectionDependent(_parSurfDiffusionMode)) + if ((secIdx == 0) || isSectionDependent(_filmDiffusionMode) || isSectionDependent(_parDiffusionMode) || + isSectionDependent(_parSurfDiffusionMode)) assembleOffdiagJac(t, secIdx, simState.vecStateY); Indexer idxr(_disc); @@ -1003,7 +1083,6 @@ void GeneralRateModel::reportSolutionStructure(ISolutionRecord recorder.unitOperationStructure(_unitOpIdx, *this, expr); } - template unsigned int GeneralRateModel::requiredADdirs() const CADET_NOEXCEPT { @@ -1036,7 +1115,9 @@ void GeneralRateModel::prepareADvectors(const AdJacobianParams for (unsigned int pblk = 0; pblk < _disc.nCol; ++pblk) { - ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), adJac.adDirOffset, idxr.strideParBlock(type), lowerParBandwidth, upperParBandwidth, lowerParBandwidth); + ad::prepareAdVectorSeedsForBandMatrix( + adJac.adY + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), adJac.adDirOffset, + idxr.strideParBlock(type), lowerParBandwidth, upperParBandwidth, lowerParBandwidth); } } } @@ -1060,7 +1141,8 @@ void GeneralRateModel::extractJacobianFromAD(active const* con for (unsigned int pblk = 0; pblk < _disc.nCol; ++pblk) { linalg::BandMatrix& jacMat = _jacP[_disc.nCol * type + pblk]; - ad::extractBandedJacobianFromAd(adRes + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), adDirOffset, jacMat.lowerBandwidth(), jacMat); + ad::extractBandedJacobianFromAd(adRes + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), + adDirOffset, jacMat.lowerBandwidth(), jacMat); } } } @@ -1074,11 +1156,13 @@ void GeneralRateModel::extractJacobianFromAD(active const* con * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) */ template -void GeneralRateModel::checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const +void GeneralRateModel::checkAnalyticJacobianAgainstAd(active const* const adRes, + unsigned int adDirOffset) const { Indexer idxr(_disc); - LOG(Debug) << "AD dir offset: " << adDirOffset << " DiagDirCol: " << _convDispOp.jacobian().lowerBandwidth() << " DiagDirPar: " << _jacP[0].lowerBandwidth(); + LOG(Debug) << "AD dir offset: " << adDirOffset << " DiagDirCol: " << _convDispOp.jacobian().lowerBandwidth() + << " DiagDirPar: " << _jacP[0].lowerBandwidth(); // Column const double maxDiffCol = _convDispOp.checkAnalyticJacobianAgainstAd(adRes, adDirOffset); @@ -1090,7 +1174,9 @@ void GeneralRateModel::checkAnalyticJacobianAgainstAd(active c for (unsigned int pblk = 0; pblk < _disc.nCol; ++pblk) { linalg::BandMatrix& jacMat = _jacP[_disc.nCol * type + pblk]; - const double localDiff = ad::compareBandedJacobianWithAd(adRes + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), adDirOffset, jacMat.lowerBandwidth(), jacMat); + const double localDiff = + ad::compareBandedJacobianWithAd(adRes + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), + adDirOffset, jacMat.lowerBandwidth(), jacMat); LOG(Debug) << "-> Par type " << type << " block " << pblk << " diff: " << localDiff; maxDiffPar = std::max(maxDiffPar, localDiff); } @@ -1100,16 +1186,20 @@ void GeneralRateModel::checkAnalyticJacobianAgainstAd(active c #endif template -int GeneralRateModel::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel::residual(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); // Evaluate residual do not compute Jacobian or parameter sensitivities - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } template -int GeneralRateModel::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); @@ -1120,15 +1210,20 @@ int GeneralRateModel::jacobian(const SimulationTime& simTime, // Variable surface diffusion requires reassembly of flux particle Jacobians if (_hasParDepSurfDiffusion) assembleOffdiagJacFluxParticle(simTime.t, simTime.secIdx, simState.vecStateY); - - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } else - return residualWithJacobian(simTime, ConstSimulationState{ simState.vecStateY, nullptr }, nullptr, adJac, threadLocalMem); + return residualWithJacobian(simTime, ConstSimulationState{simState.vecStateY, nullptr}, nullptr, adJac, + threadLocalMem); } template -int GeneralRateModel::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel::residualWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); @@ -1137,8 +1232,10 @@ int GeneralRateModel::residualWithJacobian(const SimulationTim } template -int GeneralRateModel::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, - const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity) +int GeneralRateModel::residual(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, + bool paramSensitivity) { if (updateJacobian) { @@ -1153,7 +1250,8 @@ int GeneralRateModel::residual(const SimulationTime& simTime, { if (paramSensitivity) { - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -1162,7 +1260,8 @@ int GeneralRateModel::residual(const SimulationTime& simTime, return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } else { @@ -1177,9 +1276,11 @@ int GeneralRateModel::residual(const SimulationTime& simTime, // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -1202,15 +1303,18 @@ int GeneralRateModel::residual(const SimulationTime& simTime, // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); // Only do comparison if we have a residuals vector (which is not always the case) if (res) { // Evaluate with analytical Jacobian which is stored in the band matrices - retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); // Compare AD with anaytic Jacobian checkAnalyticJacobianAgainstAd(adJac.adRes, adJac.adDirOffset); @@ -1230,7 +1334,8 @@ int GeneralRateModel::residual(const SimulationTime& simTime, // @todo Check if this is necessary ad::resetAd(adJac.adRes, numDofs()); - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -1239,13 +1344,16 @@ int GeneralRateModel::residual(const SimulationTime& simTime, return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } } template template -int GeneralRateModel::residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel::residualImpl(double t, unsigned int secIdx, StateType const* const y, + double const* const yDot, ResidualType* const res, + util::ThreadLocalStorage& threadLocalMem) { BENCH_START(_timerResidualPar); @@ -1261,7 +1369,8 @@ int GeneralRateModel::residualImpl(double t, unsigned int secI { const unsigned int type = (pblk - 1) / _disc.nCol; const unsigned int par = (pblk - 1) % _disc.nCol; - residualParticle(t, type, par, secIdx, y, yDot, res, threadLocalMem); + residualParticle(t, type, par, secIdx, y, yDot, res, + threadLocalMem); } } CADET_PARFOR_END; @@ -1283,10 +1392,13 @@ int GeneralRateModel::residualImpl(double t, unsigned int secI template template -int GeneralRateModel::residualBulk(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel::residualBulk(double t, unsigned int secIdx, StateType const* yBase, + double const* yDotBase, ResidualType* resBase, + util::ThreadLocalStorage& threadLocalMem) { if (wantRes) - _convDispOp.residual(*this, t, secIdx, yBase, yDotBase, resBase, wantJac, typename ParamSens::enabled()); + _convDispOp.residual(*this, t, secIdx, yBase, yDotBase, resBase, wantJac, + typename ParamSens::enabled()); else _convDispOp.jacobian(*this, t, secIdx, yBase, nullptr, nullptr); @@ -1301,14 +1413,16 @@ int GeneralRateModel::residualBulk(double t, unsigned int secI for (unsigned int col = 0; col < _disc.nCol; ++col, y += idxr.strideColCell(), res += idxr.strideColCell()) { - const ColumnPosition colPos{ (0.5 + static_cast(col)) / static_cast(_disc.nCol), 0.0, 0.0 }; + const ColumnPosition colPos{(0.5 + static_cast(col)) / static_cast(_disc.nCol), 0.0, 0.0}; if (wantRes) _dynReactionBulk->residualLiquidAdd(t, secIdx, colPos, y, res, -1.0, tlmAlloc); if (wantJac) { // static_cast should be sufficient here, but this statement is also analyzed when wantJac = false - _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(y), -1.0, _convDispOp.jacobian().row(col * idxr.strideColCell()), tlmAlloc); + _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(y), -1.0, + _convDispOp.jacobian().row(col * idxr.strideColCell()), + tlmAlloc); } } @@ -1317,25 +1431,31 @@ int GeneralRateModel::residualBulk(double t, unsigned int secI template template -int GeneralRateModel::residualParticle(double t, unsigned int parType, unsigned int colCell, unsigned int secIdx, StateType const* yBase, - double const* yDotBase, ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel::residualParticle(double t, unsigned int parType, unsigned int colCell, + unsigned int secIdx, StateType const* yBase, + double const* yDotBase, ResidualType* resBase, + util::ThreadLocalStorage& threadLocalMem) { Indexer idxr(_disc); // Go to the particle block of the given column cell StateType const* y = yBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colCell}); - double const* yDot = wantRes ? yDotBase + idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colCell }) : nullptr; - ResidualType* res = wantRes ? resBase + idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colCell }) : nullptr; + double const* yDot = + wantRes ? yDotBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colCell}) : nullptr; + ResidualType* res = wantRes ? resBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colCell}) : nullptr; LinearBufferAllocator tlmAlloc = threadLocalMem.get(); // Prepare parameters - active const* const parDiff = getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + parType * _disc.nComp; + active const* const parDiff = + getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + parType * _disc.nComp; // Ordering of particle surface diffusion: // bnd0comp0, bnd0comp1, bnd0comp2, bnd1comp0, bnd1comp1, bnd1comp2 - active const* const parSurfDiff = getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + _disc.nBoundBeforeType[parType]; + active const* const parSurfDiff = + getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + + _disc.nBoundBeforeType[parType]; // Midpoint of current column cell (z coordinate) - needed in externally dependent adsorption kinetic const double z = _convDispOp.relativeCoordinate(colCell); @@ -1361,17 +1481,18 @@ int GeneralRateModel::residualParticle(double t, unsigned int // Loop over particle cells for (unsigned int par = 0; par < _disc.nParCell[parType]; ++par) { - const ColumnPosition colPos{z, 0.0, static_cast(parCenterRadius[par]) / static_cast(_parRadius[parType])}; + const ColumnPosition colPos{ + z, 0.0, static_cast(parCenterRadius[par]) / static_cast(_parRadius[parType])}; // Handle time derivatives, binding, dynamic reactions if (wantRes) - parts::cell::residualKernel( - t, secIdx, colPos, y, yDotBase ? yDot : nullptr, res, jac, cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + t, secIdx, colPos, y, yDotBase ? yDot : nullptr, res, jac, cellResParams, tlmAlloc); else - parts::cell::residualKernel( - t, secIdx, colPos, y, yDotBase ? yDot : nullptr, res, jac, cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + t, secIdx, colPos, y, yDotBase ? yDot : nullptr, res, jac, cellResParams, tlmAlloc); // We still need to handle transport and quasi-stationary reactions @@ -1383,7 +1504,9 @@ int GeneralRateModel::residualParticle(double t, unsigned int for (unsigned int comp = 0; comp < _disc.nComp; ++comp, ++y, ++jac) { const unsigned int nBound = _disc.nBound[_disc.nComp * parType + comp]; - const ParamType invBetaP = (1.0 - static_cast(_parPorosity[parType])) / (static_cast(_poreAccessFactor[_disc.nComp * parType + comp]) * static_cast(_parPorosity[parType])); + const ParamType invBetaP = (1.0 - static_cast(_parPorosity[parType])) / + (static_cast(_poreAccessFactor[_disc.nComp * parType + comp]) * + static_cast(_parPorosity[parType])); const ParamType dp = static_cast(parDiff[comp]); @@ -1392,7 +1515,8 @@ int GeneralRateModel::residualParticle(double t, unsigned int if (cadet_likely(par != 0)) { // Difference between two cell-centers - const ParamType dr = static_cast(parCenterRadius[par - 1]) - static_cast(parCenterRadius[par]); + const ParamType dr = + static_cast(parCenterRadius[par - 1]) - static_cast(parCenterRadius[par]); // Molecular diffusion contribution const ResidualType gradCp = (y[-idxr.strideParShell(parType)] - y[0]) / dr; @@ -1404,37 +1528,37 @@ int GeneralRateModel::residualParticle(double t, unsigned int { if (cadet_unlikely(_parDepSurfDiffusion[parType])) { - const auto dhLocal = static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par]); - const auto dhForeign = static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par - 1]); + const auto dhLocal = + static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par]); + const auto dhForeign = + static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par - 1]); for (unsigned int i = 0; i < nBound; ++i) { // Index explanation: // - comp go back to beginning of liquid phase // + strideParLiquid skip over liquid phase to solid phase - // + offsetBoundComp jump to component comp (skips all bound states of previous components) + // + offsetBoundComp jump to component comp (skips all bound states of previous + // components) // + i go to current bound state - const int bndIdx = idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; + const int bndIdx = + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; const int curIdx = idxr.strideParLiquid() - comp + bndIdx; const ResidualType gradQ = (y[-idxr.strideParShell(parType) + curIdx] - y[curIdx]) / dr; // Evaluate surface diffusion coefficient and apply weighted arithmetic mean const ParamType baseSurfDiff = static_cast(parSurfDiff[bndIdx]); const auto localSurfDiff = _parDepSurfDiffusion[parType]->combinedParameterSolid( - colPos, - baseSurfDiff, - y - static_cast(comp), - y + idxr.strideParLiquid() - comp, - bndIdx - ); + colPos, baseSurfDiff, y - static_cast(comp), y + idxr.strideParLiquid() - comp, + bndIdx); const auto foreignSurfDiff = _parDepSurfDiffusion[parType]->combinedParameterSolid( - {z, 0.0, static_cast(parCenterRadius[par - 1]) / static_cast(_parRadius[parType])}, - baseSurfDiff, - y - idxr.strideParShell(parType) - static_cast(comp), - y - idxr.strideParShell(parType) + idxr.strideParLiquid() - comp, - bndIdx - ); - const auto surfDiff = (localSurfDiff * dhLocal + foreignSurfDiff * dhForeign) / (dhLocal + dhForeign); + {z, 0.0, + static_cast(parCenterRadius[par - 1]) / + static_cast(_parRadius[parType])}, + baseSurfDiff, y - idxr.strideParShell(parType) - static_cast(comp), + y - idxr.strideParShell(parType) + idxr.strideParLiquid() - comp, bndIdx); + const auto surfDiff = + (localSurfDiff * dhLocal + foreignSurfDiff * dhForeign) / (dhLocal + dhForeign); if (wantRes) *res -= outerAreaPerVolume * surfDiff * invBetaP * gradQ; @@ -1448,55 +1572,56 @@ int GeneralRateModel::residualParticle(double t, unsigned int // Liquid phase jac[0] += ouApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j) - jac[-idxr.strideParShell(parType)] += -ouApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j-1) + jac[-idxr.strideParShell(parType)] += + -ouApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j-1) // Solid phase for (unsigned int i = 0; i < nBound; ++i) { // See above for explanation of curIdx value - const int bndIdx = idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; + const int bndIdx = + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; const int curIdx = idxr.strideParLiquid() - comp + bndIdx; - const double gradQ = (static_cast(y[-idxr.strideParShell(parType) + curIdx]) - static_cast(y[curIdx])) / ldr; + const double gradQ = (static_cast(y[-idxr.strideParShell(parType) + curIdx]) - + static_cast(y[curIdx])) / + ldr; const double baseSurfDiff = static_cast(parSurfDiff[bndIdx]); const double localSurfDiff = _parDepSurfDiffusion[parType]->combinedParameterSolid( - colPos, - baseSurfDiff, - reinterpret_cast(y) - static_cast(comp), - reinterpret_cast(y) + idxr.strideParLiquid() - comp, - bndIdx - ); + colPos, baseSurfDiff, reinterpret_cast(y) - static_cast(comp), + reinterpret_cast(y) + idxr.strideParLiquid() - comp, bndIdx); const double foreignSurfDiff = _parDepSurfDiffusion[parType]->combinedParameterSolid( - {z, 0.0, static_cast(parCenterRadius[par - 1]) / static_cast(_parRadius[parType])}, + {z, 0.0, + static_cast(parCenterRadius[par - 1]) / + static_cast(_parRadius[parType])}, baseSurfDiff, - reinterpret_cast(y) - idxr.strideParShell(parType) - static_cast(comp), - reinterpret_cast(y) - idxr.strideParShell(parType) + idxr.strideParLiquid() - comp, - bndIdx - ); + reinterpret_cast(y) - idxr.strideParShell(parType) - + static_cast(comp), + reinterpret_cast(y) - idxr.strideParShell(parType) + + idxr.strideParLiquid() - comp, + bndIdx); const double denom = static_cast(dhLocal) + static_cast(dhForeign); - const double surfDiff = (localSurfDiff * static_cast(dhLocal) + foreignSurfDiff * static_cast(dhForeign)) / denom; + const double surfDiff = (localSurfDiff * static_cast(dhLocal) + + foreignSurfDiff * static_cast(dhForeign)) / + denom; jac[curIdx] += ouApV * localInvBetaP * surfDiff / ldr; // dres / dq_i^(p,j) - jac[-idxr.strideParShell(parType) + curIdx] += -ouApV * localInvBetaP * surfDiff / ldr; // dres / dq_i^(p,j-1) + jac[-idxr.strideParShell(parType) + curIdx] += + -ouApV * localInvBetaP * surfDiff / ldr; // dres / dq_i^(p,j-1) _parDepSurfDiffusion[parType]->analyticJacobianCombinedAddSolid( - colPos, - baseSurfDiff, - reinterpret_cast(y) - static_cast(comp), - reinterpret_cast(y) + idxr.strideParLiquid() - comp, - bndIdx, - -ouApV * localInvBetaP * gradQ * static_cast(dhLocal) / denom, - curIdx, - jac - ); + colPos, baseSurfDiff, reinterpret_cast(y) - static_cast(comp), + reinterpret_cast(y) + idxr.strideParLiquid() - comp, bndIdx, + -ouApV * localInvBetaP * gradQ * static_cast(dhLocal) / denom, curIdx, jac); _parDepSurfDiffusion[parType]->analyticJacobianCombinedAddSolid( - {z, 0.0, static_cast(parCenterRadius[par - 1]) / static_cast(_parRadius[parType])}, + {z, 0.0, + static_cast(parCenterRadius[par - 1]) / + static_cast(_parRadius[parType])}, baseSurfDiff, - reinterpret_cast(y) - static_cast(comp) - idxr.strideParShell(parType), - reinterpret_cast(y) + idxr.strideParLiquid() - comp - idxr.strideParShell(parType), - bndIdx, - -ouApV * localInvBetaP * gradQ * static_cast(dhForeign) / denom, - curIdx - idxr.strideParShell(parType), - jac - ); + reinterpret_cast(y) - static_cast(comp) - + idxr.strideParShell(parType), + reinterpret_cast(y) + idxr.strideParLiquid() - comp - + idxr.strideParShell(parType), + bndIdx, -ouApV * localInvBetaP * gradQ * static_cast(dhForeign) / denom, + curIdx - idxr.strideParShell(parType), jac); } } } @@ -1507,12 +1632,20 @@ int GeneralRateModel::residualParticle(double t, unsigned int // Index explanation: // - comp go back to beginning of liquid phase // + strideParLiquid skip over liquid phase to solid phase - // + offsetBoundComp jump to component comp (skips all bound states of previous components) + // + offsetBoundComp jump to component comp (skips all bound states of previous + // components) // + i go to current bound state - const int curIdx = idxr.strideParLiquid() - comp + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; + const int curIdx = idxr.strideParLiquid() - comp + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + i; const ResidualType gradQ = (y[-idxr.strideParShell(parType) + curIdx] - y[curIdx]) / dr; if (wantRes) - *res -= outerAreaPerVolume * static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i]) * invBetaP * gradQ; + *res -= + outerAreaPerVolume * + static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + i]) * + invBetaP * gradQ; } if (wantJac) @@ -1523,15 +1656,28 @@ int GeneralRateModel::residualParticle(double t, unsigned int // Liquid phase jac[0] += ouApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j) - jac[-idxr.strideParShell(parType)] += -ouApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j-1) + jac[-idxr.strideParShell(parType)] += + -ouApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j-1) // Solid phase for (unsigned int i = 0; i < nBound; ++i) { // See above for explanation of curIdx value - const int curIdx = idxr.strideParLiquid() - comp + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; - jac[curIdx] += ouApV * localInvBetaP * static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i]) / ldr; // dres / dq_i^(p,j) - jac[-idxr.strideParShell(parType) + curIdx] += -ouApV * localInvBetaP * static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i]) / ldr; // dres / dq_i^(p,j-1) + const int curIdx = + idxr.strideParLiquid() - comp + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; + jac[curIdx] += + ouApV * localInvBetaP * + static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + i]) / + ldr; // dres / dq_i^(p,j) + jac[-idxr.strideParShell(parType) + curIdx] += + -ouApV * localInvBetaP * + static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + i]) / + ldr; // dres / dq_i^(p,j-1) } } } @@ -1540,12 +1686,13 @@ int GeneralRateModel::residualParticle(double t, unsigned int { // No surface diffusion // Liquid phase -// const double localInvBetaP = static_cast(invBetaP); + // const double localInvBetaP = static_cast(invBetaP); const double ouApV = static_cast(outerAreaPerVolume); const double ldr = static_cast(dr); jac[0] += ouApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j) - jac[-idxr.strideParShell(parType)] += -ouApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j-1) + jac[-idxr.strideParShell(parType)] += + -ouApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j-1) } } @@ -1554,7 +1701,8 @@ int GeneralRateModel::residualParticle(double t, unsigned int if (cadet_likely(par != _disc.nParCell[parType] - 1)) { // Difference between two cell-centers - const ParamType dr = static_cast(parCenterRadius[par]) - static_cast(parCenterRadius[par + 1]); + const ParamType dr = + static_cast(parCenterRadius[par]) - static_cast(parCenterRadius[par + 1]); // Molecular diffusion contribution const ResidualType gradCp = (y[0] - y[idxr.strideParShell(parType)]) / dr; @@ -1566,33 +1714,32 @@ int GeneralRateModel::residualParticle(double t, unsigned int { if (cadet_unlikely(_parDepSurfDiffusion[parType])) { - const auto dhLocal = static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par]); - const auto dhForeign = static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par + 1]); + const auto dhLocal = + static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par]); + const auto dhForeign = + static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par + 1]); for (unsigned int i = 0; i < nBound; ++i) { // See above for explanation of curIdx value - const int bndIdx = idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; + const int bndIdx = + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; const int curIdx = idxr.strideParLiquid() - comp + bndIdx; const ResidualType gradQ = (y[curIdx] - y[idxr.strideParShell(parType) + curIdx]) / dr; // Evaluate surface diffusion coefficient and apply weighted arithmetic mean const ParamType baseSurfDiff = static_cast(parSurfDiff[bndIdx]); const auto localSurfDiff = _parDepSurfDiffusion[parType]->combinedParameterSolid( - colPos, - baseSurfDiff, - y - static_cast(comp), - y + idxr.strideParLiquid() - comp, - bndIdx - ); + colPos, baseSurfDiff, y - static_cast(comp), y + idxr.strideParLiquid() - comp, + bndIdx); const auto foreignSurfDiff = _parDepSurfDiffusion[parType]->combinedParameterSolid( - {z, 0.0, static_cast(parCenterRadius[par + 1]) / static_cast(_parRadius[parType])}, - baseSurfDiff, - y + idxr.strideParShell(parType) - static_cast(comp), - y + idxr.strideParShell(parType) + idxr.strideParLiquid() - comp, - bndIdx - ); - const auto surfDiff = (localSurfDiff * dhLocal + foreignSurfDiff * dhForeign) / (dhLocal + dhForeign); + {z, 0.0, + static_cast(parCenterRadius[par + 1]) / + static_cast(_parRadius[parType])}, + baseSurfDiff, y + idxr.strideParShell(parType) - static_cast(comp), + y + idxr.strideParShell(parType) + idxr.strideParLiquid() - comp, bndIdx); + const auto surfDiff = + (localSurfDiff * dhLocal + foreignSurfDiff * dhForeign) / (dhLocal + dhForeign); if (wantRes) *res += innerAreaPerVolume * surfDiff * invBetaP * gradQ; @@ -1606,55 +1753,56 @@ int GeneralRateModel::residualParticle(double t, unsigned int // Liquid phase jac[0] += inApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j) - jac[idxr.strideParShell(parType)] += -inApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j+1) + jac[idxr.strideParShell(parType)] += + -inApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j+1) // Solid phase for (unsigned int i = 0; i < nBound; ++i) { // See above for explanation of curIdx value - const int bndIdx = idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; + const int bndIdx = + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; const int curIdx = idxr.strideParLiquid() - comp + bndIdx; - const double gradQ = (static_cast(y[curIdx]) - static_cast(y[idxr.strideParShell(parType) + curIdx])) / ldr; + const double gradQ = (static_cast(y[curIdx]) - + static_cast(y[idxr.strideParShell(parType) + curIdx])) / + ldr; const double baseSurfDiff = static_cast(parSurfDiff[bndIdx]); const double localSurfDiff = _parDepSurfDiffusion[parType]->combinedParameterSolid( - colPos, - baseSurfDiff, - reinterpret_cast(y) - static_cast(comp), - reinterpret_cast(y) + idxr.strideParLiquid() - comp, - bndIdx - ); + colPos, baseSurfDiff, reinterpret_cast(y) - static_cast(comp), + reinterpret_cast(y) + idxr.strideParLiquid() - comp, bndIdx); const double foreignSurfDiff = _parDepSurfDiffusion[parType]->combinedParameterSolid( - {z, 0.0, static_cast(parCenterRadius[par + 1]) / static_cast(_parRadius[parType])}, + {z, 0.0, + static_cast(parCenterRadius[par + 1]) / + static_cast(_parRadius[parType])}, baseSurfDiff, - reinterpret_cast(y) + idxr.strideParShell(parType) - static_cast(comp), - reinterpret_cast(y) + idxr.strideParShell(parType) + idxr.strideParLiquid() - comp, - bndIdx - ); + reinterpret_cast(y) + idxr.strideParShell(parType) - + static_cast(comp), + reinterpret_cast(y) + idxr.strideParShell(parType) + + idxr.strideParLiquid() - comp, + bndIdx); const double denom = static_cast(dhLocal) + static_cast(dhForeign); - const double lsd = (localSurfDiff * static_cast(dhLocal) + foreignSurfDiff * static_cast(dhForeign)) / denom; + const double lsd = (localSurfDiff * static_cast(dhLocal) + + foreignSurfDiff * static_cast(dhForeign)) / + denom; jac[curIdx] += inApV * localInvBetaP * lsd / ldr; // dres / dq_i^(p,j) - jac[idxr.strideParShell(parType) + curIdx] += -inApV * localInvBetaP * lsd / ldr; // dres / dq_i^(p,j-1) + jac[idxr.strideParShell(parType) + curIdx] += + -inApV * localInvBetaP * lsd / ldr; // dres / dq_i^(p,j-1) _parDepSurfDiffusion[parType]->analyticJacobianCombinedAddSolid( - colPos, - baseSurfDiff, - reinterpret_cast(y) - static_cast(comp), - reinterpret_cast(y) + idxr.strideParLiquid() - comp, - bndIdx, - inApV * localInvBetaP * gradQ * static_cast(dhLocal) / denom, - curIdx, - jac - ); + colPos, baseSurfDiff, reinterpret_cast(y) - static_cast(comp), + reinterpret_cast(y) + idxr.strideParLiquid() - comp, bndIdx, + inApV * localInvBetaP * gradQ * static_cast(dhLocal) / denom, curIdx, jac); _parDepSurfDiffusion[parType]->analyticJacobianCombinedAddSolid( - {z, 0.0, static_cast(parCenterRadius[par + 1]) / static_cast(_parRadius[parType])}, + {z, 0.0, + static_cast(parCenterRadius[par + 1]) / + static_cast(_parRadius[parType])}, baseSurfDiff, - reinterpret_cast(y) - static_cast(comp) + idxr.strideParShell(parType), - reinterpret_cast(y) + idxr.strideParLiquid() - comp + idxr.strideParShell(parType), - bndIdx, - inApV * localInvBetaP * gradQ * static_cast(dhForeign) / denom, - curIdx + idxr.strideParShell(parType), - jac - ); + reinterpret_cast(y) - static_cast(comp) + + idxr.strideParShell(parType), + reinterpret_cast(y) + idxr.strideParLiquid() - comp + + idxr.strideParShell(parType), + bndIdx, inApV * localInvBetaP * gradQ * static_cast(dhForeign) / denom, + curIdx + idxr.strideParShell(parType), jac); } } } @@ -1663,10 +1811,17 @@ int GeneralRateModel::residualParticle(double t, unsigned int for (unsigned int i = 0; i < nBound; ++i) { // See above for explanation of curIdx value - const unsigned int curIdx = idxr.strideParLiquid() - comp + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; + const unsigned int curIdx = + idxr.strideParLiquid() - comp + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; const ResidualType gradQ = (y[curIdx] - y[idxr.strideParShell(parType) + curIdx]) / dr; if (wantRes) - *res += innerAreaPerVolume * static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i]) * invBetaP * gradQ; + *res += + innerAreaPerVolume * + static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + i]) * + invBetaP * gradQ; } if (wantJac) @@ -1677,15 +1832,28 @@ int GeneralRateModel::residualParticle(double t, unsigned int // Liquid phase jac[0] += inApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j) - jac[idxr.strideParShell(parType)] += -inApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j+1) + jac[idxr.strideParShell(parType)] += + -inApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j+1) // Solid phase for (unsigned int i = 0; i < nBound; ++i) { // See above for explanation of curIdx value - const int curIdx = idxr.strideParLiquid() - comp + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; - jac[curIdx] += inApV * localInvBetaP * static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i]) / ldr; // dres / dq_i^(p,j) - jac[idxr.strideParShell(parType) + curIdx] += -inApV * localInvBetaP * static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i]) / ldr; // dres / dq_i^(p,j-1) + const int curIdx = + idxr.strideParLiquid() - comp + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; + jac[curIdx] += + inApV * localInvBetaP * + static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + i]) / + ldr; // dres / dq_i^(p,j) + jac[idxr.strideParShell(parType) + curIdx] += + -inApV * localInvBetaP * + static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + i]) / + ldr; // dres / dq_i^(p,j-1) } } } @@ -1694,12 +1862,13 @@ int GeneralRateModel::residualParticle(double t, unsigned int { // No surface diffusion // Liquid phase -// const double localInvBetaP = static_cast(invBetaP); + // const double localInvBetaP = static_cast(invBetaP); const double inApV = static_cast(innerAreaPerVolume); const double ldr = static_cast(dr); jac[0] += inApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j) - jac[idxr.strideParShell(parType)] += -inApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j+1) + jac[idxr.strideParShell(parType)] += + -inApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j+1) } } @@ -1721,32 +1890,31 @@ int GeneralRateModel::residualParticle(double t, unsigned int if (cadet_likely(par != 0)) { // Difference between two cell-centers - const ParamType dr = static_cast(parCenterRadius[par - 1]) - static_cast(parCenterRadius[par]); + const ParamType dr = + static_cast(parCenterRadius[par - 1]) - static_cast(parCenterRadius[par]); const ResidualType gradQ = (y[-idxr.strideParShell(parType)] - y[0]) / dr; if (cadet_unlikely(_parDepSurfDiffusion[parType])) { - const auto dhLocal = static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par]); - const auto dhForeign = static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par - 1]); + const auto dhLocal = + static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par]); + const auto dhForeign = + static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par - 1]); // Evaluate surface diffusion coefficient and apply weighted arithmetic mean const ParamType baseSurfDiff = static_cast(parSurfDiff[bnd]); const auto localSurfDiff = _parDepSurfDiffusion[parType]->combinedParameterSolid( - colPos, - baseSurfDiff, - y - static_cast(bnd) - idxr.strideParLiquid(), - y - static_cast(bnd), - bnd - ); + colPos, baseSurfDiff, y - static_cast(bnd) - idxr.strideParLiquid(), + y - static_cast(bnd), bnd); const auto foreignSurfDiff = _parDepSurfDiffusion[parType]->combinedParameterSolid( - {z, 0.0, static_cast(parCenterRadius[par - 1]) / static_cast(_parRadius[parType])}, + {z, 0.0, + static_cast(parCenterRadius[par - 1]) / static_cast(_parRadius[parType])}, baseSurfDiff, y - static_cast(bnd) - idxr.strideParLiquid() - idxr.strideParShell(parType), - y - static_cast(bnd) - idxr.strideParShell(parType), - bnd - ); - const auto surfDiff = (localSurfDiff * dhLocal + foreignSurfDiff * dhForeign) / (dhLocal + dhForeign); + y - static_cast(bnd) - idxr.strideParShell(parType), bnd); + const auto surfDiff = + (localSurfDiff * dhLocal + foreignSurfDiff * dhForeign) / (dhLocal + dhForeign); if (wantRes) *res -= outerAreaPerVolume * surfDiff * gradQ; @@ -1759,29 +1927,25 @@ int GeneralRateModel::residualParticle(double t, unsigned int const double denom = static_cast(dhLocal) + static_cast(dhForeign); const double lsd = static_cast(surfDiff); - jac[0] += ouApV * lsd / ldr; // dres / dq_i^(p,j) + jac[0] += ouApV * lsd / ldr; // dres / dq_i^(p,j) jac[-idxr.strideParShell(parType)] += -ouApV * lsd / ldr; // dres / dq_i^(p,j-1) _parDepSurfDiffusion[parType]->analyticJacobianCombinedAddSolid( - colPos, - static_cast(baseSurfDiff), + colPos, static_cast(baseSurfDiff), reinterpret_cast(y) - static_cast(bnd) - idxr.strideParLiquid(), - reinterpret_cast(y) - static_cast(bnd), - bnd, - -ouApV * static_cast(gradQ) * static_cast(dhLocal) / denom, - 0, - jac - ); + reinterpret_cast(y) - static_cast(bnd), bnd, + -ouApV * static_cast(gradQ) * static_cast(dhLocal) / denom, 0, jac); _parDepSurfDiffusion[parType]->analyticJacobianCombinedAddSolid( - {z, 0.0, static_cast(parCenterRadius[par - 1]) / static_cast(_parRadius[parType])}, + {z, 0.0, + static_cast(parCenterRadius[par - 1]) / + static_cast(_parRadius[parType])}, static_cast(baseSurfDiff), - reinterpret_cast(y) - static_cast(bnd) - idxr.strideParLiquid() - idxr.strideParShell(parType), - reinterpret_cast(y) - static_cast(bnd) - idxr.strideParShell(parType), - bnd, - -ouApV * static_cast(gradQ) * static_cast(dhForeign) / denom, - -idxr.strideParShell(parType), - jac - ); + reinterpret_cast(y) - static_cast(bnd) - idxr.strideParLiquid() - + idxr.strideParShell(parType), + reinterpret_cast(y) - static_cast(bnd) - + idxr.strideParShell(parType), + bnd, -ouApV * static_cast(gradQ) * static_cast(dhForeign) / denom, + -idxr.strideParShell(parType), jac); } } else @@ -1795,7 +1959,8 @@ int GeneralRateModel::residualParticle(double t, unsigned int const double ldr = static_cast(dr); jac[0] += ouApV * static_cast(parSurfDiff[bnd]) / ldr; // dres / dq_i^(p,j) - jac[-idxr.strideParShell(parType)] += -ouApV * static_cast(parSurfDiff[bnd]) / ldr; // dres / dq_i^(p,j-1) + jac[-idxr.strideParShell(parType)] += + -ouApV * static_cast(parSurfDiff[bnd]) / ldr; // dres / dq_i^(p,j-1) } } } @@ -1805,32 +1970,31 @@ int GeneralRateModel::residualParticle(double t, unsigned int if (cadet_likely(par != _disc.nParCell[parType] - 1)) { // Difference between two cell-centers - const ParamType dr = static_cast(parCenterRadius[par]) - static_cast(parCenterRadius[par + 1]); + const ParamType dr = + static_cast(parCenterRadius[par]) - static_cast(parCenterRadius[par + 1]); const ResidualType gradQ = (y[0] - y[idxr.strideParShell(parType)]) / dr; if (cadet_unlikely(_parDepSurfDiffusion[parType])) { - const auto dhLocal = static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par]); - const auto dhForeign = static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par + 1]); + const auto dhLocal = + static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par]); + const auto dhForeign = + static_cast(_parCellSize[_disc.nParCellsBeforeType[parType] + par + 1]); // Evaluate surface diffusion coefficient and apply weighted arithmetic mean const ParamType baseSurfDiff = static_cast(parSurfDiff[bnd]); const auto localSurfDiff = _parDepSurfDiffusion[parType]->combinedParameterSolid( - colPos, - baseSurfDiff, - y - static_cast(bnd) - idxr.strideParLiquid(), - y - static_cast(bnd), - bnd - ); + colPos, baseSurfDiff, y - static_cast(bnd) - idxr.strideParLiquid(), + y - static_cast(bnd), bnd); const auto foreignSurfDiff = _parDepSurfDiffusion[parType]->combinedParameterSolid( - {z, 0.0, static_cast(parCenterRadius[par + 1]) / static_cast(_parRadius[parType])}, + {z, 0.0, + static_cast(parCenterRadius[par + 1]) / static_cast(_parRadius[parType])}, baseSurfDiff, y - static_cast(bnd) - idxr.strideParLiquid() + idxr.strideParShell(parType), - y - static_cast(bnd) + idxr.strideParShell(parType), - bnd - ); - const auto surfDiff = (localSurfDiff * dhLocal + foreignSurfDiff * dhForeign) / (dhLocal + dhForeign); + y - static_cast(bnd) + idxr.strideParShell(parType), bnd); + const auto surfDiff = + (localSurfDiff * dhLocal + foreignSurfDiff * dhForeign) / (dhLocal + dhForeign); if (wantRes) *res += innerAreaPerVolume * surfDiff * gradQ; @@ -1843,29 +2007,25 @@ int GeneralRateModel::residualParticle(double t, unsigned int const double denom = static_cast(dhLocal) + static_cast(dhForeign); const double lsd = static_cast(surfDiff); - jac[0] += inApV * lsd / ldr; // dres / dq_i^(p,j) + jac[0] += inApV * lsd / ldr; // dres / dq_i^(p,j) jac[idxr.strideParShell(parType)] += -inApV * lsd / ldr; // dres / dq_i^(p,j-1) _parDepSurfDiffusion[parType]->analyticJacobianCombinedAddSolid( - colPos, - static_cast(baseSurfDiff), + colPos, static_cast(baseSurfDiff), reinterpret_cast(y) - static_cast(bnd) - idxr.strideParLiquid(), - reinterpret_cast(y) - static_cast(bnd), - bnd, - inApV * static_cast(gradQ) * static_cast(dhLocal) / denom, - 0, - jac - ); + reinterpret_cast(y) - static_cast(bnd), bnd, + inApV * static_cast(gradQ) * static_cast(dhLocal) / denom, 0, jac); _parDepSurfDiffusion[parType]->analyticJacobianCombinedAddSolid( - {z, 0.0, static_cast(parCenterRadius[par + 1]) / static_cast(_parRadius[parType])}, + {z, 0.0, + static_cast(parCenterRadius[par + 1]) / + static_cast(_parRadius[parType])}, static_cast(baseSurfDiff), - reinterpret_cast(y) - static_cast(bnd) - idxr.strideParLiquid() + idxr.strideParShell(parType), - reinterpret_cast(y) - static_cast(bnd) + idxr.strideParShell(parType), - bnd, - inApV * static_cast(gradQ) * static_cast(dhForeign) / denom, - idxr.strideParShell(parType), - jac - ); + reinterpret_cast(y) - static_cast(bnd) - idxr.strideParLiquid() + + idxr.strideParShell(parType), + reinterpret_cast(y) - static_cast(bnd) + + idxr.strideParShell(parType), + bnd, inApV * static_cast(gradQ) * static_cast(dhForeign) / denom, + idxr.strideParShell(parType), jac); } } else @@ -1879,7 +2039,8 @@ int GeneralRateModel::residualParticle(double t, unsigned int const double ldr = static_cast(dr); jac[0] += inApV * static_cast(parSurfDiff[bnd]) / ldr; // dres / dq_i^(p,j) - jac[idxr.strideParShell(parType)] += -inApV * static_cast(parSurfDiff[bnd]) / ldr; // dres / dq_i^(p,j-1) + jac[idxr.strideParShell(parType)] += + -inApV * static_cast(parSurfDiff[bnd]) / ldr; // dres / dq_i^(p,j-1) } } } @@ -1902,7 +2063,8 @@ int GeneralRateModel::residualParticle(double t, unsigned int template template -int GeneralRateModel::residualFlux(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, ResidualType* resBase) +int GeneralRateModel::residualFlux(double t, unsigned int secIdx, StateType const* yBase, + double const* yDotBase, ResidualType* resBase) { Indexer idxr(_disc); @@ -1935,11 +2097,14 @@ int GeneralRateModel::residualFlux(double t, unsigned int secI // Ordering of diffusion: // sec0type0comp0, sec0type0comp1, sec0type0comp2, sec0type1comp0, sec0type1comp1, sec0type1comp2, // sec1type0comp0, sec1type0comp1, sec1type0comp2, sec1type1comp0, sec1type1comp1, sec1type1comp2, ... - active const* const filmDiff = getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; - active const* const parDiff = getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const filmDiff = + getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const parDiff = + getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; const ParamType surfaceToVolumeRatio = _parGeomSurfToVol[type] / static_cast(_parRadius[type]); - const ParamType outerAreaPerVolume = static_cast(_parOuterSurfAreaPerVolume[_disc.nParCellsBeforeType[type]]); + const ParamType outerAreaPerVolume = + static_cast(_parOuterSurfAreaPerVolume[_disc.nParCellsBeforeType[type]]); const ParamType jacCF_val = invBetaC * surfaceToVolumeRatio; const ParamType jacPF_val = -outerAreaPerVolume / epsP; @@ -1947,9 +2112,13 @@ int GeneralRateModel::residualFlux(double t, unsigned int secI // Discretized film diffusion kf for finite volumes if (cadet_likely(_colParBoundaryOrder == 2)) { - const ParamType absOuterShellHalfRadius = 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); + const ParamType absOuterShellHalfRadius = + 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - kf_FV[comp] = 1.0 / (absOuterShellHalfRadius / epsP / static_cast(_poreAccessFactor[type * _disc.nComp + comp]) / static_cast(parDiff[comp]) + 1.0 / static_cast(filmDiff[comp])); + kf_FV[comp] = 1.0 / (absOuterShellHalfRadius / epsP / + static_cast(_poreAccessFactor[type * _disc.nComp + comp]) / + static_cast(parDiff[comp]) + + 1.0 / static_cast(filmDiff[comp])); } else { @@ -1961,7 +2130,8 @@ int GeneralRateModel::residualFlux(double t, unsigned int secI for (unsigned int i = 0; i < _disc.nCol * _disc.nComp; ++i) { const unsigned int colCell = i / _disc.nComp; - resCol[i] += jacCF_val * static_cast(_parTypeVolFrac[type + colCell * _disc.nParType]) * yFluxType[i]; + resCol[i] += + jacCF_val * static_cast(_parTypeVolFrac[type + colCell * _disc.nParType]) * yFluxType[i]; } // J_{f,0} block, adds bulk volume state c_i to flux equation @@ -1980,7 +2150,8 @@ int GeneralRateModel::residualFlux(double t, unsigned int secI for (unsigned int comp = 0; comp < _disc.nComp; ++comp) { const unsigned int eq = pblk * idxr.strideColCell() + comp * idxr.strideColComp(); - resParType[pblk * idxr.strideParBlock(type) + comp] += jacPF_val / static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * yFluxType[eq]; + resParType[pblk * idxr.strideParBlock(type) + comp] += + jacPF_val / static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * yFluxType[eq]; } } @@ -1994,23 +2165,33 @@ int GeneralRateModel::residualFlux(double t, unsigned int secI } } - if (cadet_unlikely(_hasSurfaceDiffusion[type] && _binding[type]->hasQuasiStationaryReactions() && (_disc.nParCell[type] > 1))) + if (cadet_unlikely(_hasSurfaceDiffusion[type] && _binding[type]->hasQuasiStationaryReactions() && + (_disc.nParCell[type] > 1))) { int const* const qsReaction = _binding[type]->reactionQuasiStationarity(); // Ordering of particle surface diffusion: // bnd0comp0, bnd0comp1, bnd0comp2, bnd1comp0, bnd1comp1, bnd1comp2 - active const* const parSurfDiff = getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + _disc.nBoundBeforeType[type]; + active const* const parSurfDiff = + getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + + _disc.nBoundBeforeType[type]; active const* const parCenterRadius = _parCenterRadius.data() + _disc.nParCellsBeforeType[type]; - const ParamType absOuterShellHalfRadius = 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); + const ParamType absOuterShellHalfRadius = + 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - kf_FV[comp] = (1.0 - static_cast(_parPorosity[type])) / (1.0 + epsP * static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * static_cast(parDiff[comp]) / (absOuterShellHalfRadius * static_cast(filmDiff[comp]))); + kf_FV[comp] = (1.0 - static_cast(_parPorosity[type])) / + (1.0 + epsP * static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * + static_cast(parDiff[comp]) / + (absOuterShellHalfRadius * static_cast(filmDiff[comp]))); for (unsigned int pblk = 0; pblk < _disc.nCol; ++pblk) { - const ColumnPosition colPos{(0.5 + static_cast(pblk)) / static_cast(_disc.nCol), 0.0, static_cast(parCenterRadius[0]) / static_cast(_parRadius[type])}; - const ParamType dr = static_cast(parCenterRadius[0]) - static_cast(parCenterRadius[1]); + const ColumnPosition colPos{(0.5 + static_cast(pblk)) / static_cast(_disc.nCol), 0.0, + static_cast(parCenterRadius[0]) / + static_cast(_parRadius[type])}; + const ParamType dr = + static_cast(parCenterRadius[0]) - static_cast(parCenterRadius[1]); for (unsigned int comp = 0; comp < _disc.nComp; ++comp) { @@ -2027,15 +2208,16 @@ int GeneralRateModel::residualFlux(double t, unsigned int secI // Evaluate surface diffusion coefficient and apply weighted arithmetic mean const int curIdx = pblk * idxr.strideParBlock(type) + idxr.strideParLiquid() + idxBnd; - const auto localSurfDiff = cadet_unlikely(_parDepSurfDiffusion[type]) ? _parDepSurfDiffusion[type]->combinedParameterSolid( - colPos, - static_cast(parSurfDiff[idxBnd]), - yParType + pblk * idxr.strideParBlock(type), - yParType + pblk * idxr.strideParBlock(type) + idxr.strideParLiquid(), - idxBnd - ) : static_cast(parSurfDiff[idxBnd]); - - const ResidualType gradQ = (yParType[curIdx] - yParType[curIdx + idxr.strideParShell(type)]) / dr; + const auto localSurfDiff = + cadet_unlikely(_parDepSurfDiffusion[type]) + ? _parDepSurfDiffusion[type]->combinedParameterSolid( + colPos, static_cast(parSurfDiff[idxBnd]), + yParType + pblk * idxr.strideParBlock(type), + yParType + pblk * idxr.strideParBlock(type) + idxr.strideParLiquid(), idxBnd) + : static_cast(parSurfDiff[idxBnd]); + + const ResidualType gradQ = + (yParType[curIdx] - yParType[curIdx + idxr.strideParShell(type)]) / dr; resFluxType[eq] -= kf_FV[comp] * localSurfDiff * gradQ; } } @@ -2048,20 +2230,20 @@ int GeneralRateModel::residualFlux(double t, unsigned int secI } template -parts::cell::CellParameters GeneralRateModel::makeCellResidualParams(unsigned int parType, int const* qsReaction) const +parts::cell::CellParameters GeneralRateModel::makeCellResidualParams(unsigned int parType, + int const* qsReaction) const { - return parts::cell::CellParameters - { - _disc.nComp, - _disc.nBound + _disc.nComp * parType, - _disc.boundOffset + _disc.nComp * parType, - _disc.strideBound[parType], - qsReaction, - _parPorosity[parType], - _poreAccessFactor.data() + _disc.nComp * parType, - _binding[parType], - (_dynReaction[parType] && (_dynReaction[parType]->numReactionsCombined() > 0)) ? _dynReaction[parType] : nullptr - }; + return parts::cell::CellParameters{_disc.nComp, + _disc.nBound + _disc.nComp * parType, + _disc.boundOffset + _disc.nComp * parType, + _disc.strideBound[parType], + qsReaction, + _parPorosity[parType], + _poreAccessFactor.data() + _disc.nComp * parType, + _binding[parType], + (_dynReaction[parType] && (_dynReaction[parType]->numReactionsCombined() > 0)) + ? _dynReaction[parType] + : nullptr}; } /** @@ -2101,11 +2283,14 @@ void GeneralRateModel::assembleOffdiagJac(double t, unsigned i // Ordering of diffusion: // sec0type0comp0, sec0type0comp1, sec0type0comp2, sec0type1comp0, sec0type1comp1, sec0type1comp2, // sec1type0comp0, sec1type0comp1, sec1type0comp2, sec1type1comp0, sec1type1comp1, sec1type1comp2, ... - active const* const filmDiff = getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; - active const* const parDiff = getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const filmDiff = + getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const parDiff = + getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; const double surfaceToVolumeRatio = _parGeomSurfToVol[type] / static_cast(_parRadius[type]); - const double outerAreaPerVolume = static_cast(_parOuterSurfAreaPerVolume[_disc.nParCellsBeforeType[type]]); + const double outerAreaPerVolume = + static_cast(_parOuterSurfAreaPerVolume[_disc.nParCellsBeforeType[type]]); const double jacCF_val = invBetaC * surfaceToVolumeRatio; const double jacPF_val = -outerAreaPerVolume / epsP; @@ -2113,9 +2298,13 @@ void GeneralRateModel::assembleOffdiagJac(double t, unsigned i // Discretized film diffusion kf for finite volumes if (cadet_likely(_colParBoundaryOrder == 2)) { - const double absOuterShellHalfRadius = 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); + const double absOuterShellHalfRadius = + 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - kf_FV[comp] = 1.0 / (absOuterShellHalfRadius / epsP / static_cast(_poreAccessFactor[type * _disc.nComp + comp]) / static_cast(parDiff[comp]) + 1.0 / static_cast(filmDiff[comp])); + kf_FV[comp] = 1.0 / (absOuterShellHalfRadius / epsP / + static_cast(_poreAccessFactor[type * _disc.nComp + comp]) / + static_cast(parDiff[comp]) + + 1.0 / static_cast(filmDiff[comp])); } else { @@ -2129,7 +2318,8 @@ void GeneralRateModel::assembleOffdiagJac(double t, unsigned i const unsigned int colCell = eq / _disc.nComp; // Main diagonal corresponds to j_{f,i} (flux) state variable - _jacCF.addElement(eq, eq + typeOffset, jacCF_val * static_cast(_parTypeVolFrac[type + colCell * _disc.nParType])); + _jacCF.addElement(eq, eq + typeOffset, + jacCF_val * static_cast(_parTypeVolFrac[type + colCell * _disc.nParType])); } // J_{f,0} block, adds bulk volume state c_i to flux equation @@ -2165,22 +2355,31 @@ void GeneralRateModel::assembleOffdiagJac(double t, unsigned i } } - if (cadet_unlikely(_hasSurfaceDiffusion[type] && _binding[type]->hasQuasiStationaryReactions() && (_disc.nParCell[type] > 1))) + if (cadet_unlikely(_hasSurfaceDiffusion[type] && _binding[type]->hasQuasiStationaryReactions() && + (_disc.nParCell[type] > 1))) { int const* const qsReaction = _binding[type]->reactionQuasiStationarity(); // Ordering of particle surface diffusion: // bnd0comp0, bnd0comp1, bnd0comp2, bnd1comp0, bnd1comp1, bnd1comp2 - active const* const parSurfDiff = getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + _disc.nBoundBeforeType[type]; + active const* const parSurfDiff = + getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + + _disc.nBoundBeforeType[type]; active const* const parCenterRadius = _parCenterRadius.data() + _disc.nParCellsBeforeType[type]; - const double absOuterShellHalfRadius = 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); + const double absOuterShellHalfRadius = + 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - kf_FV[comp] = (1.0 - static_cast(_parPorosity[type])) / (1.0 + epsP * static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * static_cast(parDiff[comp]) / (absOuterShellHalfRadius * static_cast(filmDiff[comp]))); + kf_FV[comp] = (1.0 - static_cast(_parPorosity[type])) / + (1.0 + epsP * static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * + static_cast(parDiff[comp]) / + (absOuterShellHalfRadius * static_cast(filmDiff[comp]))); for (unsigned int pblk = 0; pblk < _disc.nCol; ++pblk) { - const ColumnPosition colPos{(0.5 + static_cast(pblk)) / static_cast(_disc.nCol), 0.0, static_cast(parCenterRadius[0]) / static_cast(_parRadius[type])}; + const ColumnPosition colPos{(0.5 + static_cast(pblk)) / static_cast(_disc.nCol), 0.0, + static_cast(parCenterRadius[0]) / + static_cast(_parRadius[type])}; const double dr = static_cast(parCenterRadius[0]) - static_cast(parCenterRadius[1]); double const* const yCell = vecStateY + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}); @@ -2201,12 +2400,8 @@ void GeneralRateModel::assembleOffdiagJac(double t, unsigned i if (cadet_unlikely(_parDepSurfDiffusion[type])) { const double localSurfDiff = _parDepSurfDiffusion[type]->combinedParameterSolid( - colPos, - static_cast(parSurfDiff[idxBnd]), - yCell, - yCell + idxr.strideParLiquid(), - idxBnd - ); + colPos, static_cast(parSurfDiff[idxBnd]), yCell, yCell + idxr.strideParLiquid(), + idxBnd); const double v = kf_FV[comp] * localSurfDiff / dr; const int curIdx = idxr.strideParLiquid() + idxBnd; @@ -2216,16 +2411,8 @@ void GeneralRateModel::assembleOffdiagJac(double t, unsigned i const double gradQ = (yCell[curIdx] - yCell[curIdx + idxr.strideParShell(type)]) / dr; _parDepSurfDiffusion[type]->analyticJacobianCombinedAddSolid( - colPos, - static_cast(parSurfDiff[idxBnd]), - yCell, - yCell + idxr.strideParLiquid(), - idxBnd, - -kf_FV[comp] * gradQ, - 0, - eq, - jacFPtype[pblk] - ); + colPos, static_cast(parSurfDiff[idxBnd]), yCell, yCell + idxr.strideParLiquid(), + idxBnd, -kf_FV[comp] * gradQ, 0, eq, jacFPtype[pblk]); } else { @@ -2253,14 +2440,15 @@ void GeneralRateModel::assembleOffdiagJac(double t, unsigned i * @param [in] vecStateY Current state vector */ template -void GeneralRateModel::assembleOffdiagJacFluxParticle(double t, unsigned int secIdx, double const* vecStateY) +void GeneralRateModel::assembleOffdiagJacFluxParticle(double t, unsigned int secIdx, + double const* vecStateY) { for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nParType; ++pblk) _jacFP[pblk].clear(); Indexer idxr(_disc); -// const double invBetaC = 1.0 / static_cast(_colPorosity) - 1.0; + // const double invBetaC = 1.0 / static_cast(_colPorosity) - 1.0; // Discretized film diffusion kf for finite volumes double* const kf_FV = _discParFlux.create(_disc.nComp); @@ -2273,15 +2461,21 @@ void GeneralRateModel::assembleOffdiagJacFluxParticle(double t // Ordering of diffusion: // sec0type0comp0, sec0type0comp1, sec0type0comp2, sec0type1comp0, sec0type1comp1, sec0type1comp2, // sec1type0comp0, sec1type0comp1, sec1type0comp2, sec1type1comp0, sec1type1comp1, sec1type1comp2, ... - active const* const filmDiff = getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; - active const* const parDiff = getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const filmDiff = + getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const parDiff = + getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; // Discretized film diffusion kf for finite volumes if (cadet_likely(_colParBoundaryOrder == 2)) { - const double absOuterShellHalfRadius = 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); + const double absOuterShellHalfRadius = + 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - kf_FV[comp] = 1.0 / (absOuterShellHalfRadius / epsP / static_cast(_poreAccessFactor[type * _disc.nComp + comp]) / static_cast(parDiff[comp]) + 1.0 / static_cast(filmDiff[comp])); + kf_FV[comp] = 1.0 / (absOuterShellHalfRadius / epsP / + static_cast(_poreAccessFactor[type * _disc.nComp + comp]) / + static_cast(parDiff[comp]) + + 1.0 / static_cast(filmDiff[comp])); } else { @@ -2306,16 +2500,24 @@ void GeneralRateModel::assembleOffdiagJacFluxParticle(double t // Ordering of particle surface diffusion: // bnd0comp0, bnd0comp1, bnd0comp2, bnd1comp0, bnd1comp1, bnd1comp2 - active const* const parSurfDiff = getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + _disc.nBoundBeforeType[type]; + active const* const parSurfDiff = + getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + + _disc.nBoundBeforeType[type]; active const* const parCenterRadius = _parCenterRadius.data() + _disc.nParCellsBeforeType[type]; - const double absOuterShellHalfRadius = 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); + const double absOuterShellHalfRadius = + 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - kf_FV[comp] = (1.0 - static_cast(_parPorosity[type])) / (1.0 + epsP * static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * static_cast(parDiff[comp]) / (absOuterShellHalfRadius * static_cast(filmDiff[comp]))); + kf_FV[comp] = (1.0 - static_cast(_parPorosity[type])) / + (1.0 + epsP * static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * + static_cast(parDiff[comp]) / + (absOuterShellHalfRadius * static_cast(filmDiff[comp]))); for (unsigned int pblk = 0; pblk < _disc.nCol; ++pblk) { - const ColumnPosition colPos{(0.5 + static_cast(pblk)) / static_cast(_disc.nCol), 0.0, static_cast(parCenterRadius[0]) / static_cast(_parRadius[type])}; + const ColumnPosition colPos{(0.5 + static_cast(pblk)) / static_cast(_disc.nCol), 0.0, + static_cast(parCenterRadius[0]) / + static_cast(_parRadius[type])}; const double dr = static_cast(parCenterRadius[0]) - static_cast(parCenterRadius[1]); double const* const yCell = vecStateY + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}); @@ -2336,12 +2538,8 @@ void GeneralRateModel::assembleOffdiagJacFluxParticle(double t if (_parDepSurfDiffusion[type]) { const double localSurfDiff = _parDepSurfDiffusion[type]->combinedParameterSolid( - colPos, - static_cast(parSurfDiff[idxBnd]), - yCell, - yCell + idxr.strideParLiquid(), - idxBnd - ); + colPos, static_cast(parSurfDiff[idxBnd]), yCell, yCell + idxr.strideParLiquid(), + idxBnd); const double v = kf_FV[comp] * localSurfDiff / dr; const int curIdx = idxr.strideParLiquid() + idxBnd; @@ -2351,16 +2549,8 @@ void GeneralRateModel::assembleOffdiagJacFluxParticle(double t const double gradQ = (yCell[curIdx] - yCell[curIdx + idxr.strideParShell(type)]) / dr; _parDepSurfDiffusion[type]->analyticJacobianCombinedAddSolid( - colPos, - static_cast(parSurfDiff[idxBnd]), - yCell, - yCell + idxr.strideParLiquid(), - idxBnd, - -kf_FV[comp] * gradQ, - 0, - eq, - jacFPtype[pblk] - ); + colPos, static_cast(parSurfDiff[idxBnd]), yCell, yCell + idxr.strideParLiquid(), + idxBnd, -kf_FV[comp] * gradQ, 0, eq, jacFPtype[pblk]); } else { @@ -2380,7 +2570,10 @@ void GeneralRateModel::assembleOffdiagJacFluxParticle(double t } template -int GeneralRateModel::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel::residualSensFwdWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidualSens); @@ -2390,18 +2583,22 @@ int GeneralRateModel::residualSensFwdWithJacobian(const Simula } template -int GeneralRateModel::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel::residualSensFwdAdOnly(const SimulationTime& simTime, + const ConstSimulationState& simState, active* const adRes, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidualSens); // Evaluate residual for all parameters using AD in vector mode - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adRes, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, adRes, threadLocalMem); } template -int GeneralRateModel::residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3) +int GeneralRateModel::residualSensFwdCombine( + const SimulationTime& simTime, const ConstSimulationState& simState, const std::vector& yS, + const std::vector& ySdot, const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3) { BENCH_SCOPE(_timerResidualSens); @@ -2411,10 +2608,12 @@ int GeneralRateModel::residualSensFwdCombine(const SimulationT for (std::size_t param = 0; param < yS.size(); ++param) { // Directional derivative (dF / dy) * s - multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, yS[param], 1.0, 0.0, tmp1); + multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, yS[param], 1.0, 0.0, + tmp1); // Directional derivative (dF / dyDot) * sDot - multiplyWithDerivativeJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, ySdot[param], tmp2); + multiplyWithDerivativeJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, ySdot[param], + tmp2); double* const ptrResS = resS[param]; @@ -2438,11 +2637,12 @@ int GeneralRateModel::residualSensFwdCombine(const SimulationT } /** - * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, \dot{y}\right) @f$) + * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, + * \dot{y}\right) @f$) * @details Actually, the operation @f$ z = \alpha \frac{\partial F}{\partial y} x + \beta z @f$ is performed. * - * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ once - * before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. + * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ + * once before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. * @param [in] simTime Current simulation time point * @param [in] simState Simulation state vectors * @param [in] yS Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial y} @f$ @@ -2451,7 +2651,9 @@ int GeneralRateModel::residualSensFwdCombine(const SimulationT * @param [in,out] ret Vector @f$ z @f$ which stores the result of the operation */ template -void GeneralRateModel::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret) +void GeneralRateModel::multiplyWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, double const* yS, + double alpha, double beta, double* ret) { Indexer idxr(_disc); @@ -2498,7 +2700,8 @@ void GeneralRateModel::multiplyWithJacobian(const SimulationTi { for (unsigned int par = 0; par < _disc.nCol; ++par) { - _jacFP[type * _disc.nCol + par].multiplyVector(yS + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}), alpha, 1.0, retJf); + _jacFP[type * _disc.nCol + par].multiplyVector( + yS + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}), alpha, 1.0, retJf); } } @@ -2507,7 +2710,8 @@ void GeneralRateModel::multiplyWithJacobian(const SimulationTi } /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ + * with a given vector * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). * @param [in] simTime Current simulation time point @@ -2516,7 +2720,9 @@ void GeneralRateModel::multiplyWithJacobian(const SimulationTi * @param [out] ret Vector @f$ z @f$ which stores the result of the operation */ template -void GeneralRateModel::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret) +void GeneralRateModel::multiplyWithDerivativeJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + double const* sDot, double* ret) { Indexer idxr(_disc); @@ -2549,7 +2755,9 @@ void GeneralRateModel::multiplyWithDerivativeJacobian(const Si double const* const mobileSdot = sDot + offsetCpShell; double* const mobileRet = ret + offsetCpShell; - parts::cell::multiplyWithDerivativeJacobianKernel(mobileSdot, mobileRet, _disc.nComp, nBound, boundOffset, _disc.strideBound[type], qsReaction, 1.0, invBetaP); + parts::cell::multiplyWithDerivativeJacobianKernel(mobileSdot, mobileRet, _disc.nComp, nBound, + boundOffset, _disc.strideBound[type], + qsReaction, 1.0, invBetaP); } } } CADET_PARFOR_END; @@ -2604,7 +2812,8 @@ unsigned int GeneralRateModel::localInletComponentStride(unsig } template -void GeneralRateModel::expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut) +void GeneralRateModel::expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, + double* expandOut) { // @todo Write this function } @@ -2621,7 +2830,8 @@ void GeneralRateModel::setEquidistantRadialDisc(unsigned int p const active radius = _parRadius[parType] - _parCoreRadius[parType]; const active dr = radius / static_cast(_disc.nParCell[parType]); - std::fill(_parCellSize.data() + _disc.nParCellsBeforeType[parType], _parCellSize.data() + _disc.nParCellsBeforeType[parType] + _disc.nParCell[parType], dr); + std::fill(_parCellSize.data() + _disc.nParCellsBeforeType[parType], + _parCellSize.data() + _disc.nParCellsBeforeType[parType] + _disc.nParCell[parType], dr); if (_parGeomSurfToVol[parType] == SurfVolRatioSphere) { @@ -2688,7 +2898,8 @@ void GeneralRateModel::setEquivolumeRadialDisc(unsigned int pa { active r_out = _parRadius[parType]; active r_in = _parCoreRadius[parType]; - const active volumePerShell = (pow(_parRadius[parType], 3.0) - pow(_parCoreRadius[parType], 3.0)) / static_cast(_disc.nParCell[parType]); + const active volumePerShell = (pow(_parRadius[parType], 3.0) - pow(_parCoreRadius[parType], 3.0)) / + static_cast(_disc.nParCell[parType]); for (unsigned int cell = 0; cell < _disc.nParCell[parType]; ++cell) { @@ -2711,7 +2922,8 @@ void GeneralRateModel::setEquivolumeRadialDisc(unsigned int pa { active r_out = _parRadius[parType]; active r_in = _parCoreRadius[parType]; - const active volumePerShell = (sqr(_parRadius[parType]) - sqr(_parCoreRadius[parType])) / static_cast(_disc.nParCell[parType]); + const active volumePerShell = + (sqr(_parRadius[parType]) - sqr(_parCoreRadius[parType])) / static_cast(_disc.nParCell[parType]); for (unsigned int cell = 0; cell < _disc.nParCell[parType]; ++cell) { @@ -2734,7 +2946,8 @@ void GeneralRateModel::setEquivolumeRadialDisc(unsigned int pa { active r_out = _parRadius[parType]; active r_in = _parCoreRadius[parType]; - const active volumePerShell = (_parRadius[parType] - _parCoreRadius[parType]) / static_cast(_disc.nParCell[parType]); + const active volumePerShell = + (_parRadius[parType] - _parCoreRadius[parType]) / static_cast(_disc.nParCell[parType]); for (unsigned int cell = 0; cell < _disc.nParCell[parType]; ++cell) { @@ -2768,7 +2981,8 @@ void GeneralRateModel::setUserdefinedRadialDisc(unsigned int p active* const ptrInnerSurfAreaPerVolume = _parInnerSurfAreaPerVolume.data() + _disc.nParCellsBeforeType[parType]; // Care for the right ordering and include 0.0 / 1.0 if not already in the vector. - std::vector orderedInterfaces = std::vector(_parDiscVector.begin() + _disc.nParCellsBeforeType[parType] + parType, + std::vector orderedInterfaces = std::vector( + _parDiscVector.begin() + _disc.nParCellsBeforeType[parType] + parType, _parDiscVector.begin() + _disc.nParCellsBeforeType[parType] + parType + _disc.nParCell[parType] + 1); // Sort in descending order @@ -2780,7 +2994,9 @@ void GeneralRateModel::setUserdefinedRadialDisc(unsigned int p // Map [0, 1] -> [core radius, particle radius] via linear interpolation for (unsigned int cell = 0; cell < _disc.nParCell[parType]; ++cell) - orderedInterfaces[cell] = static_cast(orderedInterfaces[cell]) * (_parRadius[parType] - _parCoreRadius[parType]) + _parCoreRadius[parType]; + orderedInterfaces[cell] = + static_cast(orderedInterfaces[cell]) * (_parRadius[parType] - _parCoreRadius[parType]) + + _parCoreRadius[parType]; if (_parGeomSurfToVol[parType] == SurfVolRatioSphere) { @@ -2826,8 +3042,7 @@ void GeneralRateModel::setUserdefinedRadialDisc(unsigned int p } } -template -void GeneralRateModel::updateRadialDisc() +template void GeneralRateModel::updateRadialDisc() { for (unsigned int i = 0; i < _disc.nParType; ++i) { @@ -2845,13 +3060,18 @@ bool GeneralRateModel::setParameter(const ParameterId& pId, do { if (pId.unitOperation == _unitOpIdx) { - if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, _poreAccessFactor, _disc.nParType, _disc.nComp, value, nullptr)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, + _poreAccessFactor, _disc.nParType, _disc.nComp, value, nullptr)) return true; - if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, _disc.nParType, _disc.nComp, value, nullptr)) + if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, + _disc.nParType, _disc.nComp, value, nullptr)) return true; - if (multiplexCompTypeSecParameterValue(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, _disc.nParType, _disc.nComp, value, nullptr)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, + _disc.nParType, _disc.nComp, value, nullptr)) return true; - if (multiplexBndCompTypeSecParameterValue(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, _disc.nBound, _disc.boundOffset, value, nullptr)) + if (multiplexBndCompTypeSecParameterValue(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, + _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, + _disc.nBound, _disc.boundOffset, value, nullptr)) return true; const int mpIc = multiplexInitialConditions(pId, value, false); if (mpIc > 0) @@ -2862,7 +3082,8 @@ bool GeneralRateModel::setParameter(const ParameterId& pId, do // Intercept changes to PAR_TYPE_VOLFRAC when not specified per axial cell (but once globally) if (_axiallyConstantParTypeVolFrac && (pId.name == hashString("PAR_TYPE_VOLFRAC"))) { - if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep)) + if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep)) return false; if (pId.particleType >= _disc.nParType) return false; @@ -2875,9 +3096,11 @@ bool GeneralRateModel::setParameter(const ParameterId& pId, do if (multiplexTypeParameterValue(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, value, nullptr)) return true; - if (multiplexTypeParameterValue(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, value, nullptr)) + if (multiplexTypeParameterValue(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, value, + nullptr)) return true; - if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, nullptr)) + if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, + nullptr)) return true; if (model::setParameter(pId, value, _parDepSurfDiffusion, _singleParDepSurfDiffusion)) @@ -2925,13 +3148,18 @@ void GeneralRateModel::setSensitiveParameterValue(const Parame { if (pId.unitOperation == _unitOpIdx) { - if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, _poreAccessFactor, _disc.nParType, _disc.nComp, value, &_sensParams)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, + _poreAccessFactor, _disc.nParType, _disc.nComp, value, &_sensParams)) return; - if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, _disc.nParType, _disc.nComp, value, &_sensParams)) + if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, + _disc.nParType, _disc.nComp, value, &_sensParams)) return; - if (multiplexCompTypeSecParameterValue(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, _disc.nParType, _disc.nComp, value, &_sensParams)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, + _disc.nParType, _disc.nComp, value, &_sensParams)) return; - if (multiplexBndCompTypeSecParameterValue(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, _disc.nBound, _disc.boundOffset, value, &_sensParams)) + if (multiplexBndCompTypeSecParameterValue(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, + _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, + _disc.nBound, _disc.boundOffset, value, &_sensParams)) return; if (multiplexInitialConditions(pId, value, true) != 0) return; @@ -2939,7 +3167,8 @@ void GeneralRateModel::setSensitiveParameterValue(const Parame // Intercept changes to PAR_TYPE_VOLFRAC when not specified per axial cell (but once globally) if (_axiallyConstantParTypeVolFrac && (pId.name == hashString("PAR_TYPE_VOLFRAC"))) { - if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep)) + if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep)) return; if (pId.particleType >= _disc.nParType) return; @@ -2953,14 +3182,18 @@ void GeneralRateModel::setSensitiveParameterValue(const Parame return; } - if (multiplexTypeParameterValue(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, value, &_sensParams)) + if (multiplexTypeParameterValue(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, value, + &_sensParams)) return; - if (multiplexTypeParameterValue(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, value, &_sensParams)) + if (multiplexTypeParameterValue(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, value, + &_sensParams)) return; - if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, &_sensParams)) + if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, + &_sensParams)) return; - if (model::setSensitiveParameterValue(pId, value, _sensParams, _parDepSurfDiffusion, _singleParDepSurfDiffusion)) + if (model::setSensitiveParameterValue(pId, value, _sensParams, _parDepSurfDiffusion, + _singleParDepSurfDiffusion)) return; if (_convDispOp.setSensitiveParameterValue(_sensParams, pId, value)) @@ -2975,29 +3208,36 @@ void GeneralRateModel::setSensitiveParameterValue(const Parame } template -bool GeneralRateModel::setSensitiveParameter(const ParameterId& pId, unsigned int adDirection, double adValue) +bool GeneralRateModel::setSensitiveParameter(const ParameterId& pId, unsigned int adDirection, + double adValue) { if (pId.unitOperation == _unitOpIdx) { - if (multiplexCompTypeSecParameterAD(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, _poreAccessFactor, _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) + if (multiplexCompTypeSecParameterAD(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, + _poreAccessFactor, _disc.nParType, _disc.nComp, adDirection, adValue, + _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexCompTypeSecParameterAD(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) + if (multiplexCompTypeSecParameterAD(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, + _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexCompTypeSecParameterAD(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) + if (multiplexCompTypeSecParameterAD(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, + _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexBndCompTypeSecParameterAD(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, _disc.nBound, _disc.boundOffset, adDirection, adValue, _sensParams)) + if (multiplexBndCompTypeSecParameterAD(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, + _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, + _disc.nBound, _disc.boundOffset, adDirection, adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; @@ -3015,7 +3255,8 @@ bool GeneralRateModel::setSensitiveParameter(const ParameterId // Intercept changes to PAR_TYPE_VOLFRAC when not specified per axial cell (but once globally) if (_axiallyConstantParTypeVolFrac && (pId.name == hashString("PAR_TYPE_VOLFRAC"))) { - if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep)) + if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep)) return false; if (pId.particleType >= _disc.nParType) return false; @@ -3030,27 +3271,32 @@ bool GeneralRateModel::setSensitiveParameter(const ParameterId return true; } - if (multiplexTypeParameterAD(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, adDirection, adValue, _sensParams)) + if (multiplexTypeParameterAD(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, adDirection, adValue, + _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexTypeParameterAD(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, adDirection, adValue, _sensParams)) + if (multiplexTypeParameterAD(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, + adDirection, adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexTypeParameterAD(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, adDirection, adValue, _sensParams)) + if (multiplexTypeParameterAD(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, adDirection, + adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (model::setSensitiveParameter(pId, adDirection, adValue, _sensParams, _parDepSurfDiffusion, _singleParDepSurfDiffusion)) + if (model::setSensitiveParameter(pId, adDirection, adValue, _sensParams, _parDepSurfDiffusion, + _singleParDepSurfDiffusion)) { - LOG(Debug) << "Found parameter " << pId << " in surface diffusion parameter dependence: Dir " << adDirection << " is set to " << adValue; + LOG(Debug) << "Found parameter " << pId << " in surface diffusion parameter dependence: Dir " << adDirection + << " is set to " << adValue; return true; } @@ -3064,8 +3310,9 @@ bool GeneralRateModel::setSensitiveParameter(const ParameterId const bool result = UnitOperationBase::setSensitiveParameter(pId, adDirection, adValue); // Check whether particle radius or core radius has been set active and update radial discretization if necessary - // Note that we need to recompute the radial discretization variables (_parCellSize, _parCenterRadius, _parOuterSurfAreaPerVolume, _parInnerSurfAreaPerVolume) - // because their gradient has changed (although their nominal value has not changed). + // Note that we need to recompute the radial discretization variables (_parCellSize, _parCenterRadius, + // _parOuterSurfAreaPerVolume, _parInnerSurfAreaPerVolume) because their gradient has changed (although their + // nominal value has not changed). if ((pId.name == hashString("PAR_RADIUS")) || (pId.name == hashString("PAR_CORERADIUS"))) updateRadialDisc(); @@ -3092,8 +3339,7 @@ double GeneralRateModel::getParameterDouble(const ParameterId& return UnitOperationBase::getParameterDouble(pId); } -template -bool GeneralRateModel::hasParameter(const ParameterId& pId) const +template bool GeneralRateModel::hasParameter(const ParameterId& pId) const { if (model::hasParameter(pId, _parDepSurfDiffusion, _singleParDepSurfDiffusion)) return true; @@ -3101,7 +3347,6 @@ bool GeneralRateModel::hasParameter(const ParameterId& pId) co return UnitOperationBase::hasParameter(pId); } - template int GeneralRateModel::Exporter::writeMobilePhase(double* buffer) const { @@ -3198,8 +3443,7 @@ int GeneralRateModel::Exporter::writeInlet(unsigned int port, return _disc.nComp; } -template -int GeneralRateModel::Exporter::writeInlet(double* buffer) const +template int GeneralRateModel::Exporter::writeInlet(double* buffer) const { std::copy_n(_data, _disc.nComp, buffer); return _disc.nComp; @@ -3218,8 +3462,7 @@ int GeneralRateModel::Exporter::writeOutlet(unsigned int port, return _disc.nComp; } -template -int GeneralRateModel::Exporter::writeOutlet(double* buffer) const +template int GeneralRateModel::Exporter::writeOutlet(double* buffer) const { if (_model._convDispOp.forwardFlow()) std::copy_n(&_idx.c(_data, _disc.nCol - 1, 0), _disc.nComp, buffer); @@ -3229,9 +3472,9 @@ int GeneralRateModel::Exporter::writeOutlet(double* buffer) co return _disc.nComp; } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet #include "model/GeneralRateModel-InitialConditions.cpp" #include "model/GeneralRateModel-LinearSolver.cpp" @@ -3260,6 +3503,6 @@ IUnitOperation* createRadialFVGRM(UnitOpIdx uoId) return new RadialGRM(uoId); } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/GeneralRateModel.hpp b/src/libcadet/model/GeneralRateModel.hpp index 887a7702a..b98617df3 100644 --- a/src/libcadet/model/GeneralRateModel.hpp +++ b/src/libcadet/model/GeneralRateModel.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the general rate model (GRM). */ @@ -37,21 +37,26 @@ namespace { - template - struct GeneralRateModelName { }; +template struct GeneralRateModelName +{ +}; - template <> - struct GeneralRateModelName +template <> struct GeneralRateModelName +{ + static const char* identifier() CADET_NOEXCEPT { - static const char* identifier() CADET_NOEXCEPT { return "GENERAL_RATE_MODEL"; } - }; + return "GENERAL_RATE_MODEL"; + } +}; - template <> - struct GeneralRateModelName +template <> struct GeneralRateModelName +{ + static const char* identifier() CADET_NOEXCEPT { - static const char* identifier() CADET_NOEXCEPT { return "RADIAL_GENERAL_RATE_MODEL"; } - }; -} + return "RADIAL_GENERAL_RATE_MODEL"; + } +}; +} // namespace namespace cadet { @@ -61,11 +66,11 @@ namespace model namespace parts { - namespace cell - { - struct CellParameters; - } +namespace cell +{ +struct CellParameters; } +} // namespace parts class IDynamicReactionModel; class IParameterStateDependence; @@ -73,12 +78,14 @@ class IParameterStateDependence; /** * @brief General rate model of liquid column chromatography * @details See @cite Guiochon2006, @cite Gu1995, @cite Felinger2004 - * + * * @f[\begin{align} - \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 c_i}{\partial z^2} - \frac{1 - \varepsilon_c}{\varepsilon_c} \frac{3}{r_p} j_{f,i} \\ - \frac{\partial c_{p,i}}{\partial t} + \frac{1 - \varepsilon_p}{\varepsilon_p} \frac{\partial q_{i}}{\partial t} &= D_{p,i} \left( \frac{\partial^2 c_{p,i}}{\partial r^2} + \frac{2}{r} \frac{\partial c_{p,i}}{\partial r} \right) + D_{s,i} \frac{1 - \varepsilon_p}{\varepsilon_p} \left( \frac{\partial^2 q_{i}}{\partial r^2} + \frac{2}{r} \frac{\partial q_{i}}{\partial r} \right) \\ - a \frac{\partial q_i}{\partial t} &= f_{\text{iso}}(c_p, q) -\end{align} @f] + \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 +c_i}{\partial z^2} - \frac{1 - \varepsilon_c}{\varepsilon_c} \frac{3}{r_p} j_{f,i} \\ + \frac{\partial c_{p,i}}{\partial t} + \frac{1 - \varepsilon_p}{\varepsilon_p} \frac{\partial q_{i}}{\partial t} &= +D_{p,i} \left( \frac{\partial^2 c_{p,i}}{\partial r^2} + \frac{2}{r} \frac{\partial c_{p,i}}{\partial r} \right) + +D_{s,i} \frac{1 - \varepsilon_p}{\varepsilon_p} \left( \frac{\partial^2 q_{i}}{\partial r^2} + \frac{2}{r} +\frac{\partial q_{i}}{\partial r} \right) \\ a \frac{\partial q_i}{\partial t} &= f_{\text{iso}}(c_p, q) \end{align} @f] @f[ \begin{align} j_{f,i} = k_{f,i} \left( c_i - c_{p,i} \left(\cdot, \cdot, r_p\right)\right) \end{align} @f] @@ -86,16 +93,15 @@ class IParameterStateDependence; @f[ \begin{align} u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partial z}(t,0) \\ \frac{\partial c_i}{\partial z}(t,L) &= 0 \\ -\varepsilon_p D_{p,i} \frac{\partial c_{p,i}}{\partial r}(\cdot, \cdot, r_p) + (1-\varepsilon_p) D_{s,i} \frac{\partial q_{i}}{\partial r}(\cdot, \cdot, r_p) &= j_{f,i} \\ -\frac{\partial c_{p,i}}{\partial r}(\cdot, \cdot, 0) &= 0 +\varepsilon_p D_{p,i} \frac{\partial c_{p,i}}{\partial r}(\cdot, \cdot, r_p) + (1-\varepsilon_p) D_{s,i} \frac{\partial +q_{i}}{\partial r}(\cdot, \cdot, r_p) &= j_{f,i} \\ \frac{\partial c_{p,i}}{\partial r}(\cdot, \cdot, 0) &= 0 \end{align} @f] - * Methods are described in @cite VonLieres2010a (WENO, linear solver), @cite Puttmann2013 @cite Puttmann2016 (forward sensitivities, AD, band compression) + * Methods are described in @cite VonLieres2010a (WENO, linear solver), @cite Puttmann2013 @cite Puttmann2016 (forward +sensitivities, AD, band compression) */ -template -class GeneralRateModel : public UnitOperationBase +template class GeneralRateModel : public UnitOperationBase { public: - GeneralRateModel(UnitOpIdx unitOpIdx); virtual ~GeneralRateModel() CADET_NOEXCEPT; @@ -104,60 +110,105 @@ class GeneralRateModel : public UnitOperationBase virtual bool usesAD() const CADET_NOEXCEPT; virtual unsigned int requiredADdirs() const CADET_NOEXCEPT; - virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT { return _unitOpIdx; } - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } + virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT + { + return _unitOpIdx; + } + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } virtual void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT; - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual bool canAccumulate() const CADET_NOEXCEPT { return false; } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual bool canAccumulate() const CADET_NOEXCEPT + { + return false; + } - static const char* identifier() { return GeneralRateModelName::identifier(); } - virtual const char* unitOperationName() const CADET_NOEXCEPT { return identifier(); } + static const char* identifier() + { + return GeneralRateModelName::identifier(); + } + virtual const char* unitOperationName() const CADET_NOEXCEPT + { + return identifier(); + } virtual bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper); virtual bool configure(IParameterProvider& paramProvider); - virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac); + virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac); virtual void useAnalyticJacobian(const bool analyticJac); virtual void reportSolution(ISolutionRecorder& recorder, double const* const solution) const; virtual void reportSolutionStructure(ISolutionRecorder& recorder) const; - virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem); + virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem); - virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3); + virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3); virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState); + const ConstSimulationState& simState); virtual void prepareADvectors(const AdJacobianParams& adJac) const; virtual void applyInitialCondition(const SimulationState& simState) const; virtual void readInitialCondition(IParameterProvider& paramProvider); - virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); virtual void initializeSensitivityStates(const std::vector& vecSensY) const; virtual void consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem); virtual void leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual bool hasInlet() const CADET_NOEXCEPT { return true; } - virtual bool hasOutlet() const CADET_NOEXCEPT { return true; } + virtual bool hasInlet() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasOutlet() const CADET_NOEXCEPT + { + return true; + } virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT; virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT; @@ -165,14 +216,19 @@ class GeneralRateModel : public UnitOperationBase virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT; virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size); - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { } + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) + { + } virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut); - virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret); - virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret); + virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret); + virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* sDot, double* ret); - inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double* ret) + inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double* ret) { multiplyWithJacobian(simTime, simState, yS, 1.0, 0.0, ret); } @@ -192,60 +248,43 @@ class GeneralRateModel : public UnitOperationBase #ifdef CADET_BENCHMARK_MODE virtual std::vector benchmarkTimings() const { - return std::vector({ - static_cast(numDofs()), - _timerResidual.totalElapsedTime(), - _timerResidualPar.totalElapsedTime(), - _timerResidualSens.totalElapsedTime(), - _timerResidualSensPar.totalElapsedTime(), - _timerJacobianPar.totalElapsedTime(), - _timerConsistentInit.totalElapsedTime(), - _timerConsistentInitPar.totalElapsedTime(), - _timerLinearSolve.totalElapsedTime(), - _timerFactorize.totalElapsedTime(), - _timerFactorizePar.totalElapsedTime(), - _timerMatVec.totalElapsedTime(), - _timerGmres.totalElapsedTime(), - static_cast(_gmres.numIterations()) - }); + return std::vector({static_cast(numDofs()), _timerResidual.totalElapsedTime(), + _timerResidualPar.totalElapsedTime(), _timerResidualSens.totalElapsedTime(), + _timerResidualSensPar.totalElapsedTime(), _timerJacobianPar.totalElapsedTime(), + _timerConsistentInit.totalElapsedTime(), _timerConsistentInitPar.totalElapsedTime(), + _timerLinearSolve.totalElapsedTime(), _timerFactorize.totalElapsedTime(), + _timerFactorizePar.totalElapsedTime(), _timerMatVec.totalElapsedTime(), + _timerGmres.totalElapsedTime(), static_cast(_gmres.numIterations())}); } virtual char const* const* benchmarkDescriptions() const { static const char* const desc[] = { - "DOFs", - "Residual", - "ResidualPar", - "ResidualSens", - "ResidualSensPar", - "JacobianPar", - "ConsistentInit", - "ConsistentInitPar", - "LinearSolve", - "Factorize", - "FactorizePar", - "MatVec", - "Gmres", - "NumGMRESIter" - }; + "DOFs", "Residual", "ResidualPar", "ResidualSens", "ResidualSensPar", "JacobianPar", + "ConsistentInit", "ConsistentInitPar", "LinearSolve", "Factorize", "FactorizePar", "MatVec", + "Gmres", "NumGMRESIter"}; return desc; } #endif protected: - class Indexer; - int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity); + int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, + bool paramSensitivity); template - int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); + int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, + ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); template - int residualBulk(double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, util::ThreadLocalStorage& threadLocalMem); + int residualBulk(double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, + util::ThreadLocalStorage& threadLocalMem); template - int residualParticle(double t, unsigned int parType, unsigned int colCell, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, util::ThreadLocalStorage& threadLocalMem); + int residualParticle(double t, unsigned int parType, unsigned int colCell, unsigned int secIdx, StateType const* y, + double const* yDot, ResidualType* res, util::ThreadLocalStorage& threadLocalMem); template int residualFlux(double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res); @@ -255,16 +294,18 @@ class GeneralRateModel : public UnitOperationBase void extractJacobianFromAD(active const* const adRes, unsigned int adDirOffset); int schurComplementMatrixVector(double const* x, double* z) const; - void assembleDiscretizedJacobianParticleBlock(unsigned int parType, unsigned int pblk, double alpha, const Indexer& idxr); - + void assembleDiscretizedJacobianParticleBlock(unsigned int parType, unsigned int pblk, double alpha, + const Indexer& idxr); + void setEquidistantRadialDisc(unsigned int parType); void setEquivolumeRadialDisc(unsigned int parType); void setUserdefinedRadialDisc(unsigned int parType); void updateRadialDisc(); - void addTimeDerivativeToJacobianParticleShell(linalg::FactorizableBandMatrix::RowIterator& jac, const Indexer& idxr, double alpha, unsigned int parType); + void addTimeDerivativeToJacobianParticleShell(linalg::FactorizableBandMatrix::RowIterator& jac, const Indexer& idxr, + double alpha, unsigned int parType); void solveForFluxes(double* const vecState, const Indexer& idxr) const; - + unsigned int numAdDirsForJacobian() const CADET_NOEXCEPT; int multiplexInitialConditions(const cadet::ParameterId& pId, unsigned int adDirection, double adValue); @@ -280,16 +321,23 @@ class GeneralRateModel : public UnitOperationBase struct Discretization { - unsigned int nComp; //!< Number of components - unsigned int nCol; //!< Number of column cells - unsigned int nParType; //!< Number of particle types + unsigned int nComp; //!< Number of components + unsigned int nCol; //!< Number of column cells + unsigned int nParType; //!< Number of particle types unsigned int* nParCell; //!< Array with number of radial cells in each particle type - unsigned int* nParCellsBeforeType; //!< Array with total number of radial cells before a particle type (cumulative sum of nParCell), additional last element contains total number of particle shells - unsigned int* parTypeOffset; //!< Array with offsets (in particle block) to particle type, additional last element contains total number of particle DOFs - unsigned int* nBound; //!< Array with number of bound states for each component and particle type (particle type major ordering) - unsigned int* boundOffset; //!< Array with offset to the first bound state of each component in the solid phase (particle type major ordering) - unsigned int* strideBound; //!< Total number of bound states for each particle type, additional last element contains total number of bound states for all types - unsigned int* nBoundBeforeType; //!< Array with number of bound states before a particle type (cumulative sum of strideBound) + unsigned int* + nParCellsBeforeType; //!< Array with total number of radial cells before a particle type (cumulative sum of + //!< nParCell), additional last element contains total number of particle shells + unsigned int* parTypeOffset; //!< Array with offsets (in particle block) to particle type, additional last + //!< element contains total number of particle DOFs + unsigned int* nBound; //!< Array with number of bound states for each component and particle type (particle type + //!< major ordering) + unsigned int* boundOffset; //!< Array with offset to the first bound state of each component in the solid phase + //!< (particle type major ordering) + unsigned int* strideBound; //!< Total number of bound states for each particle type, additional last element + //!< contains total number of bound states for all types + unsigned int* nBoundBeforeType; //!< Array with number of bound states before a particle type (cumulative sum of + //!< strideBound) }; enum class ParticleDiscretizationMode : int @@ -310,34 +358,38 @@ class GeneralRateModel : public UnitOperationBase UserDefined }; - Discretization _disc; //!< Discretization info + Discretization _disc; //!< Discretization info std::vector _hasSurfaceDiffusion; //!< Determines whether surface diffusion is present in each particle type -// IExternalFunction* _extFun; //!< External function (owned by library user) + // IExternalFunction* _extFun; //!< External function (owned by library user) - ConvDispOperator _convDispOp; //!< Convection dispersion operator for interstitial volume transport + ConvDispOperator _convDispOp; //!< Convection dispersion operator for interstitial volume transport IDynamicReactionModel* _dynReactionBulk; //!< Dynamic reactions in the bulk volume linalg::BandMatrix* _jacP; //!< Particle jacobian diagonal blocks (all of them) - linalg::FactorizableBandMatrix* _jacPdisc; //!< Particle jacobian diagonal blocks (all of them) with time derivatives from BDF method + linalg::FactorizableBandMatrix* + _jacPdisc; //!< Particle jacobian diagonal blocks (all of them) with time derivatives from BDF method - linalg::DoubleSparseMatrix _jacCF; //!< Jacobian block connecting interstitial states and fluxes (interstitial transport equation) + linalg::DoubleSparseMatrix + _jacCF; //!< Jacobian block connecting interstitial states and fluxes (interstitial transport equation) linalg::DoubleSparseMatrix _jacFC; //!< Jacobian block connecting fluxes and interstitial states (flux equation) - linalg::DoubleSparseMatrix* _jacPF; //!< Jacobian blocks connecting particle states and fluxes (particle transport boundary condition) + linalg::DoubleSparseMatrix* + _jacPF; //!< Jacobian blocks connecting particle states and fluxes (particle transport boundary condition) linalg::DoubleSparseMatrix* _jacFP; //!< Jacobian blocks connecting fluxes and particle states (flux equation) linalg::DoubleSparseMatrix _jacInlet; //!< Jacobian inlet DOF block matrix connects inlet DOFs to first bulk cells - active _colPorosity; //!< Column porosity (external porosity) \f$ \varepsilon_c \f$ + active _colPorosity; //!< Column porosity (external porosity) \f$ \varepsilon_c \f$ std::vector _parRadius; //!< Particle radius \f$ r_p \f$ bool _singleParRadius; std::vector _parCoreRadius; //!< Particle core radius \f$ r_c \f$ bool _singleParCoreRadius; std::vector _parPorosity; //!< Particle porosity (internal porosity) \f$ \varepsilon_p \f$ bool _singleParPorosity; - std::vector _parTypeVolFrac; //!< Volume fraction of each particle type + std::vector _parTypeVolFrac; //!< Volume fraction of each particle type std::vector _parDiscType; //!< Particle discretization mode - std::vector _parDiscVector; //!< Particle discretization shell edges - std::vector _parGeomSurfToVol; //!< Particle surface to volume ratio factor (i.e., 3.0 for spherical, 2.0 for cylindrical, 1.0 for hexahedral) + std::vector _parDiscVector; //!< Particle discretization shell edges + std::vector _parGeomSurfToVol; //!< Particle surface to volume ratio factor (i.e., 3.0 for spherical, 2.0 + //!< for cylindrical, 1.0 for hexahedral) // Vectorial parameters std::vector _filmDiffusion; //!< Film diffusion coefficient \f$ k_f \f$ @@ -348,16 +400,19 @@ class GeneralRateModel : public UnitOperationBase MultiplexMode _parSurfDiffusionMode; std::vector _poreAccessFactor; //!< Pore accessibility factor \f$ F_{\text{acc}} \f$ MultiplexMode _poreAccessFactorMode; - std::vector _parDepSurfDiffusion; //!< Parameter dependencies for particle surface diffusion - bool _singleParDepSurfDiffusion; //!< Determines whether a single parameter dependence for particle surface diffusion is used + std::vector + _parDepSurfDiffusion; //!< Parameter dependencies for particle surface diffusion + bool _singleParDepSurfDiffusion; //!< Determines whether a single parameter dependence for particle surface + //!< diffusion is used bool _hasParDepSurfDiffusion; //!< Determines whether particle surface diffusion parameter dependencies are present - bool _axiallyConstantParTypeVolFrac; //!< Determines whether particle type volume fraction is homogeneous across axial coordinate - bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used - unsigned int _jacobianAdDirs; //!< Number of AD seed vectors required for Jacobian computation + bool _axiallyConstantParTypeVolFrac; //!< Determines whether particle type volume fraction is homogeneous across + //!< axial coordinate + bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used + unsigned int _jacobianAdDirs; //!< Number of AD seed vectors required for Jacobian computation - std::vector _parCellSize; //!< Particle cell / shell size - std::vector _parCenterRadius; //!< Particle cell-centered position for each particle cell + std::vector _parCellSize; //!< Particle cell / shell size + std::vector _parCenterRadius; //!< Particle cell-centered position for each particle cell std::vector _parOuterSurfAreaPerVolume; //!< Particle shell outer sphere surface to volume ratio std::vector _parInnerSurfAreaPerVolume; //!< Particle shell inner sphere surface to volume ratio @@ -365,14 +420,14 @@ class GeneralRateModel : public UnitOperationBase bool _factorizeJacobian; //!< Determines whether the Jacobian needs to be factorized double* _tempState; //!< Temporary storage with the size of the state vector or larger if binding models require it - linalg::Gmres _gmres; //!< GMRES algorithm for the Schur-complement in linearSolve() - double _schurSafety; //!< Safety factor for Schur-complement solution + linalg::Gmres _gmres; //!< GMRES algorithm for the Schur-complement in linearSolve() + double _schurSafety; //!< Safety factor for Schur-complement solution int _colParBoundaryOrder; //!< Order of the bulk-particle boundary discretization - std::vector _initC; //!< Liquid bulk phase initial conditions - std::vector _initCp; //!< Liquid particle phase initial conditions - std::vector _initQ; //!< Solid phase initial conditions - std::vector _initState; //!< Initial conditions for state vector if given + std::vector _initC; //!< Liquid bulk phase initial conditions + std::vector _initCp; //!< Liquid particle phase initial conditions + std::vector _initQ; //!< Solid phase initial conditions + std::vector _initState; //!< Initial conditions for state vector if given std::vector _initStateDot; //!< Initial conditions for time derivative BENCH_TIMER(_timerResidual) @@ -389,53 +444,136 @@ class GeneralRateModel : public UnitOperationBase BENCH_TIMER(_timerGmres) // Wrapper for calling the corresponding function in GeneralRateModel class - template - friend int schurComplementMultiplierGRM(void* userData, double const* x, double* z); + template friend int schurComplementMultiplierGRM(void* userData, double const* x, double* z); class Indexer { public: - Indexer(const Discretization& disc) : _disc(disc) { } + Indexer(const Discretization& disc) : _disc(disc) + { + } // Strides - inline int strideColCell() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideColComp() const CADET_NOEXCEPT { return 1; } + inline int strideColCell() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideColComp() const CADET_NOEXCEPT + { + return 1; + } - inline int strideParComp() const CADET_NOEXCEPT { return 1; } - inline int strideParLiquid() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideParBound(int parType) const CADET_NOEXCEPT { return static_cast(_disc.strideBound[parType]); } - inline int strideParShell(int parType) const CADET_NOEXCEPT { return strideParLiquid() + strideParBound(parType); } - inline int strideParBlock(int parType) const CADET_NOEXCEPT { return static_cast(_disc.nParCell[parType]) * strideParShell(parType); } + inline int strideParComp() const CADET_NOEXCEPT + { + return 1; + } + inline int strideParLiquid() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideParBound(int parType) const CADET_NOEXCEPT + { + return static_cast(_disc.strideBound[parType]); + } + inline int strideParShell(int parType) const CADET_NOEXCEPT + { + return strideParLiquid() + strideParBound(parType); + } + inline int strideParBlock(int parType) const CADET_NOEXCEPT + { + return static_cast(_disc.nParCell[parType]) * strideParShell(parType); + } - inline int strideFluxCell() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideFluxParType() const CADET_NOEXCEPT { return static_cast(_disc.nComp * _disc.nCol); } - inline int strideFluxComp() const CADET_NOEXCEPT { return 1; } + inline int strideFluxCell() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideFluxParType() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp * _disc.nCol); + } + inline int strideFluxComp() const CADET_NOEXCEPT + { + return 1; + } // Offsets - inline int offsetC() const CADET_NOEXCEPT { return _disc.nComp; } - inline int offsetCp() const CADET_NOEXCEPT { return _disc.nComp * _disc.nCol + offsetC(); } - inline int offsetCp(ParticleTypeIndex pti) const CADET_NOEXCEPT { return offsetCp() + _disc.parTypeOffset[pti.value]; } - inline int offsetCp(ParticleTypeIndex pti, ParticleIndex pi) const CADET_NOEXCEPT { return offsetCp() + _disc.parTypeOffset[pti.value] + strideParBlock(pti.value) * pi.value; } - inline int offsetJf() const CADET_NOEXCEPT { return offsetCp() + _disc.parTypeOffset[_disc.nParType]; } - inline int offsetJf(ParticleTypeIndex pti) const CADET_NOEXCEPT { return offsetJf() + pti.value * _disc.nCol * _disc.nComp; } - inline int offsetBoundComp(ParticleTypeIndex pti, ComponentIndex comp) const CADET_NOEXCEPT { return _disc.boundOffset[pti.value * _disc.nComp + comp.value]; } + inline int offsetC() const CADET_NOEXCEPT + { + return _disc.nComp; + } + inline int offsetCp() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nCol + offsetC(); + } + inline int offsetCp(ParticleTypeIndex pti) const CADET_NOEXCEPT + { + return offsetCp() + _disc.parTypeOffset[pti.value]; + } + inline int offsetCp(ParticleTypeIndex pti, ParticleIndex pi) const CADET_NOEXCEPT + { + return offsetCp() + _disc.parTypeOffset[pti.value] + strideParBlock(pti.value) * pi.value; + } + inline int offsetJf() const CADET_NOEXCEPT + { + return offsetCp() + _disc.parTypeOffset[_disc.nParType]; + } + inline int offsetJf(ParticleTypeIndex pti) const CADET_NOEXCEPT + { + return offsetJf() + pti.value * _disc.nCol * _disc.nComp; + } + inline int offsetBoundComp(ParticleTypeIndex pti, ComponentIndex comp) const CADET_NOEXCEPT + { + return _disc.boundOffset[pti.value * _disc.nComp + comp.value]; + } // Return pointer to first element of state variable in state vector - template inline real_t* c(real_t* const data) const { return data + offsetC(); } - template inline real_t const* c(real_t const* const data) const { return data + offsetC(); } + template inline real_t* c(real_t* const data) const + { + return data + offsetC(); + } + template inline real_t const* c(real_t const* const data) const + { + return data + offsetC(); + } - template inline real_t* cp(real_t* const data) const { return data + offsetCp(); } - template inline real_t const* cp(real_t const* const data) const { return data + offsetCp(); } + template inline real_t* cp(real_t* const data) const + { + return data + offsetCp(); + } + template inline real_t const* cp(real_t const* const data) const + { + return data + offsetCp(); + } - template inline real_t* q(real_t* const data) const { return data + offsetCp() + strideParLiquid(); } - template inline real_t const* q(real_t const* const data) const { return data + offsetCp() + strideParLiquid(); } + template inline real_t* q(real_t* const data) const + { + return data + offsetCp() + strideParLiquid(); + } + template inline real_t const* q(real_t const* const data) const + { + return data + offsetCp() + strideParLiquid(); + } - template inline real_t* jf(real_t* const data) const { return data + offsetJf(); } - template inline real_t const* jf(real_t const* const data) const { return data + offsetJf(); } + template inline real_t* jf(real_t* const data) const + { + return data + offsetJf(); + } + template inline real_t const* jf(real_t const* const data) const + { + return data + offsetJf(); + } // Return specific variable in state vector - template inline real_t& c(real_t* const data, unsigned int col, unsigned int comp) const { return data[offsetC() + comp + col * strideColCell()]; } - template inline const real_t& c(real_t const* const data, unsigned int col, unsigned int comp) const { return data[offsetC() + comp + col * strideColCell()]; } + template inline real_t& c(real_t* const data, unsigned int col, unsigned int comp) const + { + return data[offsetC() + comp + col * strideColCell()]; + } + template + inline const real_t& c(real_t const* const data, unsigned int col, unsigned int comp) const + { + return data[offsetC() + comp + col * strideColCell()]; + } protected: const Discretization& _disc; @@ -444,26 +582,73 @@ class GeneralRateModel : public UnitOperationBase class Exporter : public ISolutionExporter { public: - - Exporter(const Discretization& disc, const GeneralRateModel& model, double const* data) : _disc(disc), _idx(disc), _model(model), _data(data) { } + Exporter(const Discretization& disc, const GeneralRateModel& model, double const* data) + : _disc(disc), _idx(disc), _model(model), _data(data) + { + } Exporter(const Discretization&& disc, const GeneralRateModel& model, double const* data) = delete; - virtual bool hasParticleFlux() const CADET_NOEXCEPT { return true; } - virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT { return true; } - virtual bool hasSolidPhase() const CADET_NOEXCEPT { return _disc.strideBound[_disc.nParType] > 0; } - virtual bool hasVolume() const CADET_NOEXCEPT { return false; } - virtual bool isParticleLumped() const CADET_NOEXCEPT { return false; } - virtual bool hasPrimaryExtent() const CADET_NOEXCEPT { return true; } - - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } - virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT { return _disc.nCol; } - virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numParticleTypes() const CADET_NOEXCEPT { return _disc.nParType; } - virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT { return _disc.nParCell[parType]; } - virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT { return _disc.strideBound[parType]; } - virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT { return _disc.nComp * _disc.nCol; } + virtual bool hasParticleFlux() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasSolidPhase() const CADET_NOEXCEPT + { + return _disc.strideBound[_disc.nParType] > 0; + } + virtual bool hasVolume() const CADET_NOEXCEPT + { + return false; + } + virtual bool isParticleLumped() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasPrimaryExtent() const CADET_NOEXCEPT + { + return true; + } + + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } + virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT + { + return _disc.nCol; + } + virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numParticleTypes() const CADET_NOEXCEPT + { + return _disc.nParType; + } + virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.nParCell[parType]; + } + virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.strideBound[parType]; + } + virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nCol; + } virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT { unsigned int nDofPerParType = 0; @@ -471,7 +656,10 @@ class GeneralRateModel : public UnitOperationBase nDofPerParType += _disc.nParCell[i]; return _disc.nCol * nDofPerParType * _disc.nComp; } - virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return _disc.nCol * _disc.nParCell[parType] * _disc.nComp; } + virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.nCol * _disc.nParCell[parType] * _disc.nComp; + } virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT { unsigned int nDofPerParType = 0; @@ -479,9 +667,18 @@ class GeneralRateModel : public UnitOperationBase nDofPerParType += _disc.nParCell[i] * _disc.strideBound[i]; return _disc.nCol * nDofPerParType; } - virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return _disc.nCol * _disc.nParCell[parType] * _disc.strideBound[parType]; } - virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT { return _disc.nComp * _disc.nCol * _disc.nParType; } - virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT { return 0; } + virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.nCol * _disc.nParCell[parType] * _disc.strideBound[parType]; + } + virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nCol * _disc.nParType; + } + virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT + { + return 0; + } virtual int writeMobilePhase(double* buffer) const; virtual int writeSolidPhase(double* buffer) const; @@ -490,7 +687,10 @@ class GeneralRateModel : public UnitOperationBase virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const; virtual int writeParticleFlux(double* buffer) const; virtual int writeParticleFlux(unsigned int parType, double* buffer) const; - virtual int writeVolume(double* buffer) const { return 0; } + virtual int writeVolume(double* buffer) const + { + return 0; + } virtual int writeInlet(unsigned int port, double* buffer) const; virtual int writeInlet(double* buffer) const; virtual int writeOutlet(unsigned int port, double* buffer) const; @@ -502,7 +702,10 @@ class GeneralRateModel : public UnitOperationBase coords[i] = _model._convDispOp.cellCenter(i); return _disc.nCol; } - virtual int writeSecondaryCoordinates(double* coords) const { return 0; } + virtual int writeSecondaryCoordinates(double* coords) const + { + return 0; + } virtual int writeParticleCoordinates(unsigned int parType, double* coords) const { active const* const pcr = _model._parCenterRadius.data() + _disc.nParCellsBeforeType[parType]; @@ -528,4 +731,4 @@ IUnitOperation* createRadialFVGRM(UnitOpIdx uoId); } // namespace model } // namespace cadet -#endif // LIBCADET_GENERALRATEMODEL_HPP_ +#endif // LIBCADET_GENERALRATEMODEL_HPP_ diff --git a/src/libcadet/model/GeneralRateModel2D-InitialConditions.cpp b/src/libcadet/model/GeneralRateModel2D-InitialConditions.cpp index 301bc4dc5..eab5b3f9f 100644 --- a/src/libcadet/model/GeneralRateModel2D-InitialConditions.cpp +++ b/src/libcadet/model/GeneralRateModel2D-InitialConditions.cpp @@ -29,7 +29,7 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include +#include #endif namespace cadet @@ -38,9 +38,11 @@ namespace cadet namespace model { -int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId, unsigned int adDirection, double adValue) +int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId, unsigned int adDirection, + double adValue) { - if (pId.name == hashString("INIT_C") && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep)) + if (pId.name == hashString("INIT_C") && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && + (pId.particleType == ParTypeIndep) && (pId.component != CompIndep)) { if ((pId.reaction == ReactionIndep) && _singleRadiusInitC) { @@ -66,13 +68,16 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId { if (_singleRadiusInitCp) { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) { _sensParams.insert(&_initCp[pId.component]); for (unsigned int r = 0; r < _disc.nRad; ++r) { for (unsigned int t = 0; t < _disc.nParType; ++t) - _initCp[r * _disc.nComp * _disc.nParType + t * _disc.nComp + pId.component].setADValue(adDirection, adValue); + _initCp[r * _disc.nComp * _disc.nParType + t * _disc.nComp + pId.component].setADValue( + adDirection, adValue); } return 1; } @@ -81,11 +86,14 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId } else { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) { _sensParams.insert(&_initCp[pId.reaction * _disc.nComp * _disc.nParType + pId.component]); for (unsigned int t = 0; t < _disc.nParType; ++t) - _initCp[pId.reaction * _disc.nComp * _disc.nParType + t * _disc.nComp + pId.component].setADValue(adDirection, adValue); + _initCp[pId.reaction * _disc.nComp * _disc.nParType + t * _disc.nComp + pId.component].setADValue( + adDirection, adValue); return 1; } @@ -95,13 +103,18 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId if (_singleRadiusInitQ) { - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) { - _sensParams.insert(&_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState]); + _sensParams.insert( + &_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState]); for (unsigned int r = 0; r < _disc.nRad; ++r) { for (unsigned int t = 0; t < _disc.nParType; ++t) - _initQ[r * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState].setADValue(adDirection, adValue); + _initQ[r * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[t] + + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState] + .setADValue(adDirection, adValue); } return 1; } @@ -110,11 +123,17 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId } else { - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) { - _sensParams.insert(&_initQ[pId.reaction * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState]); + _sensParams.insert( + &_initQ[pId.reaction * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[0] + + _disc.boundOffset[pId.component] + pId.boundState]); for (unsigned int t = 0; t < _disc.nParType; ++t) - _initQ[pId.reaction * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState].setADValue(adDirection, adValue); + _initQ[pId.reaction * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[t] + + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState] + .setADValue(adDirection, adValue); return 1; } @@ -126,11 +145,14 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId { if (_singleRadiusInitCp) { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) { _sensParams.insert(&_initCp[pId.particleType * _disc.nComp + pId.component]); for (unsigned int r = 0; r < _disc.nRad; ++r) - _initCp[r * _disc.nComp * _disc.nParType + pId.particleType * _disc.nComp + pId.component].setADValue(adDirection, adValue); + _initCp[r * _disc.nComp * _disc.nParType + pId.particleType * _disc.nComp + pId.component] + .setADValue(adDirection, adValue); return 1; } @@ -139,10 +161,14 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId } else { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) { - _sensParams.insert(&_initCp[pId.reaction * _disc.nComp * _disc.nParType + pId.particleType * _disc.nComp + pId.component]); - _initCp[pId.reaction * _disc.nComp * _disc.nParType + pId.particleType * _disc.nComp + pId.component].setADValue(adDirection, adValue); + _sensParams.insert(&_initCp[pId.reaction * _disc.nComp * _disc.nParType + + pId.particleType * _disc.nComp + pId.component]); + _initCp[pId.reaction * _disc.nComp * _disc.nParType + pId.particleType * _disc.nComp + pId.component] + .setADValue(adDirection, adValue); return 1; } @@ -152,11 +178,17 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId if (_singleRadiusInitQ) { - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) { - _sensParams.insert(&_initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState]); + _sensParams.insert( + &_initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState]); for (unsigned int r = 0; r < _disc.nRad; ++r) - _initQ[r * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState].setADValue(adDirection, adValue); + _initQ[r * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState] + .setADValue(adDirection, adValue); return 1; } @@ -165,10 +197,17 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId } else { - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) { - _sensParams.insert(&_initQ[pId.reaction * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState]); - _initQ[pId.reaction * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState].setADValue(adDirection, adValue); + _sensParams.insert( + &_initQ[pId.reaction * _disc.strideBound[_disc.nParType] + + _disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState]); + _initQ[pId.reaction * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState] + .setADValue(adDirection, adValue); return 1; } @@ -181,7 +220,8 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId, double val, bool checkSens) { - if (pId.name == hashString("INIT_C") && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep)) + if (pId.name == hashString("INIT_C") && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && + (pId.particleType == ParTypeIndep) && (pId.component != CompIndep)) { if ((pId.reaction == ReactionIndep) && _singleRadiusInitC) { @@ -209,7 +249,9 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId { if (_singleRadiusInitCp) { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) { if (checkSens && !contains(_sensParams, &_initCp[pId.component])) return -1; @@ -227,13 +269,17 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId } else { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) { - if (checkSens && !contains(_sensParams, &_initCp[pId.reaction * _disc.nComp * _disc.nParType + pId.component])) + if (checkSens && + !contains(_sensParams, &_initCp[pId.reaction * _disc.nComp * _disc.nParType + pId.component])) return -1; for (unsigned int t = 0; t < _disc.nParType; ++t) - _initCp[pId.reaction * _disc.nComp * _disc.nParType + t * _disc.nComp + pId.component].setValue(val); + _initCp[pId.reaction * _disc.nComp * _disc.nParType + t * _disc.nComp + pId.component].setValue( + val); return 1; } @@ -243,15 +289,21 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId if (_singleRadiusInitQ) { - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) { - if (checkSens && !contains(_sensParams, &_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState])) + if (checkSens && + !contains(_sensParams, + &_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState])) return -1; for (unsigned int r = 0; r < _disc.nRad; ++r) { for (unsigned int t = 0; t < _disc.nParType; ++t) - _initQ[r * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState].setValue(val); + _initQ[r * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[t] + + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState] + .setValue(val); } return 1; @@ -261,13 +313,20 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId } else { - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) { - if (checkSens && !contains(_sensParams, &_initQ[pId.reaction * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState])) + if (checkSens && + !contains(_sensParams, + &_initQ[pId.reaction * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[0] + + _disc.boundOffset[pId.component] + pId.boundState])) return -1; for (unsigned int t = 0; t < _disc.nParType; ++t) - _initQ[pId.reaction * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState].setValue(val); + _initQ[pId.reaction * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[t] + + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState] + .setValue(val); return 1; } @@ -279,13 +338,16 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId { if (_singleRadiusInitCp) { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) { if (checkSens && !contains(_sensParams, &_initCp[pId.particleType * _disc.nComp + pId.component])) return -1; for (unsigned int r = 0; r < _disc.nRad; ++r) - _initCp[r * _disc.nComp * _disc.nParType + pId.particleType * _disc.nComp + pId.component].setValue(val); + _initCp[r * _disc.nComp * _disc.nParType + pId.particleType * _disc.nComp + pId.component].setValue( + val); return 1; } @@ -294,12 +356,16 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId } else { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) { - if (checkSens && !contains(_sensParams, &_initCp[pId.reaction * _disc.nComp * _disc.nParType + pId.particleType * _disc.nComp + pId.component])) + if (checkSens && !contains(_sensParams, &_initCp[pId.reaction * _disc.nComp * _disc.nParType + + pId.particleType * _disc.nComp + pId.component])) return -1; - _initCp[pId.reaction * _disc.nComp * _disc.nParType + pId.particleType * _disc.nComp + pId.component].setValue(val); + _initCp[pId.reaction * _disc.nComp * _disc.nParType + pId.particleType * _disc.nComp + pId.component] + .setValue(val); return 1; } @@ -309,13 +375,21 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId if (_singleRadiusInitQ) { - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) { - if (checkSens && !contains(_sensParams, &_initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState])) + if (checkSens && + !contains( + _sensParams, + &_initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState])) return -1; for (unsigned int r = 0; r < _disc.nRad; ++r) - _initQ[r * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState].setValue(val); + _initQ[r * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState] + .setValue(val); return 1; } @@ -324,12 +398,21 @@ int GeneralRateModel2D::multiplexInitialConditions(const cadet::ParameterId& pId } else { - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && + (pId.component != CompIndep) && (pId.reaction != ReactionIndep)) { - if (checkSens && !contains(_sensParams, &_initQ[pId.reaction * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState])) + if (checkSens && + !contains( + _sensParams, + &_initQ[pId.reaction * _disc.strideBound[_disc.nParType] + + _disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState])) return -1; - _initQ[pId.reaction * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState].setValue(val); + _initQ[pId.reaction * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState] + .setValue(val); return 1; } @@ -353,7 +436,8 @@ void GeneralRateModel2D::applyInitialCondition(const SimulationState& simState) if (!_initStateDot.empty()) { std::fill(simState.vecStateYdot, simState.vecStateYdot + idxr.offsetC(), 0.0); - std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), simState.vecStateYdot + idxr.offsetC()); + std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), + simState.vecStateYdot + idxr.offsetC()); } else std::fill(simState.vecStateYdot, simState.vecStateYdot + numDofs(), 0.0); @@ -371,7 +455,8 @@ void GeneralRateModel2D::applyInitialCondition(const SimulationState& simState) { // Loop over components in cell for (unsigned comp = 0; comp < _disc.nComp; ++comp) - stateYbulk[col * idxr.strideColAxialCell() + rad * idxr.strideColRadialCell() + comp * idxr.strideColComp()] = static_cast(_initC[comp + rad * _disc.nComp]); + stateYbulk[col * idxr.strideColAxialCell() + rad * idxr.strideColRadialCell() + + comp * idxr.strideColComp()] = static_cast(_initC[comp + rad * _disc.nComp]); } } @@ -390,10 +475,12 @@ void GeneralRateModel2D::applyInitialCondition(const SimulationState& simState) // Initialize c_p for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - simState.vecStateY[shellOffset + comp] = static_cast(_initCp[comp + _disc.nComp * type + rad * _disc.nComp * _disc.nParType]); + simState.vecStateY[shellOffset + comp] = + static_cast(_initCp[comp + _disc.nComp * type + rad * _disc.nComp * _disc.nParType]); // Initialize q - active const* const iq = _initQ.data() + rad * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[type]; + active const* const iq = + _initQ.data() + rad * _disc.strideBound[_disc.nParType] + _disc.nBoundBeforeType[type]; for (unsigned int bnd = 0; bnd < _disc.strideBound[type]; ++bnd) simState.vecStateY[shellOffset + idxr.strideParLiquid() + bnd] = static_cast(iq[bnd]); } @@ -414,14 +501,16 @@ void GeneralRateModel2D::readInitialCondition(IParameterProvider& paramProvider) // Check if INIT_STATE contains the full state and its time derivative if (initState.size() >= 2 * numPureDofs()) - _initStateDot = std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); + _initStateDot = + std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); return; } const std::vector initC = paramProvider.getDoubleArray("INIT_C"); _singleRadiusInitC = (initC.size() < _disc.nComp * _disc.nRad); - if (((initC.size() < _disc.nComp) && _singleRadiusInitC) || ((initC.size() < _disc.nComp * _disc.nRad) && !_singleRadiusInitC)) + if (((initC.size() < _disc.nComp) && _singleRadiusInitC) || + ((initC.size() < _disc.nComp * _disc.nRad) && !_singleRadiusInitC)) throw InvalidParameterException("INIT_C does not contain enough values for all components (and radial zones)"); if (!_singleRadiusInitC) @@ -439,11 +528,10 @@ void GeneralRateModel2D::readInitialCondition(IParameterProvider& paramProvider) _singleRadiusInitCp = (initCp.size() == _disc.nComp * _disc.nParType) || (initCp.size() == _disc.nComp); - if ( - ((initCp.size() < _disc.nComp * _disc.nParType) && !_singleBinding && _singleRadiusInitCp) || ((initCp.size() < _disc.nComp) && _singleBinding && _singleRadiusInitCp) - || ((initCp.size() < _disc.nComp * _disc.nRad) && _singleBinding && !_singleRadiusInitCp) - || ((initCp.size() < _disc.nComp * _disc.nParType * _disc.nRad) && !_singleBinding && !_singleRadiusInitCp) - ) + if (((initCp.size() < _disc.nComp * _disc.nParType) && !_singleBinding && _singleRadiusInitCp) || + ((initCp.size() < _disc.nComp) && _singleBinding && _singleRadiusInitCp) || + ((initCp.size() < _disc.nComp * _disc.nRad) && _singleBinding && !_singleRadiusInitCp) || + ((initCp.size() < _disc.nComp * _disc.nParType * _disc.nRad) && !_singleBinding && !_singleRadiusInitCp)) throw InvalidParameterException("INIT_CP does not contain enough values for all components"); if (!_singleBinding && !_singleRadiusInitCp) @@ -451,14 +539,16 @@ void GeneralRateModel2D::readInitialCondition(IParameterProvider& paramProvider) else if (!_singleBinding && _singleRadiusInitCp) { for (unsigned int r = 0; r < _disc.nRad; ++r) - ad::copyToAd(initCp.data(), _initCp.data() + r * _disc.nComp * _disc.nParType, _disc.nComp * _disc.nParType); + ad::copyToAd(initCp.data(), _initCp.data() + r * _disc.nComp * _disc.nParType, + _disc.nComp * _disc.nParType); } else if (_singleBinding && !_singleRadiusInitCp) { for (unsigned int r = 0; r < _disc.nRad; ++r) { for (unsigned int t = 0; t < _disc.nParType; ++t) - ad::copyToAd(initCp.data() + r * _disc.nComp, _initCp.data() + t * _disc.nComp + r * _disc.nComp * _disc.nParType, _disc.nComp); + ad::copyToAd(initCp.data() + r * _disc.nComp, + _initCp.data() + t * _disc.nComp + r * _disc.nComp * _disc.nParType, _disc.nComp); } } else if (_singleBinding && _singleRadiusInitCp) @@ -466,7 +556,8 @@ void GeneralRateModel2D::readInitialCondition(IParameterProvider& paramProvider) for (unsigned int r = 0; r < _disc.nRad; ++r) { for (unsigned int t = 0; t < _disc.nParType; ++t) - ad::copyToAd(initCp.data(), _initCp.data() + t * _disc.nComp + r * _disc.nComp * _disc.nParType, _disc.nComp); + ad::copyToAd(initCp.data(), _initCp.data() + t * _disc.nComp + r * _disc.nComp * _disc.nParType, + _disc.nComp); } } } @@ -479,7 +570,8 @@ void GeneralRateModel2D::readInitialCondition(IParameterProvider& paramProvider) for (unsigned int r = 0; r < _disc.nRad; ++r) { for (unsigned int t = 0; t < _disc.nParType; ++t) - ad::copyToAd(initC.data() + r * _disc.nComp, _initCp.data() + t * _disc.nComp + r * _disc.nComp * _disc.nParType, _disc.nComp); + ad::copyToAd(initC.data() + r * _disc.nComp, + _initCp.data() + t * _disc.nComp + r * _disc.nComp * _disc.nParType, _disc.nComp); } } else @@ -487,7 +579,8 @@ void GeneralRateModel2D::readInitialCondition(IParameterProvider& paramProvider) for (unsigned int r = 0; r < _disc.nRad; ++r) { for (unsigned int t = 0; t < _disc.nParType; ++t) - ad::copyToAd(initC.data(), _initCp.data() + t * _disc.nComp + r * _disc.nComp * _disc.nParType, _disc.nComp); + ad::copyToAd(initC.data(), _initCp.data() + t * _disc.nComp + r * _disc.nComp * _disc.nParType, + _disc.nComp); } } } @@ -496,17 +589,18 @@ void GeneralRateModel2D::readInitialCondition(IParameterProvider& paramProvider) if (paramProvider.exists("INIT_Q")) { initQ = paramProvider.getDoubleArray("INIT_Q"); - _singleRadiusInitQ = (initQ.size() == _disc.strideBound[0]) || (initQ.size() == _disc.strideBound[_disc.nParType]); + _singleRadiusInitQ = + (initQ.size() == _disc.strideBound[0]) || (initQ.size() == _disc.strideBound[_disc.nParType]); } if (initQ.empty() || (_disc.strideBound[_disc.nParType] == 0)) return; - if ((_disc.strideBound[_disc.nParType] > 0) && ( - ((initQ.size() < _disc.strideBound[_disc.nParType]) && !_singleBinding && _singleRadiusInitQ) || ((initQ.size() < _disc.strideBound[0]) && _singleBinding && _singleRadiusInitQ) - || ((initQ.size() < _disc.strideBound[0] * _disc.nRad) && _singleBinding && !_singleRadiusInitQ) - || ((initQ.size() < _disc.strideBound[_disc.nParType] * _disc.nRad) && !_singleBinding && !_singleRadiusInitQ) - )) + if ((_disc.strideBound[_disc.nParType] > 0) && + (((initQ.size() < _disc.strideBound[_disc.nParType]) && !_singleBinding && _singleRadiusInitQ) || + ((initQ.size() < _disc.strideBound[0]) && _singleBinding && _singleRadiusInitQ) || + ((initQ.size() < _disc.strideBound[0] * _disc.nRad) && _singleBinding && !_singleRadiusInitQ) || + ((initQ.size() < _disc.strideBound[_disc.nParType] * _disc.nRad) && !_singleBinding && !_singleRadiusInitQ))) throw InvalidParameterException("INIT_Q does not contain enough values for all bound states"); if (!_singleBinding && !_singleRadiusInitQ) @@ -514,14 +608,17 @@ void GeneralRateModel2D::readInitialCondition(IParameterProvider& paramProvider) else if (!_singleBinding && _singleRadiusInitQ) { for (unsigned int r = 0; r < _disc.nRad; ++r) - ad::copyToAd(initQ.data(), _initQ.data() + r * _disc.strideBound[_disc.nParType], _disc.strideBound[_disc.nParType]); + ad::copyToAd(initQ.data(), _initQ.data() + r * _disc.strideBound[_disc.nParType], + _disc.strideBound[_disc.nParType]); } else if (_singleBinding && !_singleRadiusInitQ) { for (unsigned int r = 0; r < _disc.nRad; ++r) { for (unsigned int t = 0; t < _disc.nParType; ++t) - ad::copyToAd(initQ.data() + r * _disc.strideBound[0], _initQ.data() + _disc.nBoundBeforeType[t] + r * _disc.strideBound[_disc.nParType], _disc.strideBound[t]); + ad::copyToAd(initQ.data() + r * _disc.strideBound[0], + _initQ.data() + _disc.nBoundBeforeType[t] + r * _disc.strideBound[_disc.nParType], + _disc.strideBound[t]); } } else if (_singleBinding && _singleRadiusInitQ) @@ -529,7 +626,9 @@ void GeneralRateModel2D::readInitialCondition(IParameterProvider& paramProvider) for (unsigned int r = 0; r < _disc.nRad; ++r) { for (unsigned int t = 0; t < _disc.nParType; ++t) - ad::copyToAd(initQ.data(), _initQ.data() + _disc.nBoundBeforeType[t] + r * _disc.strideBound[_disc.nParType], _disc.strideBound[t]); + ad::copyToAd(initQ.data(), + _initQ.data() + _disc.nBoundBeforeType[t] + r * _disc.strideBound[_disc.nParType], + _disc.strideBound[t]); } } } @@ -583,7 +682,9 @@ void GeneralRateModel2D::readInitialCondition(IParameterProvider& paramProvider) * @param [in] errorTol Error tolerance for algebraic equations * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ -void GeneralRateModel2D::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModel2D::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -623,7 +724,7 @@ void GeneralRateModel2D::consistentInitialState(const SimulationTime& simTime, d const linalg::ConstMaskArray mask{qsMask.data(), static_cast(_disc.nComp + _disc.strideBound[type])}; const int probSize = linalg::numMaskActive(mask); - //Problem capturing variables here + // Problem capturing variables here #ifdef CADET_PARALLELIZE BENCH_SCOPE(_timerConsistentInitPar); tbb::parallel_for(std::size_t(0), static_cast(_disc.nCol * _disc.nRad), [&](std::size_t pblk) @@ -634,12 +735,14 @@ void GeneralRateModel2D::consistentInitialState(const SimulationTime& simTime, d LinearBufferAllocator tlmAlloc = threadLocalMem.get(); // Reuse memory of band matrix for dense matrix - linalg::DenseMatrixView fullJacobianMatrix(_jacPdisc[type * _disc.nCol * _disc.nRad + pblk].data(), nullptr, mask.len, mask.len); + linalg::DenseMatrixView fullJacobianMatrix(_jacPdisc[type * _disc.nCol * _disc.nRad + pblk].data(), nullptr, + mask.len, mask.len); // Midpoint of current column cell (z, rho coordinate) - needed in externally dependent adsorption kinetic const unsigned int axialCell = pblk / _disc.nRad; -// const unsigned int radialCell = pblk % _disc.nRad; -// const double r = static_cast(_convDispOp.radialCenters()[radialCell]) / static_cast(_convDispOp.columnRadius()); + // const unsigned int radialCell = pblk % _disc.nRad; + // const double r = static_cast(_convDispOp.radialCenters()[radialCell]) / + // static_cast(_convDispOp.columnRadius()); const double z = (0.5 + static_cast(axialCell)) / static_cast(_disc.nCol); // Get workspace memory @@ -661,35 +764,44 @@ void GeneralRateModel2D::consistentInitialState(const SimulationTime& simTime, d BufferedArray conservedQuantsBuffer = tlmAlloc.array(numActiveComp); double* const conservedQuants = static_cast(conservedQuantsBuffer); - linalg::DenseMatrixView jacobianMatrix(jacobianMem, _jacPdisc[type * _disc.nCol * _disc.nRad + pblk].pivot(), probSize, probSize); - const parts::cell::CellParameters cellResParams - { - _disc.nComp, - _disc.nBound + _disc.nComp * type, - _disc.boundOffset + _disc.nComp * type, - _disc.strideBound[type], - mask.mask + _disc.nComp, - _parPorosity[type], - _poreAccessFactor.data() + _disc.nComp * type, - _binding[type], - (_dynReaction[type] && (_dynReaction[type]->numReactionsCombined() > 0)) ? _dynReaction[type] : nullptr - }; - - // This loop cannot be run in parallel without creating a Jacobian matrix for each thread which would increase memory usage - const int localOffsetToParticle = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); - for(std::size_t shell = 0; shell < static_cast(_disc.nParCell[type]); ++shell) + linalg::DenseMatrixView jacobianMatrix( + jacobianMem, _jacPdisc[type * _disc.nCol * _disc.nRad + pblk].pivot(), probSize, probSize); + const parts::cell::CellParameters cellResParams{ + _disc.nComp, + _disc.nBound + _disc.nComp * type, + _disc.boundOffset + _disc.nComp * type, + _disc.strideBound[type], + mask.mask + _disc.nComp, + _parPorosity[type], + _poreAccessFactor.data() + _disc.nComp * type, + _binding[type], + (_dynReaction[type] && (_dynReaction[type]->numReactionsCombined() > 0)) ? _dynReaction[type] + : nullptr}; + + // This loop cannot be run in parallel without creating a Jacobian matrix for each thread which would + // increase memory usage + const int localOffsetToParticle = + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); + for (std::size_t shell = 0; shell < static_cast(_disc.nParCell[type]); ++shell) { const int localOffsetInParticle = static_cast(shell) * idxr.strideParShell(type); // Get pointer to q variables in a shell of particle pblk - double* const qShell = vecStateY + localOffsetToParticle + localOffsetInParticle + idxr.strideParLiquid(); - active* const localAdRes = adJac.adRes ? adJac.adRes + localOffsetToParticle + localOffsetInParticle : nullptr; - active* const localAdY = adJac.adY ? adJac.adY + localOffsetToParticle + localOffsetInParticle : nullptr; - - const ColumnPosition colPos{z, 0.0, static_cast(_parCenterRadius[_disc.nParCellsBeforeType[type] + shell]) / static_cast(_parRadius[type])}; + double* const qShell = + vecStateY + localOffsetToParticle + localOffsetInParticle + idxr.strideParLiquid(); + active* const localAdRes = + adJac.adRes ? adJac.adRes + localOffsetToParticle + localOffsetInParticle : nullptr; + active* const localAdY = + adJac.adY ? adJac.adY + localOffsetToParticle + localOffsetInParticle : nullptr; + + const ColumnPosition colPos{ + z, 0.0, + static_cast(_parCenterRadius[_disc.nParCellsBeforeType[type] + shell]) / + static_cast(_parRadius[type])}; // Determine whether nonlinear solver is required - if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - idxr.strideParLiquid(), tlmAlloc)) + if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - idxr.strideParLiquid(), tlmAlloc)) continue; // Extract initial values from current state @@ -697,15 +809,16 @@ void GeneralRateModel2D::consistentInitialState(const SimulationTime& simTime, d // Save values of conserved moieties const double epsQ = 1.0 - static_cast(_parPorosity[type]); - linalg::conservedMoietiesFromPartitionedMask(mask, _disc.nBound + type * _disc.nComp, _disc.nComp, qShell - _disc.nComp, conservedQuants, static_cast(_parPorosity[type]), epsQ); + linalg::conservedMoietiesFromPartitionedMask(mask, _disc.nBound + type * _disc.nComp, _disc.nComp, + qShell - _disc.nComp, conservedQuants, + static_cast(_parPorosity[type]), epsQ); std::function jacFunc; if (localAdY && localAdRes) { - jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) - { - // Copy over state vector to AD state vector (without changing directional values to keep seed vectors) - // and initialize residuals with zero (also resetting directional values) + jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) { + // Copy over state vector to AD state vector (without changing directional values to keep seed + // vectors) and initialize residuals with zero (also resetting directional values) ad::copyToAd(qShell - _disc.nComp, localAdY, mask.len); // @todo Check if this is necessary ad::resetAd(localAdRes, mask.len); @@ -714,32 +827,36 @@ void GeneralRateModel2D::consistentInitialState(const SimulationTime& simTime, d linalg::applyVectorSubset(x, mask, localAdY); // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, localAdY, nullptr, localAdRes, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, localAdY, nullptr, localAdRes, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); #ifdef CADET_CHECK_ANALYTIC_JACOBIAN std::copy_n(qShell - _disc.nComp, mask.len, fullX); linalg::applyVectorSubset(x, mask, fullX); // Compute analytic Jacobian - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); // Compare const double diff = ad::compareDenseJacobianWithBandedAd( - localAdRes - localOffsetInParticle, localOffsetInParticle, adJac.adDirOffset, _jacP[type * _disc.nCol * _disc.nRad].lowerBandwidth(), - _jacP[type * _disc.nCol * _disc.nRad].lowerBandwidth(), _jacP[type * _disc.nCol * _disc.nRad].upperBandwidth(), fullJacobianMatrix - ); + localAdRes - localOffsetInParticle, localOffsetInParticle, adJac.adDirOffset, + _jacP[type * _disc.nCol * _disc.nRad].lowerBandwidth(), + _jacP[type * _disc.nCol * _disc.nRad].lowerBandwidth(), + _jacP[type * _disc.nCol * _disc.nRad].upperBandwidth(), fullJacobianMatrix); LOG(Debug) << "MaxDiff: " << diff; #endif // Extract Jacobian from AD ad::extractDenseJacobianFromBandedAd( - localAdRes - localOffsetInParticle, localOffsetInParticle, adJac.adDirOffset, _jacP[type * _disc.nCol * _disc.nRad].lowerBandwidth(), - _jacP[type * _disc.nCol * _disc.nRad].lowerBandwidth(), _jacP[type * _disc.nCol * _disc.nRad].upperBandwidth(), fullJacobianMatrix - ); + localAdRes - localOffsetInParticle, localOffsetInParticle, adJac.adDirOffset, + _jacP[type * _disc.nCol * _disc.nRad].lowerBandwidth(), + _jacP[type * _disc.nCol * _disc.nRad].lowerBandwidth(), + _jacP[type * _disc.nCol * _disc.nRad].upperBandwidth(), fullJacobianMatrix); // Extract Jacobian from full Jacobian mat.setAll(0.0); @@ -778,16 +895,16 @@ void GeneralRateModel2D::consistentInitialState(const SimulationTime& simTime, d } else { - jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) - { + jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) { // Prepare input vector by overwriting masked items std::copy_n(qShell - _disc.nComp, mask.len, fullX); linalg::applyVectorSubset(x, mask, fullX); // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); // Extract Jacobian from full Jacobian mat.setAll(0.0); @@ -827,16 +944,16 @@ void GeneralRateModel2D::consistentInitialState(const SimulationTime& simTime, d // Apply nonlinear solver _nonlinearSolver->solve( - [&](double const* const x, double* const r) - { + [&](double const* const x, double* const r) { // Prepare input vector by overwriting masked items std::copy_n(qShell - _disc.nComp, mask.len, fullX); linalg::applyVectorSubset(x, mask, fullX); // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); // Extract values from residual linalg::selectVectorSubset(fullResidual, mask, r); @@ -876,7 +993,8 @@ void GeneralRateModel2D::consistentInitialState(const SimulationTime& simTime, d linalg::applyVectorSubset(solution, mask, qShell - idxr.strideParLiquid()); // Refine / correct solution - _binding[type]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - idxr.strideParLiquid(), tlmAlloc); + _binding[type]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - idxr.strideParLiquid(), tlmAlloc); } } CADET_PARFOR_END; } @@ -935,9 +1053,12 @@ void GeneralRateModel2D::consistentInitialState(const SimulationTime& simTime, d * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] vecStateY Consistently initialized state vector - * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent state time derivatives. + * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent + * state time derivatives. */ -void GeneralRateModel2D::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModel2D::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -968,7 +1089,8 @@ void GeneralRateModel2D::consistentInitialTimeDerivative(const SimulationTime& s // Midpoint of current column cell (z, rho coordinate) - needed in externally dependent adsorption kinetic const unsigned int axialCell = par / _disc.nRad; const unsigned int radialCell = par % _disc.nRad; - const double r = static_cast(_convDispOp.radialCenters()[radialCell]) / static_cast(_convDispOp.columnRadius()); + const double r = static_cast(_convDispOp.radialCenters()[radialCell]) / + static_cast(_convDispOp.columnRadius()); const double z = (0.5 + static_cast(axialCell)) / static_cast(_disc.nCol); // Assemble @@ -989,18 +1111,24 @@ void GeneralRateModel2D::consistentInitialTimeDerivative(const SimulationTime& s continue; // Get iterators to beginning of solid phase - linalg::BandMatrix::RowIterator jacSolidOrig = _jacP[pblk].row(j * static_cast(idxr.strideParShell(type)) + static_cast(idxr.strideParLiquid())); + linalg::BandMatrix::RowIterator jacSolidOrig = + _jacP[pblk].row(j * static_cast(idxr.strideParShell(type)) + + static_cast(idxr.strideParLiquid())); linalg::FactorizableBandMatrix::RowIterator jacSolid = jac - idxr.strideParBound(type); int const* const mask = _binding[type]->reactionQuasiStationarity(); - double* const qShellDot = vecStateYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}) + static_cast(j) * idxr.strideParShell(type) + idxr.strideParLiquid(); + double* const qShellDot = vecStateYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}) + + static_cast(j) * idxr.strideParShell(type) + idxr.strideParLiquid(); // Obtain derivative of fluxes wrt. time std::fill_n(dFluxDt, _disc.strideBound[type], 0.0); if (_binding[type]->dependsOnTime()) { - _binding[type]->timeDerivativeQuasiStationaryFluxes(simTime.t, simTime.secIdx, - ColumnPosition{z, r, static_cast(_parCenterRadius[_disc.nParCellsBeforeType[type] + j]) / static_cast(_parRadius[type])}, + _binding[type]->timeDerivativeQuasiStationaryFluxes( + simTime.t, simTime.secIdx, + ColumnPosition{z, r, + static_cast(_parCenterRadius[_disc.nParCellsBeforeType[type] + j]) / + static_cast(_parRadius[type])}, qShellDot - _disc.nComp, qShellDot, dFluxDt, tlmAlloc); } @@ -1024,11 +1152,13 @@ void GeneralRateModel2D::consistentInitialTimeDerivative(const SimulationTime& s const bool result = fbm.factorize(); if (!result) { - LOG(Error) << "Factorize() failed for par block " << pblk << " (type " << type << " col " << par << ")\n" << fbm; + LOG(Error) << "Factorize() failed for par block " << pblk << " (type " << type << " col " << par << ")\n" + << fbm; } // Solve - const bool result2 = fbm.solve(scaleFactors, vecStateYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par})); + const bool result2 = + fbm.solve(scaleFactors, vecStateYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par})); if (!result2) { LOG(Error) << "Solve() failed for par block " << pblk << " (type " << type << " col " << par << ")"; @@ -1048,8 +1178,6 @@ void GeneralRateModel2D::consistentInitialTimeDerivative(const SimulationTime& s solveForFluxes(vecStateYdot, idxr); } - - /** * @brief Computes approximately / partially consistent initial values (state variables without their time derivatives) * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have @@ -1092,10 +1220,13 @@ void GeneralRateModel2D::consistentInitialTimeDerivative(const SimulationTime& s * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) * @param [in] errorTol Error tolerance for algebraic equations */ -void GeneralRateModel2D::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModel2D::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) { if (isSectionDependent(_parDiffusionMode) || isSectionDependent(_parSurfDiffusionMode)) - LOG(Warning) << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; + LOG(Warning) + << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; BENCH_SCOPE(_timerConsistentInit); @@ -1149,13 +1280,18 @@ void GeneralRateModel2D::leanConsistentInitialState(const SimulationTime& simTim * * @param [in] t Current time point * @param [in] vecStateY (Lean) consistently initialized state vector - * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time derivatives. - * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during execution of the function. + * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time + * derivatives. + * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during + * execution of the function. */ -void GeneralRateModel2D::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModel2D::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem) { if (isSectionDependent(_parDiffusionMode) || isSectionDependent(_parSurfDiffusionMode)) - LOG(Warning) << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; + LOG(Warning) + << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; BENCH_SCOPE(_timerConsistentInit); @@ -1203,7 +1339,8 @@ void GeneralRateModel2D::initializeSensitivityStates(const std::vector& { // Loop over components in cell for (unsigned comp = 0; comp < _disc.nComp; ++comp) - stateYbulk[col * idxr.strideColAxialCell() + rad * idxr.strideColRadialCell() + comp * idxr.strideColComp()] = _initC[comp + rad * _disc.nComp].getADValue(param); + stateYbulk[col * idxr.strideColAxialCell() + rad * idxr.strideColRadialCell() + + comp * idxr.strideColComp()] = _initC[comp + rad * _disc.nComp].getADValue(param); } } @@ -1224,11 +1361,14 @@ void GeneralRateModel2D::initializeSensitivityStates(const std::vector& // Initialize c_p for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - stateYparticle[comp] = _initCp[comp + type * _disc.nComp + rad * _disc.nComp * _disc.nParType].getADValue(param); + stateYparticle[comp] = + _initCp[comp + type * _disc.nComp + rad * _disc.nComp * _disc.nParType].getADValue(param); // Initialize q for (unsigned int bnd = 0; bnd < _disc.strideBound[type]; ++bnd) - stateYparticleSolid[bnd] = _initQ[bnd + _disc.nBoundBeforeType[type] + _disc.strideBound[_disc.nParType] * rad].getADValue(param); + stateYparticleSolid[bnd] = + _initQ[bnd + _disc.nBoundBeforeType[type] + _disc.strideBound[_disc.nParType] * rad] + .getADValue(param); } } } @@ -1239,25 +1379,26 @@ void GeneralRateModel2D::initializeSensitivityStates(const std::vector& * @brief Computes consistent initial values and time derivatives of sensitivity subsystems * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, * the sensitivity system for a parameter @f$ p @f$ reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of this linear DAE, @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ - * have to be consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the time - * derivative \f$ \dot{s}_0 \f$ such that they are consistent. + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of this linear DAE, @f$ s_0 = + * \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ have to be + * consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the + * time derivative \f$ \dot{s}_0 \f$ such that they are consistent. * - * The process follows closely the one of consistentInitialConditions() and, in fact, is a linearized version of it. - * This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ are - * related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them - * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p}. @f$ - *
            - *
          1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria). - * Once all @f$ c_i @f$, @f$ c_{p,i} @f$, and @f$ q_i^{(j)} @f$ have been computed, solve for the - * fluxes @f$ j_{f,i} @f$. Let @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at this point, we have - * \f[ \left( \frac{\partial F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, y_0, \dot{y}_0) \right)_{\mathcal{I}_a} = 0. \f]
          2. - *
          3. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. - * However, because of the algebraic equations, we need additional conditions to fully determine + * The process follows closely the one of consistentInitialConditions() and, in fact, is a linearized version + * of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ + * are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them + * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial + * \dot{y}_0}{\partial p}. @f$
            1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, + * reaction equilibria). Once all @f$ c_i @f$, @f$ c_{p,i} @f$, and @f$ q_i^{(j)} @f$ have been computed, solve for the + * fluxes @f$ j_{f,i} @f$. Let @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at + * this point, we have \f[ \left( \frac{\partial F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, + * y_0, \dot{y}_0) \right)_{\mathcal{I}_a} = 0. \f]
            2. Compute the time derivatives of the sensitivity @f$ \dot{s} + * @f$ such that the differential equations hold. However, because of the algebraic equations, we need additional + * conditions to fully determine * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the - * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting system - * has a similar structure as the system Jacobian. + * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting + * system has a similar structure as the system Jacobian. * @f[ \begin{align} * \left[\begin{array}{c|ccc|c} * \dot{J}_0 & & & & \\ @@ -1273,8 +1414,9 @@ void GeneralRateModel2D::initializeSensitivityStates(const std::vector& * @f$ J_{i,f} @f$ matrices in the right column are missing. * * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. - * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] - * which is 0 for algebraic equations (@f$ -\frac{\partial^2 F}{\partial t \partial p}@f$, to be more precise). + * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - + * \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] which is 0 for algebraic equations (@f$ -\frac{\partial^2 + * F}{\partial t \partial p}@f$, to be more precise). * * The linear system is solved by backsubstitution. First, the diagonal blocks are solved in parallel. * Then, the equations for the fluxes @f$ j_f @f$ are solved by substituting in the solution of the @@ -1288,8 +1430,11 @@ void GeneralRateModel2D::initializeSensitivityStates(const std::vector& * @param [in] adRes Pointer to residual vector of AD datatypes with parameter sensitivities * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ -void GeneralRateModel2D::consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModel2D::consistentInitialSensitivity(const SimulationTime& simTime, + const ConstSimulationState& simState, + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -1324,7 +1469,9 @@ void GeneralRateModel2D::consistentInitialSensitivity(const SimulationTime& simT #endif { // Reuse memory of band matrix for dense matrix - linalg::DenseMatrixView jacobianMatrix(_jacPdisc[type * _disc.nCol * _disc.nRad + pblk].data(), _jacPdisc[type * _disc.nCol * _disc.nRad + pblk].pivot(), probSize, probSize); + linalg::DenseMatrixView jacobianMatrix(_jacPdisc[type * _disc.nCol * _disc.nRad + pblk].data(), + _jacPdisc[type * _disc.nCol * _disc.nRad + pblk].pivot(), + probSize, probSize); // Get workspace memory LinearBufferAllocator tlmAlloc = threadLocalMem.get(); @@ -1335,27 +1482,35 @@ void GeneralRateModel2D::consistentInitialSensitivity(const SimulationTime& simT BufferedArray rhsUnmaskedBuffer = tlmAlloc.array(idxr.strideParBound(type)); double* const rhsUnmasked = static_cast(rhsUnmaskedBuffer); - double* const maskedMultiplier = _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); - double* const scaleFactors = _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); + double* const maskedMultiplier = + _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); + double* const scaleFactors = + _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); for (unsigned int shell = 0; shell < _disc.nParCell[type]; ++shell) { const int jacRowOffset = static_cast(shell) * idxr.strideParShell(type) + _disc.nComp; - const int localQOffset = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}) + static_cast(shell) * idxr.strideParShell(type) + idxr.strideParLiquid(); + const int localQOffset = + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}) + + static_cast(shell) * idxr.strideParShell(type) + idxr.strideParLiquid(); // Extract subproblem Jacobian from full Jacobian jacobianMatrix.setAll(0.0); - linalg::copyMatrixSubset(_jacP[type * _disc.nCol * _disc.nRad + pblk], mask, mask, jacRowOffset, 0, jacobianMatrix); + linalg::copyMatrixSubset(_jacP[type * _disc.nCol * _disc.nRad + pblk], mask, mask, jacRowOffset, 0, + jacobianMatrix); // Construct right hand side linalg::selectVectorSubset(sensYdot + localQOffset, mask, rhs); // Zero out masked elements - std::copy_n(sensY + localQOffset - idxr.strideParLiquid(), idxr.strideParShell(type), maskedMultiplier); + std::copy_n(sensY + localQOffset - idxr.strideParLiquid(), idxr.strideParShell(type), + maskedMultiplier); linalg::fillVectorSubset(maskedMultiplier + _disc.nComp, mask, 0.0); // Assemble right hand side - _jacP[type * _disc.nCol * _disc.nRad + pblk].submatrixMultiplyVector(maskedMultiplier, jacRowOffset, -static_cast(_disc.nComp), _disc.strideBound[type], idxr.strideParShell(type), rhsUnmasked); + _jacP[type * _disc.nCol * _disc.nRad + pblk].submatrixMultiplyVector( + maskedMultiplier, jacRowOffset, -static_cast(_disc.nComp), _disc.strideBound[type], + idxr.strideParShell(type), rhsUnmasked); linalg::vectorSubsetAdd(rhsUnmasked, mask, -1.0, 1.0, rhs); // Precondition @@ -1415,11 +1570,14 @@ void GeneralRateModel2D::consistentInitialSensitivity(const SimulationTime& simT if (_binding[type]->hasQuasiStationaryReactions()) { // Get iterators to beginning of solid phase - linalg::BandMatrix::RowIterator jacSolidOrig = _jacP[pblk].row(j * static_cast(idxr.strideParShell(type)) + static_cast(idxr.strideParLiquid())); + linalg::BandMatrix::RowIterator jacSolidOrig = + _jacP[pblk].row(j * static_cast(idxr.strideParShell(type)) + + static_cast(idxr.strideParLiquid())); linalg::FactorizableBandMatrix::RowIterator jacSolid = jac - idxr.strideParBound(type); int const* const mask = _binding[type]->reactionQuasiStationarity(); - double* const qShellDot = sensYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}) + static_cast(j) * idxr.strideParShell(type) + idxr.strideParLiquid(); + double* const qShellDot = sensYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}) + + static_cast(j) * idxr.strideParShell(type) + idxr.strideParLiquid(); // Copy row from original Jacobian and set right hand side for (int i = 0; i < idxr.strideParBound(type); ++i, ++jacSolid, ++jacSolidOrig) @@ -1450,7 +1608,8 @@ void GeneralRateModel2D::consistentInitialSensitivity(const SimulationTime& simT } // Solve - const bool result2 = fbm.solve(scaleFactors, sensYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par})); + const bool result2 = + fbm.solve(scaleFactors, sensYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par})); if (!result2) { LOG(Error) << "Solve() failed for par block " << pblk << " (type " << type << " col " << par << ")"; @@ -1474,20 +1633,20 @@ void GeneralRateModel2D::consistentInitialSensitivity(const SimulationTime& simT * @brief Computes approximately / partially consistent initial values and time derivatives of sensitivity subsystems * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, * the sensitivity system for a parameter @f$ p @f$ reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of this linear DAE, @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ - * have to be consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the time - * derivative \f$ \dot{s}_0 \f$ such that they are consistent. + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of this linear DAE, @f$ s_0 = + * \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ have to be + * consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the + * time derivative \f$ \dot{s}_0 \f$ such that they are consistent. * - * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized version of it. - * This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ are - * related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them - * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p}. @f$ - *
                - *
              1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations). - * Only solve for the fluxes @f$ j_{f,i} @f$ (only linear equations).
              2. - *
              3. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. - * However, because of the algebraic equations, we need additional conditions to fully determine + * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized + * version of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ + * \dot{s}_0 \f$ are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by + * differentiating them with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = + * \frac{\partial \dot{y}_0}{\partial p}. @f$
                1. Keep state and time derivative vectors as they are (i.e., do not + * solve algebraic equations). Only solve for the fluxes @f$ j_{f,i} @f$ (only linear equations).
                2. Compute the + * time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. However, because of + * the algebraic equations, we need additional conditions to fully determine * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting * equations are stated below: @@ -1501,8 +1660,9 @@ void GeneralRateModel2D::consistentInitialSensitivity(const SimulationTime& simT * where @f$ \dot{J}_0 @f$ denotes the bulk block Jacobian with respect to @f$ \dot{y}@f$. * * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. - * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] - * which is 0 for algebraic equations (@f$ -\frac{\partial^2 F}{\partial t \partial p}@f$, to be more precise). + * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - + * \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] which is 0 for algebraic equations (@f$ -\frac{\partial^2 + * F}{\partial t \partial p}@f$, to be more precise). * * The linear system is solved by backsubstitution. First, the bulk block is solved. * Then, the equations for the fluxes @f$ j_f @f$ are solved by substituting in the solution of the @@ -1516,11 +1676,15 @@ void GeneralRateModel2D::consistentInitialSensitivity(const SimulationTime& simT * @param [in] adRes Pointer to residual vector of AD datatypes with parameter sensitivities * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ -void GeneralRateModel2D::leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModel2D::leanConsistentInitialSensitivity(const SimulationTime& simTime, + const ConstSimulationState& simState, + std::vector& vecSensY, + std::vector& vecSensYdot, active const* const adRes, + util::ThreadLocalStorage& threadLocalMem) { if (isSectionDependent(_parDiffusionMode) || isSectionDependent(_parSurfDiffusionMode)) - LOG(Warning) << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; + LOG(Warning) + << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; BENCH_SCOPE(_timerConsistentInit); @@ -1587,10 +1751,11 @@ void GeneralRateModel2D::solveForFluxes(double* const vecState, const Indexer& i { linalg::DoubleSparseMatrix const* const jacFPtype = _jacFP + type * _disc.nCol * _disc.nRad; for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nRad; ++pblk) - jacFPtype[pblk].multiplySubtract(vecState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), jf); + jacFPtype[pblk].multiplySubtract(vecState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), + jf); } } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/GeneralRateModel2D-LinearSolver.cpp b/src/libcadet/model/GeneralRateModel2D-LinearSolver.cpp index 3b45d8827..9c26ee7cc 100644 --- a/src/libcadet/model/GeneralRateModel2D-LinearSolver.cpp +++ b/src/libcadet/model/GeneralRateModel2D-LinearSolver.cpp @@ -26,11 +26,11 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include - #include +#include +#include - typedef tbb::flow::continue_node< tbb::flow::continue_msg > node_t; - typedef const tbb::flow::continue_msg & msg_t; +typedef tbb::flow::continue_node node_t; +typedef const tbb::flow::continue_msg& msg_t; #endif namespace cadet @@ -41,12 +41,16 @@ namespace model /** * @brief Computes the solution of the linear system involving the system Jacobian - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + b \f] * has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the - * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) \f$, - * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p rhs. + * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) + \f$, + * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p + rhs. * - * The full Jacobian @f$ J = \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) @f$ is given by + * The full Jacobian @f$ J = \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} + \right) @f$ is given by * @f[ \begin{align} J = \left[\begin{array}{c|ccc|c} @@ -87,13 +91,16 @@ namespace model * Note that @f$ J_f = I @f$ is the identity matrix and that the off-diagonal blocks @f$ J_{i,f} @f$ * and @f$ J_{f,i} @f$ for @f$ i = 0, \dots, N_{z} @f$ are sparse. * - * Exploiting the decomposition, the solution procedure @f$ x = J^{-1}b = \left( LU \right)^{-1}b = U^{-1} L^{-1} b @f$ + * Exploiting the decomposition, the solution procedure @f$ x = J^{-1}b = \left( LU \right)^{-1}b = U^{-1} + L^{-1} b @f$ * works as follows: * -# Factorize the diagonal blocks @f$ J_0, \dots, J_{N_z} @f$ - * -# Solve @f$ y = L^{-1} b @f$ by forward substitution. This is accomplished by first solving the diagonal + * -# Solve @f$ y = L^{-1} b @f$ by forward substitution. This is accomplished by first solving the + diagonal * blocks independently, that is, * @f[ y_i = J_{i}^{-1} b_i. @f] - * Then, calculate the flux-part @f$ y_f @f$ by substituting in the already calculated solutions @f$ y_i @f$: + * Then, calculate the flux-part @f$ y_f @f$ by substituting in the already calculated solutions @f$ y_i + @f$: * @f[ y_f = b_f - \sum_{i=0}^{N_z} J_{f,i} y_i. @f] * -# Solve the Schur-complement @f$ S x_f = y_f @f$ using an iterative method that only requires * matrix-vector products. The already inverted diagonal blocks @f$ J_i^{-1} @f$ come in handy here. @@ -106,11 +113,12 @@ namespace model * @param [in] outerTol Error tolerance for the solution of the linear system from outer Newton iteration * @param [in,out] rhs On entry the right hand side of the linear equation system, on exit the solution * @param [in] weight Vector with error weights - * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is evaluated + * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is + evaluated * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ -int GeneralRateModel2D::linearSolve(double t, double alpha, double outerTol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) +int GeneralRateModel2D::linearSolve(double t, double alpha, double outerTol, double* const rhs, + double const* const weight, const ConstSimulationState& simState) { BENCH_SCOPE(_timerLinearSolve); @@ -131,15 +139,15 @@ int GeneralRateModel2D::linearSolve(double t, double alpha, double outerTol, dou node_t A(g, [&](msg_t) #endif { - // Assemble and factorize discretized bulk Jacobian - const bool result = _convDispOp.assembleAndFactorizeDiscretizedJacobian(alpha); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Factorize() failed for bulk block"; - } + // Assemble and factorize discretized bulk Jacobian + const bool result = _convDispOp.assembleAndFactorizeDiscretizedJacobian(alpha); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Factorize() failed for bulk block"; + } } CADET_PARNODE_END; - // Process the particle blocks + // Process the particle blocks #ifdef CADET_PARALLELIZE node_t B(g, [&](msg_t) #endif @@ -150,54 +158,54 @@ int GeneralRateModel2D::linearSolve(double t, double alpha, double outerTol, dou for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nRad * _disc.nParType; ++pblk) #endif { - const unsigned int type = pblk / (_disc.nCol * _disc.nRad); - const unsigned int par = pblk % (_disc.nCol * _disc.nRad); + const unsigned int type = pblk / (_disc.nCol * _disc.nRad); + const unsigned int par = pblk % (_disc.nCol * _disc.nRad); - // Assemble - assembleDiscretizedJacobianParticleBlock(type, par, alpha, idxr); + // Assemble + assembleDiscretizedJacobianParticleBlock(type, par, alpha, idxr); - // Factorize - const bool result = _jacPdisc[pblk].factorize(); - if (cadet_unlikely(!result)) + // Factorize + const bool result = _jacPdisc[pblk].factorize(); + if (cadet_unlikely(!result)) + { { - { - LOG(Error) << "Factorize() failed for par block " << pblk; - } + LOG(Error) << "Factorize() failed for par block " << pblk; } + } } CADET_PARFOR_END; } CADET_PARNODE_END; #ifndef CADET_PARALLELIZE // Do not factorize again at next call without changed Jacobians _factorizeJacobian = false; - } // if (_factorizeJacobian) +} // if (_factorizeJacobian) #endif - // ====== Step 1.5: Solve J c_uo = b_uo - A * c_in = b_uo - A*b_in +// ====== Step 1.5: Solve J c_uo = b_uo - A * c_in = b_uo - A*b_in - // rhs is passed twice but due to the values in jacA the writes happen to a different area of the rhs than the reads. +// rhs is passed twice but due to the values in jacA the writes happen to a different area of the rhs than the reads. #ifdef CADET_PARALLELIZE node_t C(g, [&](msg_t) #endif { - _jacInlet.multiplySubtract(rhs, rhs + idxr.offsetC()); + _jacInlet.multiplySubtract(rhs, rhs + idxr.offsetC()); } CADET_PARNODE_END; // ==== Step 2: Solve diagonal Jacobian blocks J_i to get y_i = J_i^{-1} b_i // The result is stored in rhs (in-place solution) - // Threads that are done with solving the bulk column blocks can proceed // to solving the particle blocks #ifdef CADET_PARALLELIZE node_t D(g, [&](msg_t) #endif { - const bool result = _convDispOp.solveDiscretizedJacobian(rhs + idxr.offsetC(), weight + idxr.offsetC(), nullptr, outerTol); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Solve() failed for bulk block"; - } + const bool result = + _convDispOp.solveDiscretizedJacobian(rhs + idxr.offsetC(), weight + idxr.offsetC(), nullptr, outerTol); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Solve() failed for bulk block"; + } } CADET_PARNODE_END; #ifdef CADET_PARALLELIZE @@ -207,16 +215,16 @@ int GeneralRateModel2D::linearSolve(double t, double alpha, double outerTol, dou #ifdef CADET_PARALLELIZE tbb::parallel_for(std::size_t(0), static_cast(_disc.nCol * _disc.nRad * _disc.nParType), [&](std::size_t pblk) #else - for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nRad * _disc.nParType; ++pblk) + for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nRad * _disc.nParType; ++pblk) #endif { - const unsigned int type = pblk / (_disc.nCol * _disc.nRad); - const unsigned int par = pblk % (_disc.nCol * _disc.nRad); - const bool result = _jacPdisc[pblk].solve(rhs + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par})); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Solve() failed for par block " << pblk; - } + const unsigned int type = pblk / (_disc.nCol * _disc.nRad); + const unsigned int par = pblk % (_disc.nCol * _disc.nRad); + const bool result = _jacPdisc[pblk].solve(rhs + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par})); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Solve() failed for par block " << pblk; + } } CADET_PARFOR_END; } CADET_PARNODE_END; @@ -228,46 +236,47 @@ int GeneralRateModel2D::linearSolve(double t, double alpha, double outerTol, dou node_t F(g, [&](msg_t) #endif { - _jacFC.multiplySubtract(rhs + idxr.offsetC(), rhs + idxr.offsetJf()); + _jacFC.multiplySubtract(rhs + idxr.offsetC(), rhs + idxr.offsetJf()); - for (unsigned int type = 0; type < _disc.nParType; ++type) + for (unsigned int type = 0; type < _disc.nParType; ++type) + { + for (unsigned int par = 0; par < _disc.nCol * _disc.nRad; ++par) { - for (unsigned int par = 0; par < _disc.nCol * _disc.nRad; ++par) - { - _jacFP[type * _disc.nCol * _disc.nRad + par].multiplySubtract(rhs + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}), rhs + idxr.offsetJf()); - } + _jacFP[type * _disc.nCol * _disc.nRad + par].multiplySubtract( + rhs + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}), rhs + idxr.offsetJf()); } + } - // Now, rhs contains the full intermediate solution y = L^{-1} b + // Now, rhs contains the full intermediate solution y = L^{-1} b - // Initialize temporary storage by copying over the fluxes - // Note that the rest of _tempState is zeroed out in schurComplementMatrixVector() - std::copy(rhs + idxr.offsetJf(), rhs + numDofs(), _tempState + idxr.offsetJf()); + // Initialize temporary storage by copying over the fluxes + // Note that the rest of _tempState is zeroed out in schurComplementMatrixVector() + std::copy(rhs + idxr.offsetJf(), rhs + numDofs(), _tempState + idxr.offsetJf()); - // ==== Step 3: Solve Schur-complement to get x_f = S^{-1} y_f - // Column and particle parts remain unchanged. - // The only thing to be done is the iterative (and approximate) - // solution of the Schur complement system: - // S * x_f = y_f + // ==== Step 3: Solve Schur-complement to get x_f = S^{-1} y_f + // Column and particle parts remain unchanged. + // The only thing to be done is the iterative (and approximate) + // solution of the Schur complement system: + // S * x_f = y_f - // Note that rhs is updated in-place with the solution of the Schur-complement - // The temporary storage is only needed to hold the right hand side of the Schur-complement - const double tolerance = std::sqrt(static_cast(_gmres.matrixSize())) * outerTol * _schurSafety; + // Note that rhs is updated in-place with the solution of the Schur-complement + // The temporary storage is only needed to hold the right hand side of the Schur-complement + const double tolerance = std::sqrt(static_cast(_gmres.matrixSize())) * outerTol * _schurSafety; - BENCH_START(_timerGmres); - _gmres.solve(tolerance, weight + idxr.offsetJf(), _tempState + idxr.offsetJf(), rhs + idxr.offsetJf()); - BENCH_STOP(_timerGmres); + BENCH_START(_timerGmres); + _gmres.solve(tolerance, weight + idxr.offsetJf(), _tempState + idxr.offsetJf(), rhs + idxr.offsetJf()); + BENCH_STOP(_timerGmres); - // Remove temporary results that are leftovers from schurComplementMatrixVector() - std::fill(_tempState + idxr.offsetC(), _tempState + idxr.offsetJf(), 0.0); + // Remove temporary results that are leftovers from schurComplementMatrixVector() + std::fill(_tempState + idxr.offsetC(), _tempState + idxr.offsetJf(), 0.0); - // At this point, rhs contains the intermediate solution [y_0, ..., y_{N_z}, x_f] + // At this point, rhs contains the intermediate solution [y_0, ..., y_{N_z}, x_f] - // ==== Step 4: Solve U * x = y by backward substitution - // The fluxes are already solved and remain unchanged + // ==== Step 4: Solve U * x = y by backward substitution + // The fluxes are already solved and remain unchanged - // Compute tempState_0 = J_{0,f} * y_f - _jacCF.multiplyAdd(rhs + idxr.offsetJf(), _tempState + idxr.offsetC()); + // Compute tempState_0 = J_{0,f} * y_f + _jacCF.multiplyAdd(rhs + idxr.offsetJf(), _tempState + idxr.offsetC()); } CADET_PARNODE_END; // Threads that are done with solving the bulk column blocks can proceed @@ -276,19 +285,19 @@ int GeneralRateModel2D::linearSolve(double t, double alpha, double outerTol, dou node_t G(g, [&](msg_t) #endif { - double* const localCol = _tempState + idxr.offsetC(); - double* const rhsCol = rhs + idxr.offsetC(); + double* const localCol = _tempState + idxr.offsetC(); + double* const rhsCol = rhs + idxr.offsetC(); - // Apply J_0^{-1} to tempState_0 - const bool result = _convDispOp.solveDiscretizedJacobian(localCol, weight + idxr.offsetC(), nullptr, outerTol); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Solve() failed for bulk block"; - } + // Apply J_0^{-1} to tempState_0 + const bool result = _convDispOp.solveDiscretizedJacobian(localCol, weight + idxr.offsetC(), nullptr, outerTol); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Solve() failed for bulk block"; + } - // Compute rhs_0 = y_0 - J_0^{-1} * J_{0,f} * y_f = y_0 - tempState_0 - for (unsigned int i = 0; i < _disc.nCol * _disc.nRad * _disc.nComp; ++i) - rhsCol[i] -= localCol[i]; + // Compute rhs_0 = y_0 - J_0^{-1} * J_{0,f} * y_f = y_0 - tempState_0 + for (unsigned int i = 0; i < _disc.nCol * _disc.nRad * _disc.nComp; ++i) + rhsCol[i] -= localCol[i]; } CADET_PARNODE_END; #ifdef CADET_PARALLELIZE @@ -298,27 +307,27 @@ int GeneralRateModel2D::linearSolve(double t, double alpha, double outerTol, dou #ifdef CADET_PARALLELIZE tbb::parallel_for(std::size_t(0), static_cast(_disc.nCol * _disc.nRad * _disc.nParType), [&](std::size_t pblk) #else - for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nRad * _disc.nParType; ++pblk) + for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nRad * _disc.nParType; ++pblk) #endif { - const unsigned int type = pblk / (_disc.nCol * _disc.nRad); - const unsigned int par = pblk % (_disc.nCol * _disc.nRad); + const unsigned int type = pblk / (_disc.nCol * _disc.nRad); + const unsigned int par = pblk % (_disc.nCol * _disc.nRad); - double* const localPar = _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); - double* const rhsPar = rhs + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); + double* const localPar = _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); + double* const rhsPar = rhs + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); - // Compute tempState_i = J_{i,f} * y_f - _jacPF[pblk].multiplyAdd(rhs + idxr.offsetJf(), localPar); - // Apply J_i^{-1} to tempState_i - const bool result = _jacPdisc[pblk].solve(localPar); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Solve() failed for par block " << pblk; - } + // Compute tempState_i = J_{i,f} * y_f + _jacPF[pblk].multiplyAdd(rhs + idxr.offsetJf(), localPar); + // Apply J_i^{-1} to tempState_i + const bool result = _jacPdisc[pblk].solve(localPar); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Solve() failed for par block " << pblk; + } - // Compute rhs_i = y_i - J_i^{-1} * J_{i,f} * y_f = y_i - tempState_i - for (int i = 0; i < idxr.strideParBlock(type); ++i) - rhsPar[i] -= localPar[i]; + // Compute rhs_i = y_i - J_i^{-1} * J_{i,f} * y_f = y_i - tempState_i + for (int i = 0; i < idxr.strideParBlock(type); ++i) + rhsPar[i] -= localPar[i]; } CADET_PARFOR_END; } CADET_PARNODE_END; @@ -326,8 +335,8 @@ int GeneralRateModel2D::linearSolve(double t, double alpha, double outerTol, dou // Create TBB dependency graph if (_factorizeJacobian) { - make_edge(A, C); - make_edge(B, C); + make_edge(A, C); + make_edge(B, C); } make_edge(C, D); @@ -340,11 +349,11 @@ int GeneralRateModel2D::linearSolve(double t, double alpha, double outerTol, dou // Start the graph running if (_factorizeJacobian) { - // Do not factorize again at next call without changed Jacobians - _factorizeJacobian = false; + // Do not factorize again at next call without changed Jacobians + _factorizeJacobian = false; - A.try_put(tbb::flow::continue_msg()); - B.try_put(tbb::flow::continue_msg()); + A.try_put(tbb::flow::continue_msg()); + B.try_put(tbb::flow::continue_msg()); } else C.try_put(tbb::flow::continue_msg()); @@ -355,44 +364,45 @@ int GeneralRateModel2D::linearSolve(double t, double alpha, double outerTol, dou // The full solution is now stored in rhs return 0; -} + } -/** - * @brief Performs the matrix-vector product @f$ z = Sx @f$ with the Schur-complement @f$ S @f$ from the Jacobian - * @details The Schur-complement @f$ S @f$ is given by - * @f[ \begin{align} - S &= J_f - J_{f,0} \, J_0^{-1} \, J_{0,f} - \sum_{p=1}^{N_z}{J_{f,p} \, J_p^{-1} \, J_{p,f}} \\ - &= I - \sum_{p=0}^{N_z}{J_{f,p} \, J_p^{-1} \, J_{p,f}}, - \end{align} @f] - * where @f$ J_f = I @f$ is the identity matrix and the off-diagonal blocks @f$ J_{i,f} @f$ - * and @f$ J_{f,i} @f$ for @f$ i = 0, \dots, N_{z} @f$ are sparse. - * - * The matrix-vector multiplication is executed in parallel as follows: - * -# Compute @f$ J_{f,i} \, J_i^{-1} \, J_{i,f} @f$ independently (in parallel with respect to index @f$ i @f$) - * -# Subtract the result from @f$ z @f$ - * - * @param [in] x Vector @f$ x @f$ the matrix @f$ S @f$ is multiplied with - * @param [out] z Result of the matrix-vector multiplication - * @return @c 0 if successful, any other value in case of failure - */ -int GeneralRateModel2D::schurComplementMatrixVector(double const* x, double* z) const -{ - BENCH_SCOPE(_timerMatVec); + /** + * @brief Performs the matrix-vector product @f$ z = Sx @f$ with the Schur-complement @f$ S @f$ from the Jacobian + * @details The Schur-complement @f$ S @f$ is given by + * @f[ \begin{align} + S &= J_f - J_{f,0} \, J_0^{-1} \, J_{0,f} - \sum_{p=1}^{N_z}{J_{f,p} \, J_p^{-1} \, J_{p,f}} \\ + &= I - \sum_{p=0}^{N_z}{J_{f,p} \, J_p^{-1} \, J_{p,f}}, + \end{align} @f] + * where @f$ J_f = I @f$ is the identity matrix and the off-diagonal blocks @f$ J_{i,f} @f$ + * and @f$ J_{f,i} @f$ for @f$ i = 0, \dots, N_{z} @f$ are sparse. + * + * The matrix-vector multiplication is executed in parallel as follows: + * -# Compute @f$ J_{f,i} \, J_i^{-1} \, J_{i,f} @f$ independently (in parallel with respect to index + @f$ i @f$) + * -# Subtract the result from @f$ z @f$ + * + * @param [in] x Vector @f$ x @f$ the matrix @f$ S @f$ is multiplied with + * @param [out] z Result of the matrix-vector multiplication + * @return @c 0 if successful, any other value in case of failure + */ + int GeneralRateModel2D::schurComplementMatrixVector(double const* x, double* z) const + { + BENCH_SCOPE(_timerMatVec); - // Copy x over to result z, which corresponds to the application of the identity matrix - std::copy(x, x + _disc.nCol * _disc.nComp * _disc.nRad * _disc.nParType, z); + // Copy x over to result z, which corresponds to the application of the identity matrix + std::copy(x, x + _disc.nCol * _disc.nComp * _disc.nRad * _disc.nParType, z); - Indexer idxr(_disc); - std::fill(_tempState + idxr.offsetC(), _tempState + idxr.offsetJf(), 0.0); + Indexer idxr(_disc); + std::fill(_tempState + idxr.offsetC(), _tempState + idxr.offsetJf(), 0.0); #ifdef CADET_PARALLELIZE - tbb::flow::graph g; + tbb::flow::graph g; #endif - // Solve bulk column block first + // Solve bulk column block first - // Apply J_{0,f} - _jacCF.multiplyAdd(x, _tempState + idxr.offsetC()); + // Apply J_{0,f} + _jacCF.multiplyAdd(x, _tempState + idxr.offsetC()); #ifdef CADET_PARALLELIZE node_t A(g, [&](msg_t) @@ -410,11 +420,11 @@ int GeneralRateModel2D::schurComplementMatrixVector(double const* x, double* z) node_t B(g, [&](msg_t) #endif { - // Handle particle blocks + // Handle particle blocks #ifdef CADET_PARALLELIZE tbb::parallel_for(std::size_t(0), static_cast(_disc.nCol * _disc.nRad * _disc.nParType), [&](std::size_t pblk) #else - for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nRad * _disc.nParType; ++pblk) + for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nRad * _disc.nParType; ++pblk) #endif { const unsigned int type = pblk / (_disc.nCol * _disc.nRad); @@ -446,7 +456,8 @@ int GeneralRateModel2D::schurComplementMatrixVector(double const* x, double* z) for (unsigned int par = 0; par < _disc.nCol * _disc.nRad; ++par) { // Apply J_{f,i} and subtract results from z - _jacFP[type * _disc.nCol * _disc.nRad + par].multiplySubtract(_tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}), z); + _jacFP[type * _disc.nCol * _disc.nRad + par].multiplySubtract( + _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}), z); } } } CADET_PARNODE_END; @@ -464,60 +475,61 @@ int GeneralRateModel2D::schurComplementMatrixVector(double const* x, double* z) #endif return 0; -} + } -/** - * @brief Assembles a particle Jacobian block @f$ J_i @f$ (@f$ i > 0 @f$) of the time-discretized equations - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The system Jacobian of the original equations, - * \f[ \frac{\partial F}{\partial y}, \f] - * is already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible - * for adding - * \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] - * to the system Jacobian, which yields the Jacobian of the time-discretized equations - * \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] - * when a BDF method is used. The time integrator needs to solve this equation for @f$ y_0 @f$, which requires - * the solution of the linear system mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). - * - * @param [in] parType Index of the particle type - * @param [in] pblk Index of the particle block within a type - * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) - * @param [in] idxr Indexer - */ -void GeneralRateModel2D::assembleDiscretizedJacobianParticleBlock(unsigned int parType, unsigned int pblk, double alpha, const Indexer& idxr) -{ - linalg::FactorizableBandMatrix& fbm = _jacPdisc[_disc.nCol * _disc.nRad * parType + pblk]; - const linalg::BandMatrix& bm = _jacP[_disc.nCol * _disc.nRad * parType + pblk]; + /** + * @brief Assembles a particle Jacobian block @f$ J_i @f$ (@f$ i > 0 @f$) of the time-discretized equations + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) + * x = b \f] has to be solved. The system Jacobian of the original equations, \f[ \frac{\partial F}{\partial y}, \f] + * is already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is + * responsible for adding \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] to the system Jacobian, which yields + * the Jacobian of the time-discretized equations \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] when + * a BDF method is used. The time integrator needs to solve this equation for @f$ y_0 @f$, which requires the + * solution of the linear system mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). + * + * @param [in] parType Index of the particle type + * @param [in] pblk Index of the particle block within a type + * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) + * @param [in] idxr Indexer + */ + void GeneralRateModel2D::assembleDiscretizedJacobianParticleBlock(unsigned int parType, unsigned int pblk, + double alpha, const Indexer& idxr) + { + linalg::FactorizableBandMatrix& fbm = _jacPdisc[_disc.nCol * _disc.nRad * parType + pblk]; + const linalg::BandMatrix& bm = _jacP[_disc.nCol * _disc.nRad * parType + pblk]; - // Copy normal matrix over to factorizable matrix - fbm.copyOver(bm); + // Copy normal matrix over to factorizable matrix + fbm.copyOver(bm); - // Add time derivatives to particle shells - linalg::FactorizableBandMatrix::RowIterator jac = fbm.row(0); - for (unsigned int j = 0; j < _disc.nParCell[parType]; ++j) - { - // Mobile and stationary phase (advances jac accordingly) - addTimeDerivativeToJacobianParticleShell(jac, idxr, alpha, parType); + // Add time derivatives to particle shells + linalg::FactorizableBandMatrix::RowIterator jac = fbm.row(0); + for (unsigned int j = 0; j < _disc.nParCell[parType]; ++j) + { + // Mobile and stationary phase (advances jac accordingly) + addTimeDerivativeToJacobianParticleShell(jac, idxr, alpha, parType); + } } -} - -/** - * @brief Adds Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ to bead rows of system Jacobian - * @details Actually adds @f$ \alpha \frac{\partial F}{\partial \dot{y}} @f$, which is useful - * for constructing the linear system in BDF time discretization. - * @param [in,out] jac On entry, RowIterator of the particle block pointing to the beginning of a bead shell; - * on exit, the iterator points to the end of the bead shell - * @param [in] idxr Indexer - * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) - * @param [in] parType Index of the particle type - */ -void GeneralRateModel2D::addTimeDerivativeToJacobianParticleShell(linalg::FactorizableBandMatrix::RowIterator& jac, const Indexer& idxr, double alpha, unsigned int parType) -{ - parts::cell::addTimeDerivativeToJacobianParticleShell(jac, alpha, static_cast(_parPorosity[parType]), _disc.nComp, _disc.nBound + _disc.nComp * parType, - _poreAccessFactor.data() + _disc.nComp * parType, _disc.strideBound[parType], _disc.boundOffset + _disc.nComp * parType, _binding[parType]->reactionQuasiStationarity()); -} + /** + * @brief Adds Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ to bead rows of system Jacobian + * @details Actually adds @f$ \alpha \frac{\partial F}{\partial \dot{y}} @f$, which is useful + * for constructing the linear system in BDF time discretization. + * @param [in,out] jac On entry, RowIterator of the particle block pointing to the beginning of a bead shell; + * on exit, the iterator points to the end of the bead shell + * @param [in] idxr Indexer + * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) + * @param [in] parType Index of the particle type + */ + void GeneralRateModel2D::addTimeDerivativeToJacobianParticleShell(linalg::FactorizableBandMatrix::RowIterator& jac, + const Indexer& idxr, double alpha, + unsigned int parType) + { + parts::cell::addTimeDerivativeToJacobianParticleShell( + jac, alpha, static_cast(_parPorosity[parType]), _disc.nComp, _disc.nBound + _disc.nComp * parType, + _poreAccessFactor.data() + _disc.nComp * parType, _disc.strideBound[parType], + _disc.boundOffset + _disc.nComp * parType, _binding[parType]->reactionQuasiStationarity()); + } -} // namespace model + } // namespace model -} // namespace cadet + } // namespace cadet diff --git a/src/libcadet/model/GeneralRateModel2D.cpp b/src/libcadet/model/GeneralRateModel2D.cpp index 9497b8479..02f840da0 100644 --- a/src/libcadet/model/GeneralRateModel2D.cpp +++ b/src/libcadet/model/GeneralRateModel2D.cpp @@ -39,13 +39,16 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include +#include #endif namespace { -cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvider& paramProvider, std::unordered_map& parameters, std::vector& values, const std::string& name, unsigned int nAxial, unsigned int nRad, unsigned int nParType, cadet::UnitOpIdx uoi) +cadet::model::MultiplexMode readAndRegisterMultiplexParam( + cadet::IParameterProvider& paramProvider, std::unordered_map& parameters, + std::vector& values, const std::string& name, unsigned int nAxial, unsigned int nRad, + unsigned int nParType, cadet::UnitOpIdx uoi) { cadet::model::MultiplexMode mode = cadet::model::MultiplexMode::Independent; readScalarParameterOrArray(values, paramProvider, name, 1); @@ -56,25 +59,33 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi { mode = cadet::model::MultiplexMode::Independent; if (values.size() != nParType) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nParType) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be " + std::to_string(nParType) + + ")"); } else if (modeConfig == 1) { mode = cadet::model::MultiplexMode::Radial; if (values.size() != nRad * nParType) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nRad * nParType) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be " + + std::to_string(nRad * nParType) + ")"); } else if (modeConfig == 2) { mode = cadet::model::MultiplexMode::Axial; if (values.size() != nAxial * nParType) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nAxial * nParType) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be " + + std::to_string(nAxial * nParType) + ")"); } else if (modeConfig == 3) { mode = cadet::model::MultiplexMode::AxialRadial; if (values.size() != nAxial * nRad * nParType) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nAxial * nRad * nParType) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be " + + std::to_string(nAxial * nRad * nParType) + ")"); } } else @@ -88,230 +99,244 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi else if (values.size() == nRad * nAxial * nParType) mode = cadet::model::MultiplexMode::AxialRadial; else - throw cadet::InvalidParameterException("Could not infer multiplex mode of field " + name + ", set " + name + "_MULTIPLEX or change number of elements"); + throw cadet::InvalidParameterException("Could not infer multiplex mode of field " + name + ", set " + name + + "_MULTIPLEX or change number of elements"); } const cadet::StringHash nameHash = cadet::hashStringRuntime(name); switch (mode) { - case cadet::model::MultiplexMode::Independent: - { - std::vector p(nAxial * nRad * nParType); - for (unsigned int s = 0; s < nAxial * nRad; ++s) - std::copy(values.begin(), values.end(), p.begin() + s * nParType); + case cadet::model::MultiplexMode::Independent: { + std::vector p(nAxial * nRad * nParType); + for (unsigned int s = 0; s < nAxial * nRad; ++s) + std::copy(values.begin(), values.end(), p.begin() + s * nParType); - values = std::move(p); + values = std::move(p); - for (unsigned int s = 0; s < nParType; ++s) - parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, s, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep)] = &values[s]; - } - break; - case cadet::model::MultiplexMode::Radial: - { - std::vector p(nAxial * nRad * nParType); - for (unsigned int s = 0; s < nAxial; ++s) - std::copy(values.begin(), values.end(), p.begin() + s * nParType * nRad); + for (unsigned int s = 0; s < nParType; ++s) + parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, s, cadet::BoundStateIndep, + cadet::ReactionIndep, cadet::SectionIndep)] = &values[s]; + } + break; + case cadet::model::MultiplexMode::Radial: { + std::vector p(nAxial * nRad * nParType); + for (unsigned int s = 0; s < nAxial; ++s) + std::copy(values.begin(), values.end(), p.begin() + s * nParType * nRad); - values = std::move(p); + values = std::move(p); - for (unsigned int s = 0; s < nRad; ++s) - { - for (unsigned int i = 0; i < nParType; ++i) - parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, i, cadet::BoundStateIndep, s, cadet::SectionIndep)] = &values[s * nParType + i]; - } - } - break; - case cadet::model::MultiplexMode::Axial: - { - std::vector p(nAxial * nRad * nParType); - for (unsigned int i = 0; i < nAxial; ++i) - { - for (unsigned int j = 0; j < nRad; ++j) - std::copy(values.begin() + i * nParType, values.begin() + (i+1) * nParType, p.begin() + i * nRad * nParType + j * nParType); - } + for (unsigned int s = 0; s < nRad; ++s) + { + for (unsigned int i = 0; i < nParType; ++i) + parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, i, cadet::BoundStateIndep, s, + cadet::SectionIndep)] = &values[s * nParType + i]; + } + } + break; + case cadet::model::MultiplexMode::Axial: { + std::vector p(nAxial * nRad * nParType); + for (unsigned int i = 0; i < nAxial; ++i) + { + for (unsigned int j = 0; j < nRad; ++j) + std::copy(values.begin() + i * nParType, values.begin() + (i + 1) * nParType, + p.begin() + i * nRad * nParType + j * nParType); + } - values = std::move(p); + values = std::move(p); - for (unsigned int s = 0; s < nAxial; ++s) - { - for (unsigned int i = 0; i < nParType; ++i) - parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, i, cadet::BoundStateIndep, cadet::ReactionIndep, s)] = &values[s * nParType * nRad + i]; - } - } - break; - case cadet::model::MultiplexMode::AxialRadial: - cadet::registerParam3DArray(parameters, values, [=](bool multi, unsigned int ax, unsigned int rad, unsigned int pt) { return cadet::makeParamId(nameHash, uoi, cadet::CompIndep, pt, cadet::BoundStateIndep, rad, ax); }, nParType, nRad); - break; - case cadet::model::MultiplexMode::RadialSection: - case cadet::model::MultiplexMode::Component: - case cadet::model::MultiplexMode::ComponentRadial: - case cadet::model::MultiplexMode::ComponentRadialSection: - case cadet::model::MultiplexMode::ComponentSection: - case cadet::model::MultiplexMode::Section: - case cadet::model::MultiplexMode::Type: - case cadet::model::MultiplexMode::ComponentType: - case cadet::model::MultiplexMode::ComponentSectionType: - cadet_assert(false); - break; + for (unsigned int s = 0; s < nAxial; ++s) + { + for (unsigned int i = 0; i < nParType; ++i) + parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, i, cadet::BoundStateIndep, + cadet::ReactionIndep, s)] = &values[s * nParType * nRad + i]; + } + } + break; + case cadet::model::MultiplexMode::AxialRadial: + cadet::registerParam3DArray( + parameters, values, + [=](bool multi, unsigned int ax, unsigned int rad, unsigned int pt) { + return cadet::makeParamId(nameHash, uoi, cadet::CompIndep, pt, cadet::BoundStateIndep, rad, ax); + }, + nParType, nRad); + break; + case cadet::model::MultiplexMode::RadialSection: + case cadet::model::MultiplexMode::Component: + case cadet::model::MultiplexMode::ComponentRadial: + case cadet::model::MultiplexMode::ComponentRadialSection: + case cadet::model::MultiplexMode::ComponentSection: + case cadet::model::MultiplexMode::Section: + case cadet::model::MultiplexMode::Type: + case cadet::model::MultiplexMode::ComponentType: + case cadet::model::MultiplexMode::ComponentSectionType: + cadet_assert(false); + break; } return mode; } -bool multiplexParameterValue(const cadet::ParameterId& pId, cadet::StringHash nameHash, cadet::model::MultiplexMode mode, std::vector& data, unsigned int nAxial, unsigned int nRad, unsigned int nParType, double value, std::unordered_set const* sensParams) +bool multiplexParameterValue(const cadet::ParameterId& pId, cadet::StringHash nameHash, + cadet::model::MultiplexMode mode, std::vector& data, unsigned int nAxial, + unsigned int nRad, unsigned int nParType, double value, + std::unordered_set const* sensParams) { if (pId.name != nameHash) return false; switch (mode) { - case cadet::model::MultiplexMode::Independent: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + case cadet::model::MultiplexMode::Independent: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.particleType])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.particleType])) + return false; - for (unsigned int i = 0; i < nAxial * nRad; ++i) - data[i * nParType + pId.particleType].setValue(value); + for (unsigned int i = 0; i < nAxial * nRad; ++i) + data[i * nParType + pId.particleType].setValue(value); - return true; - } - case cadet::model::MultiplexMode::Radial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction == cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Radial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction == cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.reaction * nParType + pId.particleType])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.reaction * nParType + pId.particleType])) + return false; - for (unsigned int i = 0; i < nAxial; ++i) - data[i * nRad * nParType + pId.reaction * nParType + pId.particleType].setValue(value); + for (unsigned int i = 0; i < nAxial; ++i) + data[i * nRad * nParType + pId.reaction * nParType + pId.particleType].setValue(value); - return true; - } - case cadet::model::MultiplexMode::Axial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Axial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.section * nParType * nRad + pId.particleType])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.section * nParType * nRad + pId.particleType])) + return false; - for (unsigned int i = 0; i < nRad; ++i) - data[pId.section * nParType * nRad + i * nParType + pId.particleType].setValue(value); + for (unsigned int i = 0; i < nRad; ++i) + data[pId.section * nParType * nRad + i * nParType + pId.particleType].setValue(value); - return true; - } - case cadet::model::MultiplexMode::AxialRadial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction == cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::AxialRadial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction == cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.section * nParType * nRad + pId.reaction * nParType + pId.particleType])) - return false; + if (sensParams && + !cadet::contains(*sensParams, + &data[pId.section * nParType * nRad + pId.reaction * nParType + pId.particleType])) + return false; - data[pId.section * nParType * nRad + pId.reaction * nParType + pId.particleType].setValue(value); + data[pId.section * nParType * nRad + pId.reaction * nParType + pId.particleType].setValue(value); - return true; - } - case cadet::model::MultiplexMode::RadialSection: - case cadet::model::MultiplexMode::Component: - case cadet::model::MultiplexMode::ComponentRadial: - case cadet::model::MultiplexMode::ComponentRadialSection: - case cadet::model::MultiplexMode::ComponentSection: - case cadet::model::MultiplexMode::Section: - case cadet::model::MultiplexMode::Type: - case cadet::model::MultiplexMode::ComponentType: - case cadet::model::MultiplexMode::ComponentSectionType: - cadet_assert(false); - break; + return true; + } + case cadet::model::MultiplexMode::RadialSection: + case cadet::model::MultiplexMode::Component: + case cadet::model::MultiplexMode::ComponentRadial: + case cadet::model::MultiplexMode::ComponentRadialSection: + case cadet::model::MultiplexMode::ComponentSection: + case cadet::model::MultiplexMode::Section: + case cadet::model::MultiplexMode::Type: + case cadet::model::MultiplexMode::ComponentType: + case cadet::model::MultiplexMode::ComponentSectionType: + cadet_assert(false); + break; } return false; } -bool multiplexParameterAD(const cadet::ParameterId& pId, cadet::StringHash nameHash, cadet::model::MultiplexMode mode, std::vector& data, unsigned int nAxial, unsigned int nRad, unsigned int nParType, unsigned int adDirection, double adValue, std::unordered_set& sensParams) +bool multiplexParameterAD(const cadet::ParameterId& pId, cadet::StringHash nameHash, cadet::model::MultiplexMode mode, + std::vector& data, unsigned int nAxial, unsigned int nRad, + unsigned int nParType, unsigned int adDirection, double adValue, + std::unordered_set& sensParams) { if (pId.name != nameHash) return false; switch (mode) { - case cadet::model::MultiplexMode::Independent: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + case cadet::model::MultiplexMode::Independent: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.particleType]); + sensParams.insert(&data[pId.particleType]); - for (unsigned int i = 0; i < nAxial * nRad; ++i) - data[i * nParType + pId.particleType].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nAxial * nRad; ++i) + data[i * nParType + pId.particleType].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::Radial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction == cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Radial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction == cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.reaction * nParType + pId.particleType]); + sensParams.insert(&data[pId.reaction * nParType + pId.particleType]); - for (unsigned int i = 0; i < nAxial; ++i) - data[i * nRad * nParType + pId.reaction * nParType + pId.particleType].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nAxial; ++i) + data[i * nRad * nParType + pId.reaction * nParType + pId.particleType].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::Axial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Axial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.section * nParType * nRad + pId.particleType]); + sensParams.insert(&data[pId.section * nParType * nRad + pId.particleType]); - for (unsigned int i = 0; i < nRad; ++i) - data[pId.section * nParType * nRad + i * nParType + pId.particleType].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nRad; ++i) + data[pId.section * nParType * nRad + i * nParType + pId.particleType].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::AxialRadial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction == cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::AxialRadial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction == cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.section * nParType * nRad + pId.reaction * nParType + pId.particleType]); - data[pId.section * nParType * nRad + pId.reaction * nParType + pId.particleType].setADValue(adDirection, adValue); + sensParams.insert(&data[pId.section * nParType * nRad + pId.reaction * nParType + pId.particleType]); + data[pId.section * nParType * nRad + pId.reaction * nParType + pId.particleType].setADValue(adDirection, + adValue); - return true; - } - case cadet::model::MultiplexMode::RadialSection: - case cadet::model::MultiplexMode::Component: - case cadet::model::MultiplexMode::ComponentRadial: - case cadet::model::MultiplexMode::ComponentRadialSection: - case cadet::model::MultiplexMode::ComponentSection: - case cadet::model::MultiplexMode::Section: - case cadet::model::MultiplexMode::Type: - case cadet::model::MultiplexMode::ComponentType: - case cadet::model::MultiplexMode::ComponentSectionType: - cadet_assert(false); - break; + return true; + } + case cadet::model::MultiplexMode::RadialSection: + case cadet::model::MultiplexMode::Component: + case cadet::model::MultiplexMode::ComponentRadial: + case cadet::model::MultiplexMode::ComponentRadialSection: + case cadet::model::MultiplexMode::ComponentSection: + case cadet::model::MultiplexMode::Section: + case cadet::model::MultiplexMode::Type: + case cadet::model::MultiplexMode::ComponentType: + case cadet::model::MultiplexMode::ComponentSectionType: + cadet_assert(false); + break; } return false; } - -} // namespace - +} // namespace namespace cadet { @@ -329,11 +354,11 @@ int schurComplementMultiplierGRM2D(void* userData, double const* x, double* z) return grm->schurComplementMatrixVector(x, z); } - -GeneralRateModel2D::GeneralRateModel2D(UnitOpIdx unitOpIdx) : UnitOperationBase(unitOpIdx), - _dynReactionBulk(nullptr), _jacP(nullptr), _jacPdisc(nullptr), _jacPF(nullptr), _jacFP(nullptr), _jacInlet(), - _analyticJac(true), _jacobianAdDirs(0), _factorizeJacobian(false), _tempState(nullptr), - _initC(0), _singleRadiusInitC(true), _initCp(0), _singleRadiusInitCp(true), _initQ(0), _singleRadiusInitQ(true), _initState(0), _initStateDot(0) +GeneralRateModel2D::GeneralRateModel2D(UnitOpIdx unitOpIdx) + : UnitOperationBase(unitOpIdx), _dynReactionBulk(nullptr), _jacP(nullptr), _jacPdisc(nullptr), _jacPF(nullptr), + _jacFP(nullptr), _jacInlet(), _analyticJac(true), _jacobianAdDirs(0), _factorizeJacobian(false), + _tempState(nullptr), _initC(0), _singleRadiusInitC(true), _initCp(0), _singleRadiusInitCp(true), _initQ(0), + _singleRadiusInitQ(true), _initState(0), _initStateDot(0) { } @@ -361,23 +386,25 @@ GeneralRateModel2D::~GeneralRateModel2D() CADET_NOEXCEPT unsigned int GeneralRateModel2D::numDofs() const CADET_NOEXCEPT { // Column bulk DOFs: nCol * nComp * nRad - // Particle DOFs: nCol * nRad * nParType particles each having nComp (liquid phase) + sum boundStates (solid phase) DOFs + // Particle DOFs: nCol * nRad * nParType particles each having nComp (liquid phase) + sum boundStates (solid phase) + // DOFs // in each shell; there are nParCell shells for each particle type // Flux DOFs: nCol * nComp * nRad * nParType (as many as column bulk DOFs) // Inlet DOFs: nComp * nRad - return _disc.nCol * _disc.nRad * (_disc.nComp * (1 + _disc.nParType)) + _disc.parTypeOffset[_disc.nParType] + _disc.nComp * _disc.nRad; + return _disc.nCol * _disc.nRad * (_disc.nComp * (1 + _disc.nParType)) + _disc.parTypeOffset[_disc.nParType] + + _disc.nComp * _disc.nRad; } unsigned int GeneralRateModel2D::numPureDofs() const CADET_NOEXCEPT { // Column bulk DOFs: nCol * nComp * nRad - // Particle DOFs: nCol * nRad * nParType particles each having nComp (liquid phase) + sum boundStates (solid phase) DOFs + // Particle DOFs: nCol * nRad * nParType particles each having nComp (liquid phase) + sum boundStates (solid phase) + // DOFs // in each shell; there are nParCell shells for each particle type // Flux DOFs: nCol * nComp * nRad * nParType (as many as column bulk DOFs) return _disc.nCol * _disc.nRad * (_disc.nComp * (1 + _disc.nParType)) + _disc.parTypeOffset[_disc.nParType]; } - bool GeneralRateModel2D::usesAD() const CADET_NOEXCEPT { #ifdef CADET_CHECK_ANALYTIC_JACOBIAN @@ -400,7 +427,8 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP paramProvider.pushScope("discretization"); - if (!newNBoundInterface && paramProvider.exists("NBOUND")) // done here and in this order for backwards compatibility + if (!newNBoundInterface && + paramProvider.exists("NBOUND")) // done here and in this order for backwards compatibility nBound = paramProvider.getIntArray("NBOUND"); else { @@ -409,15 +437,16 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP paramProvider.pushScope("discretization"); } if (nBound.size() < _disc.nComp) - throw InvalidParameterException("Field NBOUND contains too few elements (NCOMP = " + std::to_string(_disc.nComp) + " required)"); + throw InvalidParameterException( + "Field NBOUND contains too few elements (NCOMP = " + std::to_string(_disc.nComp) + " required)"); _disc.nCol = paramProvider.getInt("NCOL"); _disc.nRad = paramProvider.getInt("NRAD"); const std::vector nParCell = paramProvider.getIntArray("NPAR"); - - if (!newNPartypeInterface && paramProvider.exists("NPARTYPE")) // done here and in this order for backwards compatibility + if (!newNPartypeInterface && + paramProvider.exists("NPARTYPE")) // done here and in this order for backwards compatibility _disc.nParType = paramProvider.getInt("NPARTYPE"); else if (newNPartypeInterface) { @@ -432,7 +461,8 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP } if ((nParCell.size() > 1) && (nParCell.size() < _disc.nParType)) - throw InvalidParameterException("Field NPAR must have 1 or NPARTYPE (" + std::to_string(_disc.nParType) + ") entries"); + throw InvalidParameterException("Field NPAR must have 1 or NPARTYPE (" + std::to_string(_disc.nParType) + + ") entries"); _disc.nParCell = new unsigned int[_disc.nParType]; if (nParCell.size() < _disc.nParType) @@ -445,7 +475,9 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP std::copy_n(nParCell.begin(), _disc.nParType, _disc.nParCell); if ((nBound.size() > _disc.nComp) && (nBound.size() < _disc.nComp * _disc.nParType)) - throw InvalidParameterException("Field NBOUND must have NCOMP (" + std::to_string(_disc.nComp) + ") or NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + ") entries"); + throw InvalidParameterException("Field NBOUND must have NCOMP (" + std::to_string(_disc.nComp) + + ") or NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + + ") entries"); _disc.nBound = new unsigned int[_disc.nComp * _disc.nParType]; if (nBound.size() < _disc.nComp * _disc.nParType) @@ -489,9 +521,10 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP unsigned int nTotalParCells = 0; for (unsigned int j = 1; j < _disc.nParType + 1; ++j) { - _disc.parTypeOffset[j] = _disc.parTypeOffset[j-1] + (_disc.nComp + _disc.strideBound[j-1]) * _disc.nParCell[j-1] * _disc.nCol * _disc.nRad; - _disc.nParCellsBeforeType[j] = _disc.nParCellsBeforeType[j-1] + _disc.nParCell[j-1]; - nTotalParCells += _disc.nParCell[j-1]; + _disc.parTypeOffset[j] = _disc.parTypeOffset[j - 1] + (_disc.nComp + _disc.strideBound[j - 1]) * + _disc.nParCell[j - 1] * _disc.nCol * _disc.nRad; + _disc.nParCellsBeforeType[j] = _disc.nParCellsBeforeType[j - 1] + _disc.nParCell[j - 1]; + nTotalParCells += _disc.nParCell[j - 1]; } _disc.nParCellsBeforeType[_disc.nParType] = nTotalParCells; @@ -510,7 +543,8 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP pdt.resize(_disc.nParType, pdt[0]); } else if (pdt.size() < _disc.nParType) - throw InvalidParameterException("Field PAR_DISC_TYPE contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field PAR_DISC_TYPE contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); for (unsigned int i = 0; i < _disc.nParType; ++i) { @@ -532,7 +566,8 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP pg.resize(_disc.nParType, pg[0]); } else if (pg.size() < _disc.nParType) - throw InvalidParameterException("Field PAR_GEOM contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field PAR_GEOM contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); for (unsigned int i = 0; i < _disc.nParType; ++i) { @@ -543,7 +578,8 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP else if (pg[i] == "SLAB") _parGeomSurfToVol[i] = SurfVolRatioSlab; else - throw InvalidParameterException("Unknown particle geometry type \"" + pg[i] + "\" at index " + std::to_string(i) + " of field PAR_GEOM"); + throw InvalidParameterException("Unknown particle geometry type \"" + pg[i] + "\" at index " + + std::to_string(i) + " of field PAR_GEOM"); } } paramProvider.pushScope("discretization"); @@ -552,7 +588,8 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP { _parDiscVector = paramProvider.getDoubleArray("PAR_DISC_VECTOR"); if (_parDiscVector.size() < nTotalParCells + _disc.nParType) - throw InvalidParameterException("Field PAR_DISC_VECTOR contains too few elements (Sum [NPAR + 1] = " + std::to_string(nTotalParCells + _disc.nParType) + " required)"); + throw InvalidParameterException("Field PAR_DISC_VECTOR contains too few elements (Sum [NPAR + 1] = " + + std::to_string(nTotalParCells + _disc.nParType) + " required)"); } // Determine whether analytic Jacobian should be used but don't set it right now. @@ -574,7 +611,9 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP } // Initialize and configure GMRES for solving the Schur-complement - _gmres.initialize(_disc.nCol * _disc.nRad * _disc.nComp * _disc.nParType, paramProvider.getInt("MAX_KRYLOV"), linalg::toOrthogonalization(paramProvider.getInt("GS_TYPE")), paramProvider.getInt("MAX_RESTARTS")); + _gmres.initialize(_disc.nCol * _disc.nRad * _disc.nComp * _disc.nParType, paramProvider.getInt("MAX_KRYLOV"), + linalg::toOrthogonalization(paramProvider.getInt("GS_TYPE")), + paramProvider.getInt("MAX_RESTARTS")); _gmres.matrixVectorMultiplier(&schurComplementMultiplierGRM2D, this); _schurSafety = paramProvider.getDouble("SCHUR_SAFETY"); @@ -601,8 +640,10 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP linalg::FactorizableBandMatrix* const ptrJacDisc = _jacPdisc + _disc.nCol * _disc.nRad * j; for (unsigned int i = 0; i < _disc.nCol * _disc.nRad; ++i) { - ptrJacDisc[i].resize(_disc.nParCell[j] * (_disc.nComp + _disc.strideBound[j]), _disc.nComp + _disc.strideBound[j], _disc.nComp + 2 * _disc.strideBound[j]); - ptrJac[i].resize(_disc.nParCell[j] * (_disc.nComp + _disc.strideBound[j]), _disc.nComp + _disc.strideBound[j], _disc.nComp + 2 * _disc.strideBound[j]); + ptrJacDisc[i].resize(_disc.nParCell[j] * (_disc.nComp + _disc.strideBound[j]), + _disc.nComp + _disc.strideBound[j], _disc.nComp + 2 * _disc.strideBound[j]); + ptrJac[i].resize(_disc.nParCell[j] * (_disc.nComp + _disc.strideBound[j]), + _disc.nComp + _disc.strideBound[j], _disc.nComp + 2 * _disc.strideBound[j]); } } @@ -627,7 +668,7 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP clearBindingModels(); _binding = std::vector(_disc.nParType, nullptr); - std::vector bindModelNames = { "NONE" }; + std::vector bindModelNames = {"NONE"}; if (paramProvider.exists("ADSORPTION_MODEL")) bindModelNames = paramProvider.getStringArray("ADSORPTION_MODEL"); @@ -640,7 +681,8 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP } if (!_singleBinding && (bindModelNames.size() < _disc.nParType)) - throw InvalidParameterException("Field ADSORPTION_MODEL contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field ADSORPTION_MODEL contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); else if (_singleBinding && (bindModelNames.size() != 1)) throw InvalidParameterException("Field ADSORPTION_MODEL requires (only) 1 element"); @@ -658,8 +700,12 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP if (!_binding[i]) throw InvalidParameterException("Unknown binding model " + bindModelNames[i]); - MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", _singleBinding, i, _disc.nParType == 1, _binding[i]->usesParamProviderInDiscretizationConfig()); - bindingConfSuccess = _binding[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, _disc.boundOffset + i * _disc.nComp) && bindingConfSuccess; + MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", _singleBinding, i, _disc.nParType == 1, + _binding[i]->usesParamProviderInDiscretizationConfig()); + bindingConfSuccess = + _binding[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, + _disc.boundOffset + i * _disc.nComp) && + bindingConfSuccess; } } @@ -677,7 +723,8 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP if (_dynReactionBulk->usesParamProviderInDiscretizationConfig()) paramProvider.pushScope("reaction_bulk"); - reactionConfSuccess = _dynReactionBulk->configureModelDiscretization(paramProvider, _disc.nComp, nullptr, nullptr); + reactionConfSuccess = + _dynReactionBulk->configureModelDiscretization(paramProvider, _disc.nComp, nullptr, nullptr); if (_dynReactionBulk->usesParamProviderInDiscretizationConfig()) paramProvider.popScope(); @@ -699,7 +746,8 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP } if (!_singleDynReaction && (dynReactModelNames.size() < _disc.nParType)) - throw InvalidParameterException("Field REACTION_MODEL_PARTICLES contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field REACTION_MODEL_PARTICLES contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); else if (_singleDynReaction && (dynReactModelNames.size() != 1)) throw InvalidParameterException("Field REACTION_MODEL_PARTICLES requires (only) 1 element"); @@ -716,13 +764,19 @@ bool GeneralRateModel2D::configureModelDiscretization(IParameterProvider& paramP if (!_dynReaction[i]) throw InvalidParameterException("Unknown dynamic reaction model " + dynReactModelNames[i]); - MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", _singleDynReaction, i, _disc.nParType == 1, _dynReaction[i]->usesParamProviderInDiscretizationConfig()); - reactionConfSuccess = _dynReaction[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, _disc.boundOffset + i * _disc.nComp) && reactionConfSuccess; + MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", _singleDynReaction, i, + _disc.nParType == 1, + _dynReaction[i]->usesParamProviderInDiscretizationConfig()); + reactionConfSuccess = _dynReaction[i]->configureModelDiscretization( + paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, + _disc.boundOffset + i * _disc.nComp) && + reactionConfSuccess; } } } - const bool transportSuccess = _convDispOp.configureModelDiscretization(paramProvider, helper, _disc.nComp, _disc.nCol, _disc.nRad, _dynReactionBulk); + const bool transportSuccess = _convDispOp.configureModelDiscretization(paramProvider, helper, _disc.nComp, + _disc.nCol, _disc.nRad, _dynReactionBulk); // Setup the memory for tempState based on state vector _tempState = new double[numDofs()]; @@ -737,12 +791,15 @@ bool GeneralRateModel2D::configure(IParameterProvider& paramProvider) const bool transportSuccess = _convDispOp.configure(_unitOpIdx, paramProvider, _parameters); // Read geometry parameters - _singleParRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parRadius, "PAR_RADIUS", _disc.nParType, _unitOpIdx); - _singleParPorosity = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parPorosity, "PAR_POROSITY", _disc.nParType, _unitOpIdx); + _singleParRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parRadius, "PAR_RADIUS", + _disc.nParType, _unitOpIdx); + _singleParPorosity = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parPorosity, "PAR_POROSITY", + _disc.nParType, _unitOpIdx); // Let PAR_CORERADIUS default to 0.0 for backwards compatibility if (paramProvider.exists("PAR_CORERADIUS")) - _singleParCoreRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parCoreRadius, "PAR_CORERADIUS", _disc.nParType, _unitOpIdx); + _singleParCoreRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parCoreRadius, + "PAR_CORERADIUS", _disc.nParType, _unitOpIdx); else { _singleParCoreRadius = true; @@ -755,7 +812,9 @@ bool GeneralRateModel2D::configure(IParameterProvider& paramProvider) // Let PAR_TYPE_VOLFRAC default to 1.0 for backwards compatibility if (paramProvider.exists("PAR_TYPE_VOLFRAC")) - _parTypeVolFracMode = readAndRegisterMultiplexParam(paramProvider, _parameters, _parTypeVolFrac, "PAR_TYPE_VOLFRAC", _disc.nCol, _disc.nRad, _disc.nParType, _unitOpIdx); + _parTypeVolFracMode = + readAndRegisterMultiplexParam(paramProvider, _parameters, _parTypeVolFrac, "PAR_TYPE_VOLFRAC", _disc.nCol, + _disc.nRad, _disc.nParType, _unitOpIdx); else { // Only one particle type present @@ -765,44 +824,67 @@ bool GeneralRateModel2D::configure(IParameterProvider& paramProvider) // Check whether all sizes are matched if (_disc.nParType != _parRadius.size()) - throw InvalidParameterException("Number of elements in field PAR_RADIUS does not match number of particle types"); + throw InvalidParameterException( + "Number of elements in field PAR_RADIUS does not match number of particle types"); if (_disc.nParType * _disc.nCol * _disc.nRad != _parTypeVolFrac.size()) - throw InvalidParameterException("Number of elements in field PAR_TYPE_VOLFRAC does not match number of particle types times number of bulk cells"); + throw InvalidParameterException("Number of elements in field PAR_TYPE_VOLFRAC does not match number of " + "particle types times number of bulk cells"); if (_disc.nParType != _parPorosity.size()) - throw InvalidParameterException("Number of elements in field PAR_POROSITY does not match number of particle types"); + throw InvalidParameterException( + "Number of elements in field PAR_POROSITY does not match number of particle types"); if (_disc.nParType != _parCoreRadius.size()) - throw InvalidParameterException("Number of elements in field PAR_CORERADIUS does not match number of particle types"); + throw InvalidParameterException( + "Number of elements in field PAR_CORERADIUS does not match number of particle types"); // Check that particle volume fractions sum to 1.0 for (unsigned int i = 0; i < _disc.nCol * _disc.nRad; ++i) { - const double volFracSum = std::accumulate(_parTypeVolFrac.begin() + i * _disc.nParType, _parTypeVolFrac.begin() + (i+1) * _disc.nParType, 0.0, + const double volFracSum = std::accumulate( + _parTypeVolFrac.begin() + i * _disc.nParType, _parTypeVolFrac.begin() + (i + 1) * _disc.nParType, 0.0, [](double a, const active& b) -> double { return a + static_cast(b); }); if (std::abs(1.0 - volFracSum) > 1e-10) - throw InvalidParameterException("Sum of field PAR_TYPE_VOLFRAC differs from 1.0 (is " + std::to_string(volFracSum) + ") in axial cell " + std::to_string(i / _disc.nRad) + " radial cell " + std::to_string(i % _disc.nRad)); + throw InvalidParameterException( + "Sum of field PAR_TYPE_VOLFRAC differs from 1.0 (is " + std::to_string(volFracSum) + + ") in axial cell " + std::to_string(i / _disc.nRad) + " radial cell " + std::to_string(i % _disc.nRad)); } // Read vectorial parameters (which may also be section dependent; transport) - _filmDiffusionMode = readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _filmDiffusion, "FILM_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); - _parDiffusionMode = readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _parDiffusion, "PAR_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); + _filmDiffusionMode = readAndRegisterMultiplexCompTypeSecParam( + paramProvider, _parameters, _filmDiffusion, "FILM_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); + _parDiffusionMode = readAndRegisterMultiplexCompTypeSecParam( + paramProvider, _parameters, _parDiffusion, "PAR_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); if (paramProvider.exists("PAR_SURFDIFFUSION")) - _parSurfDiffusionMode = readAndRegisterMultiplexBndCompTypeSecParam(paramProvider, _parameters, _parSurfDiffusion, "PAR_SURFDIFFUSION", _disc.nParType, _disc.nComp, _disc.strideBound, _disc.nBound, _unitOpIdx); + _parSurfDiffusionMode = readAndRegisterMultiplexBndCompTypeSecParam( + paramProvider, _parameters, _parSurfDiffusion, "PAR_SURFDIFFUSION", _disc.nParType, _disc.nComp, + _disc.strideBound, _disc.nBound, _unitOpIdx); else { _parSurfDiffusionMode = MultiplexMode::Component; _parSurfDiffusion.resize(_disc.strideBound[_disc.nParType], 0.0); } - if ((_filmDiffusion.size() < _disc.nComp * _disc.nParType) || (_filmDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) - throw InvalidParameterException("Number of elements in field FILM_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + ")"); - if ((_parDiffusion.size() < _disc.nComp * _disc.nParType) || (_parDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) - throw InvalidParameterException("Number of elements in field PAR_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + ")"); - if ((_parSurfDiffusion.size() < _disc.strideBound[_disc.nParType]) || ((_disc.strideBound[_disc.nParType] > 0) && (_parSurfDiffusion.size() % _disc.strideBound[_disc.nParType] != 0))) - throw InvalidParameterException("Number of elements in field PAR_SURFDIFFUSION is not a positive multiple of NTOTALBND (" + std::to_string(_disc.strideBound[_disc.nParType]) + ")"); + if ((_filmDiffusion.size() < _disc.nComp * _disc.nParType) || + (_filmDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) + throw InvalidParameterException( + "Number of elements in field FILM_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + + std::to_string(_disc.nComp * _disc.nParType) + ")"); + if ((_parDiffusion.size() < _disc.nComp * _disc.nParType) || + (_parDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) + throw InvalidParameterException( + "Number of elements in field PAR_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + + std::to_string(_disc.nComp * _disc.nParType) + ")"); + if ((_parSurfDiffusion.size() < _disc.strideBound[_disc.nParType]) || + ((_disc.strideBound[_disc.nParType] > 0) && + (_parSurfDiffusion.size() % _disc.strideBound[_disc.nParType] != 0))) + throw InvalidParameterException( + "Number of elements in field PAR_SURFDIFFUSION is not a positive multiple of NTOTALBND (" + + std::to_string(_disc.strideBound[_disc.nParType]) + ")"); if (paramProvider.exists("PORE_ACCESSIBILITY")) - _poreAccessFactorMode = readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _poreAccessFactor, "PORE_ACCESSIBILITY", _disc.nParType, _disc.nComp, _unitOpIdx); + _poreAccessFactorMode = + readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _poreAccessFactor, + "PORE_ACCESSIBILITY", _disc.nParType, _disc.nComp, _unitOpIdx); else { _poreAccessFactorMode = MultiplexMode::ComponentType; @@ -810,7 +892,9 @@ bool GeneralRateModel2D::configure(IParameterProvider& paramProvider) } if (_disc.nComp * _disc.nParType != _poreAccessFactor.size()) - throw InvalidParameterException("Number of elements in field PORE_ACCESSIBILITY differs from NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + ")"); + throw InvalidParameterException( + "Number of elements in field PORE_ACCESSIBILITY differs from NCOMP * NPARTYPE (" + + std::to_string(_disc.nComp * _disc.nParType) + ")"); // Add parameters to map @@ -818,29 +902,52 @@ bool GeneralRateModel2D::configure(IParameterProvider& paramProvider) updateRadialDisc(); // Register initial conditions parameters - registerParam1DArray(_parameters, _initC, [=](bool multi, unsigned int comp) { return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep); }); + registerParam1DArray(_parameters, _initC, [=](bool multi, unsigned int comp) { + return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep); + }); if (_disc.nRad > 1) - registerParam2DArray(_parameters, _initC, [=](bool multi, unsigned int rad, unsigned int comp) { return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, rad, SectionIndep); }, _disc.nComp); + registerParam2DArray( + _parameters, _initC, + [=](bool multi, unsigned int rad, unsigned int comp) { + return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, rad, + SectionIndep); + }, + _disc.nComp); if (_singleBinding) { for (unsigned int c = 0; c < _disc.nComp; ++c) - _parameters[makeParamId(hashString("INIT_CP"), _unitOpIdx, c, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_initCp[c]; + _parameters[makeParamId(hashString("INIT_CP"), _unitOpIdx, c, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep)] = &_initCp[c]; if (_disc.nRad > 1) { for (unsigned int r = 0; r < _disc.nRad; ++r) { for (unsigned int c = 0; c < _disc.nComp; ++c) - _parameters[makeParamId(hashString("INIT_CP"), _unitOpIdx, c, ParTypeIndep, BoundStateIndep, r, SectionIndep)] = &_initCp[r * _disc.nComp * _disc.nParType + c]; + _parameters[makeParamId(hashString("INIT_CP"), _unitOpIdx, c, ParTypeIndep, BoundStateIndep, r, + SectionIndep)] = &_initCp[r * _disc.nComp * _disc.nParType + c]; } } } else { - registerParam2DArray(_parameters, _initCp, [=](bool multi, unsigned int type, unsigned int comp) { return makeParamId(hashString("INIT_CP"), _unitOpIdx, comp, type, BoundStateIndep, ReactionIndep, SectionIndep); }, _disc.nComp); + registerParam2DArray( + _parameters, _initCp, + [=](bool multi, unsigned int type, unsigned int comp) { + return makeParamId(hashString("INIT_CP"), _unitOpIdx, comp, type, BoundStateIndep, ReactionIndep, + SectionIndep); + }, + _disc.nComp); if (_disc.nRad > 1) - registerParam3DArray(_parameters, _initCp, [=](bool multi, unsigned int rad, unsigned int type, unsigned int comp) { return makeParamId(hashString("INIT_CP"), _unitOpIdx, comp, type, BoundStateIndep, rad, SectionIndep); }, _disc.nComp, _disc.nParType); + registerParam3DArray( + _parameters, _initCp, + [=](bool multi, unsigned int rad, unsigned int type, unsigned int comp) { + return makeParamId(hashString("INIT_CP"), _unitOpIdx, comp, type, BoundStateIndep, rad, + SectionIndep); + }, + _disc.nComp, _disc.nParType); } if (!_binding.empty()) @@ -881,7 +988,8 @@ bool GeneralRateModel2D::configure(IParameterProvider& paramProvider) for (ParameterId& pId : initParams) pId.reaction = r; - active* const iq = _initQ.data() + _disc.nBoundBeforeType[0] + r * _disc.strideBound[_disc.nParType]; + active* const iq = + _initQ.data() + _disc.nBoundBeforeType[0] + r * _disc.strideBound[_disc.nParType]; for (unsigned int i = 0; i < _disc.strideBound[0]; ++i) _parameters[initParams[i]] = iq + i; } @@ -896,7 +1004,8 @@ bool GeneralRateModel2D::configure(IParameterProvider& paramProvider) for (ParameterId& pId : initParams) pId.reaction = r; - active* const iq = _initQ.data() + _disc.nBoundBeforeType[type] + r * _disc.strideBound[_disc.nParType]; + active* const iq = + _initQ.data() + _disc.nBoundBeforeType[type] + r * _disc.strideBound[_disc.nParType]; for (unsigned int i = 0; i < _disc.strideBound[type]; ++i) _parameters[initParams[i]] = iq + i; } @@ -921,10 +1030,10 @@ bool GeneralRateModel2D::configure(IParameterProvider& paramProvider) { for (unsigned int type = 0; type < _disc.nParType; ++type) { - if (!_binding[type] || !_binding[type]->requiresConfiguration()) - continue; + if (!_binding[type] || !_binding[type]->requiresConfiguration()) + continue; - // Check whether required = true and no isActive() check should be performed + // Check whether required = true and no isActive() check should be performed MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", type, _disc.nParType == 1, false); if (!scopeGuard.isActive()) continue; @@ -948,25 +1057,26 @@ bool GeneralRateModel2D::configure(IParameterProvider& paramProvider) if (_dynReaction[0] && _dynReaction[0]->requiresConfiguration()) { MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", true); - dynReactionConfSuccess = _dynReaction[0]->configure(paramProvider, _unitOpIdx, ParTypeIndep) && dynReactionConfSuccess; + dynReactionConfSuccess = + _dynReaction[0]->configure(paramProvider, _unitOpIdx, ParTypeIndep) && dynReactionConfSuccess; } } else { for (unsigned int type = 0; type < _disc.nParType; ++type) { - if (!_dynReaction[type] || !_dynReaction[type]->requiresConfiguration()) - continue; + if (!_dynReaction[type] || !_dynReaction[type]->requiresConfiguration()) + continue; MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", type, _disc.nParType == 1, true); - dynReactionConfSuccess = _dynReaction[type]->configure(paramProvider, _unitOpIdx, type) && dynReactionConfSuccess; + dynReactionConfSuccess = + _dynReaction[type]->configure(paramProvider, _unitOpIdx, type) && dynReactionConfSuccess; } } return transportSuccess && bindingConfSuccess && dynReactionConfSuccess; } - unsigned int GeneralRateModel2D::threadLocalMemorySize() const CADET_NOEXCEPT { LinearMemorySizer lms; @@ -978,7 +1088,8 @@ unsigned int GeneralRateModel2D::threadLocalMemorySize() const CADET_NOEXCEPT lms.fitBlock(_binding[i]->workspaceSize(_disc.nComp, _disc.strideBound[i], _disc.nBound + i * _disc.nComp)); if (_dynReaction[i] && _dynReaction[i]->requiresWorkspace()) - lms.fitBlock(_dynReaction[i]->workspaceSize(_disc.nComp, _disc.strideBound[i], _disc.nBound + i * _disc.nComp)); + lms.fitBlock( + _dynReaction[i]->workspaceSize(_disc.nComp, _disc.strideBound[i], _disc.nBound + i * _disc.nComp)); } if (_dynReactionBulk && _dynReactionBulk->requiresWorkspace()) @@ -1041,7 +1152,9 @@ void GeneralRateModel2D::useAnalyticJacobian(const bool analyticJac) #endif } -void GeneralRateModel2D::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) +void GeneralRateModel2D::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac) { // Setup flux Jacobian blocks at the beginning of the simulation or in case of // section dependent film or particle diffusion coefficients @@ -1098,7 +1211,6 @@ void GeneralRateModel2D::reportSolutionStructure(ISolutionRecorder& recorder) co recorder.unitOperationStructure(_unitOpIdx, *this, expr); } - unsigned int GeneralRateModel2D::requiredADdirs() const CADET_NOEXCEPT { const unsigned int numDirsBinding = maxBindingAdDirs(); @@ -1126,7 +1238,9 @@ void GeneralRateModel2D::prepareADvectors(const AdJacobianParams& adJac) const for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nRad; ++pblk) { - ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), adJac.adDirOffset, idxr.strideParBlock(type), lowerParBandwidth, upperParBandwidth, lowerParBandwidth); + ad::prepareAdVectorSeedsForBandMatrix( + adJac.adY + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), adJac.adDirOffset, + idxr.strideParBlock(type), lowerParBandwidth, upperParBandwidth, lowerParBandwidth); } } } @@ -1146,7 +1260,8 @@ void GeneralRateModel2D::extractJacobianFromAD(active const* const adRes, unsign for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nRad; ++pblk) { linalg::BandMatrix& jacMat = _jacP[_disc.nCol * _disc.nRad * type + pblk]; - ad::extractBandedJacobianFromAd(adRes + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), adDirOffset, jacMat.lowerBandwidth(), jacMat); + ad::extractBandedJacobianFromAd(adRes + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), + adDirOffset, jacMat.lowerBandwidth(), jacMat); } } } @@ -1172,7 +1287,9 @@ void GeneralRateModel2D::checkAnalyticJacobianAgainstAd(active const* const adRe for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nRad; ++pblk) { linalg::BandMatrix& jacMat = _jacP[_disc.nCol * _disc.nRad * type + pblk]; - const double localDiff = ad::compareBandedJacobianWithAd(adRes + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), adDirOffset, jacMat.lowerBandwidth(), jacMat); + const double localDiff = + ad::compareBandedJacobianWithAd(adRes + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), + adDirOffset, jacMat.lowerBandwidth(), jacMat); LOG(Debug) << "-> Par type " << type << " block " << pblk << " diff: " << localDiff; maxDiffPar = std::max(maxDiffPar, localDiff); } @@ -1181,7 +1298,8 @@ void GeneralRateModel2D::checkAnalyticJacobianAgainstAd(active const* const adRe #endif -int GeneralRateModel2D::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel2D::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); @@ -1194,15 +1312,19 @@ int GeneralRateModel2D::jacobian(const SimulationTime& simTime, const ConstSimul return residual(simTime, simState, res, adJac, threadLocalMem, true, false); } -int GeneralRateModel2D::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel2D::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); // Evaluate residual do not compute Jacobian or parameter sensitivities - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } -int GeneralRateModel2D::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel2D::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); @@ -1211,7 +1333,8 @@ int GeneralRateModel2D::residualWithJacobian(const SimulationTime& simTime, cons } int GeneralRateModel2D::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, - const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity) + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, + bool updateJacobian, bool paramSensitivity) { if (updateJacobian) { @@ -1222,7 +1345,8 @@ int GeneralRateModel2D::residual(const SimulationTime& simTime, const ConstSimul { if (paramSensitivity) { - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -1231,7 +1355,8 @@ int GeneralRateModel2D::residual(const SimulationTime& simTime, const ConstSimul return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } else { @@ -1246,9 +1371,11 @@ int GeneralRateModel2D::residual(const SimulationTime& simTime, const ConstSimul // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -1271,15 +1398,18 @@ int GeneralRateModel2D::residual(const SimulationTime& simTime, const ConstSimul // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); // Only do comparison if we have a residuals vector (which is not always the case) if (res) { // Evaluate with analytical Jacobian which is stored in the band matrices - retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); // Compare AD with anaytic Jacobian checkAnalyticJacobianAgainstAd(adJac.adRes, adJac.adDirOffset); @@ -1299,7 +1429,8 @@ int GeneralRateModel2D::residual(const SimulationTime& simTime, const ConstSimul // @todo Check if this is necessary ad::resetAd(adJac.adRes, numDofs()); - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -1308,12 +1439,14 @@ int GeneralRateModel2D::residual(const SimulationTime& simTime, const ConstSimul return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } } template -int GeneralRateModel2D::residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel2D::residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, + ResidualType* const res, util::ThreadLocalStorage& threadLocalMem) { BENCH_START(_timerResidualPar); @@ -1329,7 +1462,8 @@ int GeneralRateModel2D::residualImpl(double t, unsigned int secIdx, StateType co { const unsigned int type = (pblk - 1) / (_disc.nCol * _disc.nRad); const unsigned int par = (pblk - 1) % (_disc.nCol * _disc.nRad); - residualParticle(t, type, par, secIdx, y, yDot, res, threadLocalMem); + residualParticle(t, type, par, secIdx, y, yDot, res, + threadLocalMem); } } CADET_PARFOR_END; @@ -1347,7 +1481,8 @@ int GeneralRateModel2D::residualImpl(double t, unsigned int secIdx, StateType co } template -int GeneralRateModel2D::residualBulk(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel2D::residualBulk(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, + ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem) { _convDispOp.residual(*this, t, secIdx, yBase, yDotBase, resBase, wantJac, typename ParamSens::enabled()); if (!_dynReactionBulk || (_dynReactionBulk->numReactionsLiquid() == 0)) @@ -1359,11 +1494,13 @@ int GeneralRateModel2D::residualBulk(double t, unsigned int secIdx, StateType co ResidualType* res = resBase + idxr.offsetC(); LinearBufferAllocator tlmAlloc = threadLocalMem.get(); - for (unsigned int colCell = 0; colCell < _disc.nCol * _disc.nRad; ++colCell, y += idxr.strideColRadialCell(), res += idxr.strideColRadialCell()) + for (unsigned int colCell = 0; colCell < _disc.nCol * _disc.nRad; + ++colCell, y += idxr.strideColRadialCell(), res += idxr.strideColRadialCell()) { const unsigned int axialCell = colCell / _disc.nRad; const unsigned int radialCell = colCell % _disc.nRad; - const double r = static_cast(_convDispOp.radialCenters()[radialCell]) / static_cast(_convDispOp.columnRadius()); + const double r = static_cast(_convDispOp.radialCenters()[radialCell]) / + static_cast(_convDispOp.columnRadius()); const double z = (0.5 + static_cast(axialCell)) / static_cast(_disc.nCol); const ColumnPosition colPos{z, r, 0.0}; @@ -1372,7 +1509,9 @@ int GeneralRateModel2D::residualBulk(double t, unsigned int secIdx, StateType co if (wantJac) { // static_cast should be sufficient here, but this statement is also analyzed when wantJac = false - _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(y), -1.0, _convDispOp.jacobian().row(colCell * idxr.strideColRadialCell()), tlmAlloc); + _dynReactionBulk->analyticJacobianLiquidAdd( + t, secIdx, colPos, reinterpret_cast(y), -1.0, + _convDispOp.jacobian().row(colCell * idxr.strideColRadialCell()), tlmAlloc); } } @@ -1380,7 +1519,9 @@ int GeneralRateModel2D::residualBulk(double t, unsigned int secIdx, StateType co } template -int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigned int colCell, unsigned int secIdx, StateType const* yBase, double const* yDotBase, ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigned int colCell, unsigned int secIdx, + StateType const* yBase, double const* yDotBase, ResidualType* resBase, + util::ThreadLocalStorage& threadLocalMem) { Indexer idxr(_disc); @@ -1392,16 +1533,20 @@ int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigne LinearBufferAllocator tlmAlloc = threadLocalMem.get(); // Prepare parameters - active const* const parDiff = getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + parType * _disc.nComp; + active const* const parDiff = + getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + parType * _disc.nComp; // Ordering of particle surface diffusion: // bnd0comp0, bnd0comp1, bnd0comp2, bnd1comp0, bnd1comp1, bnd1comp2 - active const* const parSurfDiff = getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + _disc.nBoundBeforeType[parType]; + active const* const parSurfDiff = + getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + + _disc.nBoundBeforeType[parType]; // Midpoint of current column cell (z, rho coordinate) - needed in externally dependent adsorption kinetic const unsigned int axialCell = colCell / _disc.nRad; const unsigned int radialCell = colCell % _disc.nRad; - const double r = static_cast(_convDispOp.radialCenters()[radialCell]) / static_cast(_convDispOp.columnRadius()); + const double r = + static_cast(_convDispOp.radialCenters()[radialCell]) / static_cast(_convDispOp.columnRadius()); const double z = (0.5 + static_cast(axialCell)) / static_cast(_disc.nCol); // Reset Jacobian @@ -1421,28 +1566,28 @@ int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigne int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - const parts::cell::CellParameters cellResParams - { - _disc.nComp, - _disc.nBound + _disc.nComp * parType, - _disc.boundOffset + _disc.nComp * parType, - _disc.strideBound[parType], - qsReaction, - _parPorosity[parType], - _poreAccessFactor.data() + _disc.nComp * parType, - _binding[parType], - (_dynReaction[parType] && (_dynReaction[parType]->numReactionsCombined() > 0)) ? _dynReaction[parType] : nullptr - }; + const parts::cell::CellParameters cellResParams{ + _disc.nComp, + _disc.nBound + _disc.nComp * parType, + _disc.boundOffset + _disc.nComp * parType, + _disc.strideBound[parType], + qsReaction, + _parPorosity[parType], + _poreAccessFactor.data() + _disc.nComp * parType, + _binding[parType], + (_dynReaction[parType] && (_dynReaction[parType]->numReactionsCombined() > 0)) ? _dynReaction[parType] + : nullptr}; // Loop over particle cells for (unsigned int par = 0; par < _disc.nParCell[parType]; ++par) { - const ColumnPosition colPos{z, r, static_cast(parCenterRadius[par]) / static_cast(_parRadius[parType])}; + const ColumnPosition colPos{ + z, r, static_cast(parCenterRadius[par]) / static_cast(_parRadius[parType])}; // Handle time derivatives, binding, dynamic reactions - parts::cell::residualKernel( - t, secIdx, colPos, y, yDotBase ? yDot : nullptr, res, jac, cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + t, secIdx, colPos, y, yDotBase ? yDot : nullptr, res, jac, cellResParams, tlmAlloc); // We still need to handle transport and quasi-stationary reactions @@ -1454,7 +1599,9 @@ int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigne for (unsigned int comp = 0; comp < _disc.nComp; ++comp, ++res, ++y, ++yDot, ++jac) { const unsigned int nBound = _disc.nBound[_disc.nComp * parType + comp]; - const ParamType invBetaP = (1.0 - static_cast(_parPorosity[parType])) / (static_cast(_poreAccessFactor[_disc.nComp * parType + comp]) * static_cast(_parPorosity[parType])); + const ParamType invBetaP = (1.0 - static_cast(_parPorosity[parType])) / + (static_cast(_poreAccessFactor[_disc.nComp * parType + comp]) * + static_cast(_parPorosity[parType])); const ParamType dp = static_cast(parDiff[comp]); // Add flow through outer surface @@ -1462,7 +1609,8 @@ int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigne if (cadet_likely(par != 0)) { // Difference between two cell-centers - const ParamType dr = static_cast(parCenterRadius[par - 1]) - static_cast(parCenterRadius[par]); + const ParamType dr = + static_cast(parCenterRadius[par - 1]) - static_cast(parCenterRadius[par]); // Molecular diffusion contribution const ResidualType gradCp = (y[-idxr.strideParShell(parType)] - y[0]) / dr; @@ -1472,9 +1620,14 @@ int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigne for (unsigned int i = 0; i < nBound; ++i) { // See above for explanation of curIdx value - const int curIdx = idxr.strideParLiquid() - comp + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; + const int curIdx = idxr.strideParLiquid() - comp + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; const ResidualType gradQ = (y[-idxr.strideParShell(parType) + curIdx] - y[curIdx]) / dr; - *res -= outerAreaPerVolume * static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i]) * invBetaP * gradQ; + *res -= + outerAreaPerVolume * + static_cast( + parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i]) * + invBetaP * gradQ; } if (wantJac) @@ -1485,15 +1638,27 @@ int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigne // Liquid phase jac[0] += ouApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j) - jac[-idxr.strideParShell(parType)] = -ouApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j-1) + jac[-idxr.strideParShell(parType)] = + -ouApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j-1) // Solid phase for (unsigned int i = 0; i < nBound; ++i) { // See above for explanation of curIdx value - const int curIdx = idxr.strideParLiquid() - comp + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; - jac[curIdx] += ouApV * localInvBetaP * static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i]) / ldr; // dres / dq_i^(p,j) - jac[-idxr.strideParShell(parType) + curIdx] = -ouApV * localInvBetaP * static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i]) / ldr; // dres / dq_i^(p,j-1) + const int curIdx = idxr.strideParLiquid() - comp + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; + jac[curIdx] += + ouApV * localInvBetaP * + static_cast( + parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + i]) / + ldr; // dres / dq_i^(p,j) + jac[-idxr.strideParShell(parType) + curIdx] = + -ouApV * localInvBetaP * + static_cast( + parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + i]) / + ldr; // dres / dq_i^(p,j-1) } } } @@ -1503,7 +1668,8 @@ int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigne if (cadet_likely(par != _disc.nParCell[parType] - 1)) { // Difference between two cell-centers - const ParamType dr = static_cast(parCenterRadius[par]) - static_cast(parCenterRadius[par + 1]); + const ParamType dr = + static_cast(parCenterRadius[par]) - static_cast(parCenterRadius[par + 1]); // Molecular diffusion contribution const ResidualType gradCp = (y[0] - y[idxr.strideParShell(parType)]) / dr; @@ -1513,9 +1679,15 @@ int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigne for (unsigned int i = 0; i < nBound; ++i) { // See above for explanation of curIdx value - const unsigned int curIdx = idxr.strideParLiquid() - comp + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; + const unsigned int curIdx = idxr.strideParLiquid() - comp + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + i; const ResidualType gradQ = (y[curIdx] - y[idxr.strideParShell(parType) + curIdx]) / dr; - *res += innerAreaPerVolume * static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i]) * invBetaP * gradQ; + *res += + innerAreaPerVolume * + static_cast( + parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i]) * + invBetaP * gradQ; } if (wantJac) @@ -1525,16 +1697,27 @@ int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigne const double ldr = static_cast(dr); // Liquid phase - jac[0] += inApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j) + jac[0] += inApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j) jac[idxr.strideParShell(parType)] = -inApV * static_cast(dp) / ldr; // dres / dc_p,i^(p,j+1) // Solid phase for (unsigned int i = 0; i < nBound; ++i) { // See above for explanation of curIdx value - const int curIdx = idxr.strideParLiquid() - comp + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; - jac[curIdx] += inApV * localInvBetaP * static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i]) / ldr; // dres / dq_i^(p,j) - jac[idxr.strideParShell(parType) + curIdx] = -inApV * localInvBetaP * static_cast(parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i]) / ldr; // dres / dq_i^(p,j-1) + const int curIdx = idxr.strideParLiquid() - comp + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + i; + jac[curIdx] += + inApV * localInvBetaP * + static_cast( + parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + i]) / + ldr; // dres / dq_i^(p,j) + jac[idxr.strideParShell(parType) + curIdx] = + -inApV * localInvBetaP * + static_cast( + parSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + i]) / + ldr; // dres / dq_i^(p,j-1) } } } @@ -1552,7 +1735,8 @@ int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigne if (cadet_likely(par != 0)) { // Difference between two cell-centers - const ParamType dr = static_cast(parCenterRadius[par - 1]) - static_cast(parCenterRadius[par]); + const ParamType dr = + static_cast(parCenterRadius[par - 1]) - static_cast(parCenterRadius[par]); const ResidualType gradQ = (y[-idxr.strideParShell(parType)] - y[0]) / dr; *res -= outerAreaPerVolume * static_cast(parSurfDiff[bnd]) * gradQ; @@ -1563,7 +1747,8 @@ int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigne const double ldr = static_cast(dr); jac[0] += ouApV * static_cast(parSurfDiff[bnd]) / ldr; // dres / dq_i^(p,j) - jac[-idxr.strideParShell(parType)] += -ouApV * static_cast(parSurfDiff[bnd]) / ldr; // dres / dq_i^(p,j-1) + jac[-idxr.strideParShell(parType)] += + -ouApV * static_cast(parSurfDiff[bnd]) / ldr; // dres / dq_i^(p,j-1) } } @@ -1572,7 +1757,8 @@ int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigne if (cadet_likely(par != _disc.nParCell[parType] - 1)) { // Difference between two cell-centers - const ParamType dr = static_cast(parCenterRadius[par]) - static_cast(parCenterRadius[par + 1]); + const ParamType dr = + static_cast(parCenterRadius[par]) - static_cast(parCenterRadius[par + 1]); const ResidualType gradQ = (y[0] - y[idxr.strideParShell(parType)]) / dr; *res += innerAreaPerVolume * static_cast(parSurfDiff[bnd]) * gradQ; @@ -1583,7 +1769,8 @@ int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigne const double ldr = static_cast(dr); jac[0] += inApV * static_cast(parSurfDiff[bnd]) / ldr; // dres / dq_i^(p,j) - jac[idxr.strideParShell(parType)] += -inApV * static_cast(parSurfDiff[bnd]) / ldr; // dres / dq_i^(p,j-1) + jac[idxr.strideParShell(parType)] += + -inApV * static_cast(parSurfDiff[bnd]) / ldr; // dres / dq_i^(p,j-1) } } } @@ -1595,7 +1782,8 @@ int GeneralRateModel2D::residualParticle(double t, unsigned int parType, unsigne } template -int GeneralRateModel2D::residualFlux(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, ResidualType* resBase) +int GeneralRateModel2D::residualFlux(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, + ResidualType* resBase) { Indexer idxr(_disc); @@ -1626,20 +1814,27 @@ int GeneralRateModel2D::residualFlux(double t, unsigned int secIdx, StateType co // Ordering of diffusion: // sec0type0comp0, sec0type0comp1, sec0type0comp2, sec0type1comp0, sec0type1comp1, sec0type1comp2, // sec1type0comp0, sec1type0comp1, sec1type0comp2, sec1type1comp0, sec1type1comp1, sec1type1comp2, ... - active const* const filmDiff = getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; - active const* const parDiff = getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const filmDiff = + getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const parDiff = + getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; const ParamType surfaceToVolumeRatio = _parGeomSurfToVol[type] / static_cast(_parRadius[type]); - const ParamType outerAreaPerVolume = static_cast(_parOuterSurfAreaPerVolume[_disc.nParCellsBeforeType[type]]); + const ParamType outerAreaPerVolume = + static_cast(_parOuterSurfAreaPerVolume[_disc.nParCellsBeforeType[type]]); const ParamType jacPF_val = -outerAreaPerVolume / epsP; // Discretized film diffusion kf for finite volumes if (cadet_likely(_colParBoundaryOrder == 2)) { - const ParamType absOuterShellHalfRadius = 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); + const ParamType absOuterShellHalfRadius = + 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - kf_FV[comp] = 1.0 / (absOuterShellHalfRadius / epsP / static_cast(_poreAccessFactor[type * _disc.nComp + comp]) / static_cast(parDiff[comp]) + 1.0 / static_cast(filmDiff[comp])); + kf_FV[comp] = 1.0 / (absOuterShellHalfRadius / epsP / + static_cast(_poreAccessFactor[type * _disc.nComp + comp]) / + static_cast(parDiff[comp]) + + 1.0 / static_cast(filmDiff[comp])); } else { @@ -1657,7 +1852,10 @@ int GeneralRateModel2D::residualFlux(double t, unsigned int secIdx, StateType co const ParamType jacCF_val = invBetaC * surfaceToVolumeRatio; for (unsigned int k = 0; k < _disc.nComp; ++k, ++idx) { - resCol[idx] += jacCF_val * static_cast(_parTypeVolFrac[type + i * _disc.nParType * _disc.nRad + j * _disc.nParType]) * yFluxType[idx]; + resCol[idx] += jacCF_val * + static_cast( + _parTypeVolFrac[type + i * _disc.nParType * _disc.nRad + j * _disc.nParType]) * + yFluxType[idx]; } } } @@ -1678,7 +1876,8 @@ int GeneralRateModel2D::residualFlux(double t, unsigned int secIdx, StateType co for (unsigned int comp = 0; comp < _disc.nComp; ++comp) { const unsigned int eq = pblk * idxr.strideColRadialCell() + comp * idxr.strideColComp(); - resParType[pblk * idxr.strideParBlock(type) + comp] += jacPF_val / static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * yFluxType[eq]; + resParType[pblk * idxr.strideParBlock(type) + comp] += + jacPF_val / static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * yFluxType[eq]; } } @@ -1698,16 +1897,23 @@ int GeneralRateModel2D::residualFlux(double t, unsigned int secIdx, StateType co // Ordering of particle surface diffusion: // bnd0comp0, bnd0comp1, bnd0comp2, bnd1comp0, bnd1comp1, bnd1comp2 - active const* const parSurfDiff = getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + _disc.nBoundBeforeType[type]; + active const* const parSurfDiff = + getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + + _disc.nBoundBeforeType[type]; active const* const parCenterRadius = _parCenterRadius.data() + _disc.nParCellsBeforeType[type]; - const ParamType absOuterShellHalfRadius = 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); + const ParamType absOuterShellHalfRadius = + 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - kf_FV[comp] = (1.0 - static_cast(_parPorosity[type])) / (1.0 + epsP * static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * static_cast(parDiff[comp]) / (absOuterShellHalfRadius * static_cast(filmDiff[comp]))); + kf_FV[comp] = (1.0 - static_cast(_parPorosity[type])) / + (1.0 + epsP * static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * + static_cast(parDiff[comp]) / + (absOuterShellHalfRadius * static_cast(filmDiff[comp]))); for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nRad; ++pblk) { - const ParamType dr = static_cast(parCenterRadius[0]) - static_cast(parCenterRadius[1]); + const ParamType dr = + static_cast(parCenterRadius[0]) - static_cast(parCenterRadius[1]); for (unsigned int comp = 0; comp < _disc.nComp; ++comp) { @@ -1723,7 +1929,8 @@ int GeneralRateModel2D::residualFlux(double t, unsigned int secIdx, StateType co continue; const int curIdx = pblk * idxr.strideParBlock(type) + idxr.strideParLiquid() + idxBnd; - const ResidualType gradQ = (yParType[curIdx] - yParType[curIdx + idxr.strideParShell(type)]) / dr; + const ResidualType gradQ = + (yParType[curIdx] - yParType[curIdx + idxr.strideParShell(type)]) / dr; resFluxType[eq] -= kf_FV[comp] * static_cast(parSurfDiff[idxBnd]) * gradQ; } } @@ -1768,20 +1975,27 @@ void GeneralRateModel2D::assembleOffdiagJac(double t, unsigned int secIdx) // Ordering of diffusion: // sec0type0comp0, sec0type0comp1, sec0type0comp2, sec0type1comp0, sec0type1comp1, sec0type1comp2, // sec1type0comp0, sec1type0comp1, sec1type0comp2, sec1type1comp0, sec1type1comp1, sec1type1comp2, ... - active const* const filmDiff = getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; - active const* const parDiff = getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const filmDiff = + getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const parDiff = + getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; const double surfaceToVolumeRatio = _parGeomSurfToVol[type] / static_cast(_parRadius[type]); - const double outerAreaPerVolume = static_cast(_parOuterSurfAreaPerVolume[_disc.nParCellsBeforeType[type]]); + const double outerAreaPerVolume = + static_cast(_parOuterSurfAreaPerVolume[_disc.nParCellsBeforeType[type]]); const double jacPF_val = -outerAreaPerVolume / epsP; // Discretized film diffusion kf for finite volumes if (cadet_likely(_colParBoundaryOrder == 2)) { - const double absOuterShellHalfRadius = 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); + const double absOuterShellHalfRadius = + 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - kf_FV[comp] = 1.0 / (absOuterShellHalfRadius / epsP / static_cast(_poreAccessFactor[type * _disc.nComp + comp]) / static_cast(parDiff[comp]) + 1.0 / static_cast(filmDiff[comp])); + kf_FV[comp] = 1.0 / (absOuterShellHalfRadius / epsP / + static_cast(_poreAccessFactor[type * _disc.nComp + comp]) / + static_cast(parDiff[comp]) + + 1.0 / static_cast(filmDiff[comp])); } else { @@ -1801,7 +2015,11 @@ void GeneralRateModel2D::assembleOffdiagJac(double t, unsigned int secIdx) for (unsigned int comp = 0; comp < _disc.nComp; ++comp, ++idx) { // Main diagonal corresponds to j_{f,i} (flux) state variable - _jacCF.addElement(idx, idx + typeOffset, jacCF_val * static_cast(_parTypeVolFrac[type + col * _disc.nRad * _disc.nParType + rad * _disc.nParType])); + _jacCF.addElement( + idx, idx + typeOffset, + jacCF_val * + static_cast( + _parTypeVolFrac[type + col * _disc.nRad * _disc.nParType + rad * _disc.nParType])); } } } @@ -1845,12 +2063,18 @@ void GeneralRateModel2D::assembleOffdiagJac(double t, unsigned int secIdx) // Ordering of particle surface diffusion: // bnd0comp0, bnd0comp1, bnd0comp2, bnd1comp0, bnd1comp1, bnd1comp2 - active const* const parSurfDiff = getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + _disc.nBoundBeforeType[type]; + active const* const parSurfDiff = + getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + + _disc.nBoundBeforeType[type]; active const* const parCenterRadius = _parCenterRadius.data() + _disc.nParCellsBeforeType[type]; - const double absOuterShellHalfRadius = 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); + const double absOuterShellHalfRadius = + 0.5 * static_cast(_parCellSize[_disc.nParCellsBeforeType[type]]); for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - kf_FV[comp] = (1.0 - static_cast(_parPorosity[type])) / (1.0 + epsP * static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * static_cast(parDiff[comp]) / (absOuterShellHalfRadius * static_cast(filmDiff[comp]))); + kf_FV[comp] = (1.0 - static_cast(_parPorosity[type])) / + (1.0 + epsP * static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * + static_cast(parDiff[comp]) / + (absOuterShellHalfRadius * static_cast(filmDiff[comp]))); for (unsigned int pblk = 0; pblk < _disc.nCol * _disc.nRad; ++pblk) { @@ -1858,7 +2082,8 @@ void GeneralRateModel2D::assembleOffdiagJac(double t, unsigned int secIdx) for (unsigned int comp = 0; comp < _disc.nComp; ++comp) { - const unsigned int eq = typeOffset + pblk * idxr.strideColRadialCell() + comp * idxr.strideColComp(); + const unsigned int eq = + typeOffset + pblk * idxr.strideColRadialCell() + comp * idxr.strideColComp(); const unsigned int nBound = _disc.nBound[_disc.nComp * type + comp]; for (unsigned int i = 0; i < nBound; ++i) @@ -1883,7 +2108,9 @@ void GeneralRateModel2D::assembleOffdiagJac(double t, unsigned int secIdx) _discParFlux.destroy(); } -int GeneralRateModel2D::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel2D::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidualSens); @@ -1892,17 +2119,21 @@ int GeneralRateModel2D::residualSensFwdWithJacobian(const SimulationTime& simTim return residual(simTime, simState, nullptr, adJac, threadLocalMem, true, true); } -int GeneralRateModel2D::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModel2D::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidualSens); // Evaluate residual for all parameters using AD in vector mode - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adRes, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, adRes, threadLocalMem); } int GeneralRateModel2D::residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3) + const std::vector& yS, + const std::vector& ySdot, + const std::vector& resS, active const* adRes, + double* const tmp1, double* const tmp2, double* const tmp3) { BENCH_SCOPE(_timerResidualSens); @@ -1942,11 +2173,12 @@ int GeneralRateModel2D::residualSensFwdCombine(const SimulationTime& simTime, co } /** - * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, \dot{y}\right) @f$) + * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, + * \dot{y}\right) @f$) * @details Actually, the operation @f$ z = \alpha \frac{\partial F}{\partial y} x + \beta z @f$ is performed. * - * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ once - * before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. + * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ + * once before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. * @param [in] simTime Current simulation time point * @param [in] simState Simulation state vectors * @param [in] yS Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial y} @f$ @@ -1954,7 +2186,8 @@ int GeneralRateModel2D::residualSensFwdCombine(const SimulationTime& simTime, co * @param [in] beta Factor @f$ \beta @f$ in front of @f$ z @f$ * @param [in,out] ret Vector @f$ z @f$ which stores the result of the operation */ -void GeneralRateModel2D::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret) +void GeneralRateModel2D::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret) { Indexer idxr(_disc); @@ -2001,7 +2234,8 @@ void GeneralRateModel2D::multiplyWithJacobian(const SimulationTime& simTime, con { for (unsigned int par = 0; par < _disc.nCol * _disc.nRad; ++par) { - _jacFP[type * _disc.nCol * _disc.nRad + par].multiplyVector(yS + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}), alpha, 1.0, retJf); + _jacFP[type * _disc.nCol * _disc.nRad + par].multiplyVector( + yS + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}), alpha, 1.0, retJf); } } @@ -2010,7 +2244,8 @@ void GeneralRateModel2D::multiplyWithJacobian(const SimulationTime& simTime, con } /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ + * with a given vector * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). * @param [in] simTime Current simulation time point @@ -2018,7 +2253,9 @@ void GeneralRateModel2D::multiplyWithJacobian(const SimulationTime& simTime, con * @param [in] sDot Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ * @param [out] ret Vector @f$ z @f$ which stores the result of the operation */ -void GeneralRateModel2D::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret) +void GeneralRateModel2D::multiplyWithDerivativeJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, double const* sDot, + double* ret) { Indexer idxr(_disc); @@ -2046,9 +2283,13 @@ void GeneralRateModel2D::multiplyWithDerivativeJacobian(const SimulationTime& si // Particle shells for (unsigned int shell = 0; shell < _disc.nParCell[type]; ++shell) { - double const* const localSdot = sDot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}) + shell * idxr.strideParShell(type); - double* const localRet = ret + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}) + shell * idxr.strideParShell(type); - parts::cell::multiplyWithDerivativeJacobianKernel(localSdot, localRet, _disc.nComp, nBound, boundOffset, _disc.strideBound[type], qsReaction, 1.0, invBetaP); + double const* const localSdot = sDot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}) + + shell * idxr.strideParShell(type); + double* const localRet = ret + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}) + + shell * idxr.strideParShell(type); + parts::cell::multiplyWithDerivativeJacobianKernel(localSdot, localRet, _disc.nComp, nBound, + boundOffset, _disc.strideBound[type], + qsReaction, 1.0, invBetaP); } } } CADET_PARFOR_END; @@ -2113,7 +2354,8 @@ void GeneralRateModel2D::setEquidistantRadialDisc(unsigned int parType) const active radius = _parRadius[parType] - _parCoreRadius[parType]; const active dr = radius / static_cast(_disc.nParCell[parType]); - std::fill(_parCellSize.data() + _disc.nParCellsBeforeType[parType], _parCellSize.data() + _disc.nParCellsBeforeType[parType] + _disc.nParCell[parType], dr); + std::fill(_parCellSize.data() + _disc.nParCellsBeforeType[parType], + _parCellSize.data() + _disc.nParCellsBeforeType[parType] + _disc.nParCell[parType], dr); if (_parGeomSurfToVol[parType] == SurfVolRatioSphere) { @@ -2179,7 +2421,8 @@ void GeneralRateModel2D::setEquivolumeRadialDisc(unsigned int parType) { active r_out = _parRadius[parType]; active r_in = _parCoreRadius[parType]; - const active volumePerShell = (pow(_parRadius[parType], 3.0) - pow(_parCoreRadius[parType], 3.0)) / static_cast(_disc.nParCell[parType]); + const active volumePerShell = (pow(_parRadius[parType], 3.0) - pow(_parCoreRadius[parType], 3.0)) / + static_cast(_disc.nParCell[parType]); for (unsigned int cell = 0; cell < _disc.nParCell[parType]; ++cell) { @@ -2202,7 +2445,8 @@ void GeneralRateModel2D::setEquivolumeRadialDisc(unsigned int parType) { active r_out = _parRadius[parType]; active r_in = _parCoreRadius[parType]; - const active volumePerShell = (sqr(_parRadius[parType]) - sqr(_parCoreRadius[parType])) / static_cast(_disc.nParCell[parType]); + const active volumePerShell = + (sqr(_parRadius[parType]) - sqr(_parCoreRadius[parType])) / static_cast(_disc.nParCell[parType]); for (unsigned int cell = 0; cell < _disc.nParCell[parType]; ++cell) { @@ -2225,7 +2469,8 @@ void GeneralRateModel2D::setEquivolumeRadialDisc(unsigned int parType) { active r_out = _parRadius[parType]; active r_in = _parCoreRadius[parType]; - const active volumePerShell = (_parRadius[parType] - _parCoreRadius[parType]) / static_cast(_disc.nParCell[parType]); + const active volumePerShell = + (_parRadius[parType] - _parCoreRadius[parType]) / static_cast(_disc.nParCell[parType]); for (unsigned int cell = 0; cell < _disc.nParCell[parType]; ++cell) { @@ -2258,7 +2503,8 @@ void GeneralRateModel2D::setUserdefinedRadialDisc(unsigned int parType) active* const ptrInnerSurfAreaPerVolume = _parInnerSurfAreaPerVolume.data() + _disc.nParCellsBeforeType[parType]; // Care for the right ordering and include 0.0 / 1.0 if not already in the vector. - std::vector orderedInterfaces = std::vector(_parDiscVector.begin() + _disc.nParCellsBeforeType[parType] + parType, + std::vector orderedInterfaces = std::vector( + _parDiscVector.begin() + _disc.nParCellsBeforeType[parType] + parType, _parDiscVector.begin() + _disc.nParCellsBeforeType[parType] + parType + _disc.nParCell[parType] + 1); // Sort in descending order @@ -2270,7 +2516,9 @@ void GeneralRateModel2D::setUserdefinedRadialDisc(unsigned int parType) // Map [0, 1] -> [core radius, particle radius] via linear interpolation for (unsigned int cell = 0; cell < _disc.nParCell[parType]; ++cell) - orderedInterfaces[cell] = static_cast(orderedInterfaces[cell]) * (_parRadius[parType] - _parCoreRadius[parType]) + _parCoreRadius[parType]; + orderedInterfaces[cell] = + static_cast(orderedInterfaces[cell]) * (_parRadius[parType] - _parCoreRadius[parType]) + + _parCoreRadius[parType]; if (_parGeomSurfToVol[parType] == SurfVolRatioSphere) { @@ -2333,15 +2581,21 @@ bool GeneralRateModel2D::setParameter(const ParameterId& pId, double value) { if (pId.unitOperation == _unitOpIdx) { - if (multiplexParameterValue(pId, hashString("PAR_TYPE_VOLFRAC"), _parTypeVolFracMode, _parTypeVolFrac, _disc.nCol, _disc.nRad, _disc.nParType, value, nullptr)) + if (multiplexParameterValue(pId, hashString("PAR_TYPE_VOLFRAC"), _parTypeVolFracMode, _parTypeVolFrac, + _disc.nCol, _disc.nRad, _disc.nParType, value, nullptr)) return true; - if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, _poreAccessFactor, _disc.nParType, _disc.nComp, value, nullptr)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, + _poreAccessFactor, _disc.nParType, _disc.nComp, value, nullptr)) return true; - if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, _disc.nParType, _disc.nComp, value, nullptr)) + if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, + _disc.nParType, _disc.nComp, value, nullptr)) return true; - if (multiplexCompTypeSecParameterValue(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, _disc.nParType, _disc.nComp, value, nullptr)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, + _disc.nParType, _disc.nComp, value, nullptr)) return true; - if (multiplexBndCompTypeSecParameterValue(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, _disc.nBound, _disc.boundOffset, value, nullptr)) + if (multiplexBndCompTypeSecParameterValue(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, + _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, + _disc.nBound, _disc.boundOffset, value, nullptr)) return true; const int mpIc = multiplexInitialConditions(pId, value, false); if (mpIc > 0) @@ -2351,9 +2605,11 @@ bool GeneralRateModel2D::setParameter(const ParameterId& pId, double value) if (multiplexTypeParameterValue(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, value, nullptr)) return true; - if (multiplexTypeParameterValue(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, value, nullptr)) + if (multiplexTypeParameterValue(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, value, + nullptr)) return true; - if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, nullptr)) + if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, + nullptr)) return true; if (_convDispOp.setParameter(pId, value)) @@ -2373,24 +2629,33 @@ void GeneralRateModel2D::setSensitiveParameterValue(const ParameterId& pId, doub { if (pId.unitOperation == _unitOpIdx) { - if (multiplexParameterValue(pId, hashString("PAR_TYPE_VOLFRAC"), _parTypeVolFracMode, _parTypeVolFrac, _disc.nCol, _disc.nRad, _disc.nParType, value, &_sensParams)) + if (multiplexParameterValue(pId, hashString("PAR_TYPE_VOLFRAC"), _parTypeVolFracMode, _parTypeVolFrac, + _disc.nCol, _disc.nRad, _disc.nParType, value, &_sensParams)) return; - if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, _poreAccessFactor, _disc.nParType, _disc.nComp, value, &_sensParams)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, + _poreAccessFactor, _disc.nParType, _disc.nComp, value, &_sensParams)) return; - if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, _disc.nParType, _disc.nComp, value, &_sensParams)) + if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, + _disc.nParType, _disc.nComp, value, &_sensParams)) return; - if (multiplexCompTypeSecParameterValue(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, _disc.nParType, _disc.nComp, value, &_sensParams)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, + _disc.nParType, _disc.nComp, value, &_sensParams)) return; - if (multiplexBndCompTypeSecParameterValue(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, _disc.nBound, _disc.boundOffset, value, &_sensParams)) + if (multiplexBndCompTypeSecParameterValue(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, + _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, + _disc.nBound, _disc.boundOffset, value, &_sensParams)) return; if (multiplexInitialConditions(pId, value, true) != 0) return; - if (multiplexTypeParameterValue(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, value, &_sensParams)) + if (multiplexTypeParameterValue(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, value, + &_sensParams)) return; - if (multiplexTypeParameterValue(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, value, &_sensParams)) + if (multiplexTypeParameterValue(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, value, + &_sensParams)) return; - if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, &_sensParams)) + if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, + &_sensParams)) return; if (_convDispOp.setSensitiveParameterValue(_sensParams, pId, value)) @@ -2408,31 +2673,38 @@ bool GeneralRateModel2D::setSensitiveParameter(const ParameterId& pId, unsigned { if (pId.unitOperation == _unitOpIdx) { - if (multiplexParameterAD(pId, hashString("PAR_TYPE_VOLFRAC"), _parTypeVolFracMode, _parTypeVolFrac, _disc.nCol, _disc.nRad, _disc.nParType, adDirection, adValue, _sensParams)) + if (multiplexParameterAD(pId, hashString("PAR_TYPE_VOLFRAC"), _parTypeVolFracMode, _parTypeVolFrac, _disc.nCol, + _disc.nRad, _disc.nParType, adDirection, adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexCompTypeSecParameterAD(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, _poreAccessFactor, _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) + if (multiplexCompTypeSecParameterAD(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, + _poreAccessFactor, _disc.nParType, _disc.nComp, adDirection, adValue, + _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexCompTypeSecParameterAD(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) + if (multiplexCompTypeSecParameterAD(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, + _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexCompTypeSecParameterAD(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) + if (multiplexCompTypeSecParameterAD(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, + _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexBndCompTypeSecParameterAD(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, _disc.nBound, _disc.boundOffset, adDirection, adValue, _sensParams)) + if (multiplexBndCompTypeSecParameterAD(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, + _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, + _disc.nBound, _disc.boundOffset, adDirection, adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; @@ -2447,19 +2719,22 @@ bool GeneralRateModel2D::setSensitiveParameter(const ParameterId& pId, unsigned else if (mpIc < 0) return false; - if (multiplexTypeParameterAD(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, adDirection, adValue, _sensParams)) + if (multiplexTypeParameterAD(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, adDirection, adValue, + _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexTypeParameterAD(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, adDirection, adValue, _sensParams)) + if (multiplexTypeParameterAD(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, + adDirection, adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexTypeParameterAD(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, adDirection, adValue, _sensParams)) + if (multiplexTypeParameterAD(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, adDirection, + adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; @@ -2475,15 +2750,15 @@ bool GeneralRateModel2D::setSensitiveParameter(const ParameterId& pId, unsigned const bool result = UnitOperationBase::setSensitiveParameter(pId, adDirection, adValue); // Check whether particle radius or core radius has been set active and update radial discretization if necessary - // Note that we need to recompute the radial discretization variables (_parCellSize, _parCenterRadius, _parOuterSurfAreaPerVolume, _parInnerSurfAreaPerVolume) - // because their gradient has changed (although their nominal value has not changed). + // Note that we need to recompute the radial discretization variables (_parCellSize, _parCenterRadius, + // _parOuterSurfAreaPerVolume, _parInnerSurfAreaPerVolume) because their gradient has changed (although their + // nominal value has not changed). if ((pId.name == hashString("PAR_RADIUS")) || (pId.name == hashString("PAR_CORERADIUS"))) updateRadialDisc(); return result; } - int GeneralRateModel2D::Exporter::writeMobilePhase(double* buffer) const { const int blockSize = numMobilePhaseDofs(); @@ -2600,13 +2875,15 @@ int GeneralRateModel2D::Exporter::writeOutlet(double* buffer) const return _disc.nComp * _disc.nRad; } - -void registerGeneralRateModel2D(std::unordered_map>& models) +void registerGeneralRateModel2D( + std::unordered_map>& models) { - models[GeneralRateModel2D::identifier()] = [](UnitOpIdx uoId, IParameterProvider&) { return new GeneralRateModel2D(uoId); }; + models[GeneralRateModel2D::identifier()] = [](UnitOpIdx uoId, IParameterProvider&) { + return new GeneralRateModel2D(uoId); + }; models["GRM2D"] = [](UnitOpIdx uoId, IParameterProvider&) { return new GeneralRateModel2D(uoId); }; } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/GeneralRateModel2D.hpp b/src/libcadet/model/GeneralRateModel2D.hpp index 60c10ca9b..cc5a8da80 100644 --- a/src/libcadet/model/GeneralRateModel2D.hpp +++ b/src/libcadet/model/GeneralRateModel2D.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the 2D general rate model (GRM). */ @@ -46,12 +46,14 @@ class IDynamicReactionModel; /** * @brief General rate model of liquid column chromatography with 2D bulk volume (radially symmetric) * @details See @cite Guiochon2006, @cite Gu1995, @cite Felinger2004 - * + * * @f[\begin{align} - \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 c_i}{\partial z^2} - \frac{1 - \varepsilon_c}{\varepsilon_c} \frac{3}{r_p} j_{f,i} \\ - \frac{\partial c_{p,i}}{\partial t} + \frac{1 - \varepsilon_p}{\varepsilon_p} \frac{\partial q_{i}}{\partial t} &= D_{p,i} \left( \frac{\partial^2 c_{p,i}}{\partial r^2} + \frac{2}{r} \frac{\partial c_{p,i}}{\partial r} \right) + D_{s,i} \frac{1 - \varepsilon_p}{\varepsilon_p} \left( \frac{\partial^2 q_{i}}{\partial r^2} + \frac{2}{r} \frac{\partial q_{i}}{\partial r} \right) \\ - a \frac{\partial q_i}{\partial t} &= f_{\text{iso}}(c_p, q) -\end{align} @f] + \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 +c_i}{\partial z^2} - \frac{1 - \varepsilon_c}{\varepsilon_c} \frac{3}{r_p} j_{f,i} \\ + \frac{\partial c_{p,i}}{\partial t} + \frac{1 - \varepsilon_p}{\varepsilon_p} \frac{\partial q_{i}}{\partial t} &= +D_{p,i} \left( \frac{\partial^2 c_{p,i}}{\partial r^2} + \frac{2}{r} \frac{\partial c_{p,i}}{\partial r} \right) + +D_{s,i} \frac{1 - \varepsilon_p}{\varepsilon_p} \left( \frac{\partial^2 q_{i}}{\partial r^2} + \frac{2}{r} +\frac{\partial q_{i}}{\partial r} \right) \\ a \frac{\partial q_i}{\partial t} &= f_{\text{iso}}(c_p, q) \end{align} @f] @f[ \begin{align} j_{f,i} = k_{f,i} \left( c_i - c_{p,i} \left(\cdot, \cdot, r_p\right)\right) \end{align} @f] @@ -59,15 +61,15 @@ class IDynamicReactionModel; @f[ \begin{align} u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partial z}(t,0) \\ \frac{\partial c_i}{\partial z}(t,L) &= 0 \\ -\varepsilon_p D_{p,i} \frac{\partial c_{p,i}}{\partial r}(\cdot, \cdot, r_p) + (1-\varepsilon_p) D_{s,i} \frac{\partial q_{i}}{\partial r}(\cdot, \cdot, r_p) &= j_{f,i} \\ -\frac{\partial c_{p,i}}{\partial r}(\cdot, \cdot, 0) &= 0 +\varepsilon_p D_{p,i} \frac{\partial c_{p,i}}{\partial r}(\cdot, \cdot, r_p) + (1-\varepsilon_p) D_{s,i} \frac{\partial +q_{i}}{\partial r}(\cdot, \cdot, r_p) &= j_{f,i} \\ \frac{\partial c_{p,i}}{\partial r}(\cdot, \cdot, 0) &= 0 \end{align} @f] - * Methods are described in @cite VonLieres2010a (WENO, linear solver), @cite Puttmann2013 @cite Puttmann2016 (forward sensitivities, AD, band compression) + * Methods are described in @cite VonLieres2010a (WENO, linear solver), @cite Puttmann2013 @cite Puttmann2016 (forward +sensitivities, AD, band compression) */ class GeneralRateModel2D : public UnitOperationBase { public: - GeneralRateModel2D(UnitOpIdx unitOpIdx); virtual ~GeneralRateModel2D() CADET_NOEXCEPT; @@ -76,59 +78,104 @@ class GeneralRateModel2D : public UnitOperationBase virtual bool usesAD() const CADET_NOEXCEPT; virtual unsigned int requiredADdirs() const CADET_NOEXCEPT; - virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT { return _unitOpIdx; } - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } + virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT + { + return _unitOpIdx; + } + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } virtual void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT; - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return _disc.nRad; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return _disc.nRad; } - virtual bool canAccumulate() const CADET_NOEXCEPT { return false; } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return _disc.nRad; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return _disc.nRad; + } + virtual bool canAccumulate() const CADET_NOEXCEPT + { + return false; + } - static const char* identifier() { return "GENERAL_RATE_MODEL_2D"; } - virtual const char* unitOperationName() const CADET_NOEXCEPT { return identifier(); } + static const char* identifier() + { + return "GENERAL_RATE_MODEL_2D"; + } + virtual const char* unitOperationName() const CADET_NOEXCEPT + { + return identifier(); + } virtual bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper); virtual bool configure(IParameterProvider& paramProvider); - virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac); + virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac); virtual void useAnalyticJacobian(const bool analyticJac); virtual void reportSolution(ISolutionRecorder& recorder, double const* const solution) const; virtual void reportSolutionStructure(ISolutionRecorder& recorder) const; - virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem); - virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - - virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem); + virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + + virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3); + virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3); virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState); + const ConstSimulationState& simState); virtual void prepareADvectors(const AdJacobianParams& adJac) const; virtual void applyInitialCondition(const SimulationState& simState) const; virtual void readInitialCondition(IParameterProvider& paramProvider); - virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); virtual void initializeSensitivityStates(const std::vector& vecSensY) const; virtual void consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem); virtual void leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual bool hasInlet() const CADET_NOEXCEPT { return true; } - virtual bool hasOutlet() const CADET_NOEXCEPT { return true; } + virtual bool hasInlet() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasOutlet() const CADET_NOEXCEPT + { + return true; + } virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT; virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT; @@ -136,14 +183,19 @@ class GeneralRateModel2D : public UnitOperationBase virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT; virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size); - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { } + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) + { + } virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut); - virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret); - virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret); + virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret); + virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* sDot, double* ret); - inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double* ret) + inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double* ret) { multiplyWithJacobian(simTime, simState, yS, 1.0, 0.0, ret); } @@ -157,60 +209,43 @@ class GeneralRateModel2D : public UnitOperationBase #ifdef CADET_BENCHMARK_MODE virtual std::vector benchmarkTimings() const { - return std::vector({ - static_cast(numDofs()), - _timerResidual.totalElapsedTime(), - _timerResidualPar.totalElapsedTime(), - _timerResidualSens.totalElapsedTime(), - _timerResidualSensPar.totalElapsedTime(), - _timerJacobianPar.totalElapsedTime(), - _timerConsistentInit.totalElapsedTime(), - _timerConsistentInitPar.totalElapsedTime(), - _timerLinearSolve.totalElapsedTime(), - _timerFactorize.totalElapsedTime(), - _timerFactorizePar.totalElapsedTime(), - _timerMatVec.totalElapsedTime(), - _timerGmres.totalElapsedTime(), - static_cast(_gmres.numIterations()) - }); + return std::vector({static_cast(numDofs()), _timerResidual.totalElapsedTime(), + _timerResidualPar.totalElapsedTime(), _timerResidualSens.totalElapsedTime(), + _timerResidualSensPar.totalElapsedTime(), _timerJacobianPar.totalElapsedTime(), + _timerConsistentInit.totalElapsedTime(), _timerConsistentInitPar.totalElapsedTime(), + _timerLinearSolve.totalElapsedTime(), _timerFactorize.totalElapsedTime(), + _timerFactorizePar.totalElapsedTime(), _timerMatVec.totalElapsedTime(), + _timerGmres.totalElapsedTime(), static_cast(_gmres.numIterations())}); } virtual char const* const* benchmarkDescriptions() const { static const char* const desc[] = { - "DOFs", - "Residual", - "ResidualPar", - "ResidualSens", - "ResidualSensPar", - "JacobianPar", - "ConsistentInit", - "ConsistentInitPar", - "LinearSolve", - "Factorize", - "FactorizePar", - "MatVec", - "Gmres", - "NumGMRESIter" - }; + "DOFs", "Residual", "ResidualPar", "ResidualSens", "ResidualSensPar", "JacobianPar", + "ConsistentInit", "ConsistentInitPar", "LinearSolve", "Factorize", "FactorizePar", "MatVec", + "Gmres", "NumGMRESIter"}; return desc; } #endif protected: - class Indexer; - int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity); + int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, + bool paramSensitivity); template - int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); + int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, + ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); template - int residualBulk(double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, util::ThreadLocalStorage& threadLocalMem); + int residualBulk(double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, + util::ThreadLocalStorage& threadLocalMem); template - int residualParticle(double t, unsigned int parType, unsigned int colCell, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, util::ThreadLocalStorage& threadLocalMem); + int residualParticle(double t, unsigned int parType, unsigned int colCell, unsigned int secIdx, StateType const* y, + double const* yDot, ResidualType* res, util::ThreadLocalStorage& threadLocalMem); template int residualFlux(double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res); @@ -219,16 +254,18 @@ class GeneralRateModel2D : public UnitOperationBase void extractJacobianFromAD(active const* const adRes, unsigned int adDirOffset); int schurComplementMatrixVector(double const* x, double* z) const; - void assembleDiscretizedJacobianParticleBlock(unsigned int parType, unsigned int pblk, double alpha, const Indexer& idxr); - + void assembleDiscretizedJacobianParticleBlock(unsigned int parType, unsigned int pblk, double alpha, + const Indexer& idxr); + void setEquidistantRadialDisc(unsigned int parType); void setEquivolumeRadialDisc(unsigned int parType); void setUserdefinedRadialDisc(unsigned int parType); void updateRadialDisc(); - void addTimeDerivativeToJacobianParticleShell(linalg::FactorizableBandMatrix::RowIterator& jac, const Indexer& idxr, double alpha, unsigned int parType); + void addTimeDerivativeToJacobianParticleShell(linalg::FactorizableBandMatrix::RowIterator& jac, const Indexer& idxr, + double alpha, unsigned int parType); void solveForFluxes(double* const vecState, const Indexer& idxr) const; - + unsigned int numAdDirsForJacobian() const CADET_NOEXCEPT; int multiplexInitialConditions(const cadet::ParameterId& pId, unsigned int adDirection, double adValue); @@ -240,17 +277,24 @@ class GeneralRateModel2D : public UnitOperationBase struct Discretization { - unsigned int nComp; //!< Number of components - unsigned int nCol; //!< Number of column cells - unsigned int nRad; //!< Number of radial cells - unsigned int nParType; //!< Number of particle types + unsigned int nComp; //!< Number of components + unsigned int nCol; //!< Number of column cells + unsigned int nRad; //!< Number of radial cells + unsigned int nParType; //!< Number of particle types unsigned int* nParCell; //!< Array with number of radial cells in each particle type - unsigned int* nParCellsBeforeType; //!< Array with total number of radial cells before a particle type (cumulative sum of nParCell), additional last element contains total number of particle shells - unsigned int* parTypeOffset; //!< Array with offsets (in particle block) to particle type, additional last element contains total number of particle DOFs - unsigned int* nBound; //!< Array with number of bound states for each component and particle type (particle type major ordering) - unsigned int* boundOffset; //!< Array with offset to the first bound state of each component in the solid phase (particle type major ordering) - unsigned int* strideBound; //!< Total number of bound states for each particle type, additional last element contains total number of bound states for all types - unsigned int* nBoundBeforeType; //!< Array with number of bound states before a particle type (cumulative sum of strideBound) + unsigned int* + nParCellsBeforeType; //!< Array with total number of radial cells before a particle type (cumulative sum of + //!< nParCell), additional last element contains total number of particle shells + unsigned int* parTypeOffset; //!< Array with offsets (in particle block) to particle type, additional last + //!< element contains total number of particle DOFs + unsigned int* nBound; //!< Array with number of bound states for each component and particle type (particle type + //!< major ordering) + unsigned int* boundOffset; //!< Array with offset to the first bound state of each component in the solid phase + //!< (particle type major ordering) + unsigned int* strideBound; //!< Total number of bound states for each particle type, additional last element + //!< contains total number of bound states for all types + unsigned int* nBoundBeforeType; //!< Array with number of bound states before a particle type (cumulative sum of + //!< strideBound) }; enum class ParticleDiscretizationMode : int @@ -272,17 +316,21 @@ class GeneralRateModel2D : public UnitOperationBase }; Discretization _disc; //!< Discretization info -// IExternalFunction* _extFun; //!< External function (owned by library user) + // IExternalFunction* _extFun; //!< External function (owned by library user) - parts::TwoDimensionalConvectionDispersionOperator _convDispOp; //!< Convection dispersion operator for interstitial volume transport + parts::TwoDimensionalConvectionDispersionOperator + _convDispOp; //!< Convection dispersion operator for interstitial volume transport IDynamicReactionModel* _dynReactionBulk; //!< Dynamic reactions in the bulk volume linalg::BandMatrix* _jacP; //!< Particle jacobian diagonal blocks (all of them) - linalg::FactorizableBandMatrix* _jacPdisc; //!< Particle jacobian diagonal blocks (all of them) with time derivatives from BDF method + linalg::FactorizableBandMatrix* + _jacPdisc; //!< Particle jacobian diagonal blocks (all of them) with time derivatives from BDF method - linalg::DoubleSparseMatrix _jacCF; //!< Jacobian block connecting interstitial states and fluxes (interstitial transport equation) + linalg::DoubleSparseMatrix + _jacCF; //!< Jacobian block connecting interstitial states and fluxes (interstitial transport equation) linalg::DoubleSparseMatrix _jacFC; //!< Jacobian block connecting fluxes and interstitial states (flux equation) - linalg::DoubleSparseMatrix* _jacPF; //!< Jacobian blocks connecting particle states and fluxes (particle transport boundary condition) + linalg::DoubleSparseMatrix* + _jacPF; //!< Jacobian blocks connecting particle states and fluxes (particle transport boundary condition) linalg::DoubleSparseMatrix* _jacFP; //!< Jacobian blocks connecting fluxes and particle states (flux equation) linalg::DoubleSparseMatrix _jacInlet; //!< Jacobian inlet DOF block matrix connects inlet DOFs to first bulk cells @@ -293,11 +341,12 @@ class GeneralRateModel2D : public UnitOperationBase bool _singleParCoreRadius; std::vector _parPorosity; //!< Particle porosity (internal porosity) \f$ \varepsilon_p \f$ bool _singleParPorosity; - std::vector _parTypeVolFrac; //!< Volume fraction of each particle type - MultiplexMode _parTypeVolFracMode; //!< Multiplexing mode of particle volume fractions + std::vector _parTypeVolFrac; //!< Volume fraction of each particle type + MultiplexMode _parTypeVolFracMode; //!< Multiplexing mode of particle volume fractions std::vector _parDiscType; //!< Particle discretization mode - std::vector _parDiscVector; //!< Particle discretization shell edges - std::vector _parGeomSurfToVol; //!< Particle surface to volume ratio factor (i.e., 3.0 for spherical, 2.0 for cylindrical, 1.0 for hexahedral) + std::vector _parDiscVector; //!< Particle discretization shell edges + std::vector _parGeomSurfToVol; //!< Particle surface to volume ratio factor (i.e., 3.0 for spherical, 2.0 + //!< for cylindrical, 1.0 for hexahedral) // Vectorial parameters std::vector _filmDiffusion; //!< Film diffusion coefficient \f$ k_f \f$ @@ -309,11 +358,11 @@ class GeneralRateModel2D : public UnitOperationBase std::vector _poreAccessFactor; //!< Pore accessibility factor \f$ F_{\text{acc}} \f$ MultiplexMode _poreAccessFactorMode; - bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used + bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used unsigned int _jacobianAdDirs; //!< Number of AD seed vectors required for Jacobian computation - std::vector _parCellSize; //!< Particle cell / shell size - std::vector _parCenterRadius; //!< Particle cell-centered position for each particle cell + std::vector _parCellSize; //!< Particle cell / shell size + std::vector _parCenterRadius; //!< Particle cell-centered position for each particle cell std::vector _parOuterSurfAreaPerVolume; //!< Particle shell outer sphere surface to volume ratio std::vector _parInnerSurfAreaPerVolume; //!< Particle shell inner sphere surface to volume ratio @@ -321,8 +370,8 @@ class GeneralRateModel2D : public UnitOperationBase bool _factorizeJacobian; //!< Determines whether the Jacobian needs to be factorized double* _tempState; //!< Temporary storage with the size of the state vector or larger if binding models require it - linalg::Gmres _gmres; //!< GMRES algorithm for the Schur-complement in linearSolve() - double _schurSafety; //!< Safety factor for Schur-complement solution + linalg::Gmres _gmres; //!< GMRES algorithm for the Schur-complement in linearSolve() + double _schurSafety; //!< Safety factor for Schur-complement solution int _colParBoundaryOrder; //!< Order of the bulk-particle boundary discretization std::vector _initC; //!< Liquid bulk phase initial conditions @@ -331,7 +380,7 @@ class GeneralRateModel2D : public UnitOperationBase bool _singleRadiusInitCp; std::vector _initQ; //!< Solid phase initial conditions bool _singleRadiusInitQ; - std::vector _initState; //!< Initial conditions for state vector if given + std::vector _initState; //!< Initial conditions for state vector if given std::vector _initStateDot; //!< Initial conditions for time derivative BENCH_TIMER(_timerResidual) @@ -353,48 +402,136 @@ class GeneralRateModel2D : public UnitOperationBase class Indexer { public: - Indexer(const Discretization& disc) : _disc(disc) { } + Indexer(const Discretization& disc) : _disc(disc) + { + } // Strides - inline int strideColAxialCell() const CADET_NOEXCEPT { return static_cast(_disc.nComp) * static_cast(_disc.nRad); } - inline int strideColRadialCell() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideColComp() const CADET_NOEXCEPT { return 1; } + inline int strideColAxialCell() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp) * static_cast(_disc.nRad); + } + inline int strideColRadialCell() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideColComp() const CADET_NOEXCEPT + { + return 1; + } - inline int strideParComp() const CADET_NOEXCEPT { return 1; } - inline int strideParLiquid() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideParBound(int parType) const CADET_NOEXCEPT { return static_cast(_disc.strideBound[parType]); } - inline int strideParShell(int parType) const CADET_NOEXCEPT { return strideParLiquid() + strideParBound(parType); } - inline int strideParBlock(int parType) const CADET_NOEXCEPT { return static_cast(_disc.nParCell[parType]) * strideParShell(parType); } + inline int strideParComp() const CADET_NOEXCEPT + { + return 1; + } + inline int strideParLiquid() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideParBound(int parType) const CADET_NOEXCEPT + { + return static_cast(_disc.strideBound[parType]); + } + inline int strideParShell(int parType) const CADET_NOEXCEPT + { + return strideParLiquid() + strideParBound(parType); + } + inline int strideParBlock(int parType) const CADET_NOEXCEPT + { + return static_cast(_disc.nParCell[parType]) * strideParShell(parType); + } - inline int strideFluxCell() const CADET_NOEXCEPT { return static_cast(_disc.nComp) * static_cast(_disc.nParType); } - inline int strideFluxParType() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideFluxComp() const CADET_NOEXCEPT { return 1; } + inline int strideFluxCell() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp) * static_cast(_disc.nParType); + } + inline int strideFluxParType() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideFluxComp() const CADET_NOEXCEPT + { + return 1; + } // Offsets - inline int offsetC() const CADET_NOEXCEPT { return _disc.nComp * _disc.nRad; } - inline int offsetCp() const CADET_NOEXCEPT { return _disc.nComp * _disc.nCol * _disc.nRad + offsetC(); } - inline int offsetCp(ParticleTypeIndex pti) const CADET_NOEXCEPT { return offsetCp() + _disc.parTypeOffset[pti.value]; } - inline int offsetCp(ParticleTypeIndex pti, ParticleIndex pi) const CADET_NOEXCEPT { return offsetCp() + _disc.parTypeOffset[pti.value] + strideParBlock(pti.value) * pi.value; } - inline int offsetJf() const CADET_NOEXCEPT { return offsetCp() + _disc.parTypeOffset[_disc.nParType]; } - inline int offsetJf(ParticleTypeIndex pti) const CADET_NOEXCEPT { return offsetJf() + pti.value * _disc.nCol * _disc.nComp * _disc.nRad; } - inline int offsetBoundComp(ParticleTypeIndex pti, ComponentIndex comp) const CADET_NOEXCEPT { return _disc.boundOffset[pti.value * _disc.nComp + comp.value]; } + inline int offsetC() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nRad; + } + inline int offsetCp() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nCol * _disc.nRad + offsetC(); + } + inline int offsetCp(ParticleTypeIndex pti) const CADET_NOEXCEPT + { + return offsetCp() + _disc.parTypeOffset[pti.value]; + } + inline int offsetCp(ParticleTypeIndex pti, ParticleIndex pi) const CADET_NOEXCEPT + { + return offsetCp() + _disc.parTypeOffset[pti.value] + strideParBlock(pti.value) * pi.value; + } + inline int offsetJf() const CADET_NOEXCEPT + { + return offsetCp() + _disc.parTypeOffset[_disc.nParType]; + } + inline int offsetJf(ParticleTypeIndex pti) const CADET_NOEXCEPT + { + return offsetJf() + pti.value * _disc.nCol * _disc.nComp * _disc.nRad; + } + inline int offsetBoundComp(ParticleTypeIndex pti, ComponentIndex comp) const CADET_NOEXCEPT + { + return _disc.boundOffset[pti.value * _disc.nComp + comp.value]; + } // Return pointer to first element of state variable in state vector - template inline real_t* c(real_t* const data) const { return data + offsetC(); } - template inline real_t const* c(real_t const* const data) const { return data + offsetC(); } + template inline real_t* c(real_t* const data) const + { + return data + offsetC(); + } + template inline real_t const* c(real_t const* const data) const + { + return data + offsetC(); + } - template inline real_t* cp(real_t* const data) const { return data + offsetCp(); } - template inline real_t const* cp(real_t const* const data) const { return data + offsetCp(); } + template inline real_t* cp(real_t* const data) const + { + return data + offsetCp(); + } + template inline real_t const* cp(real_t const* const data) const + { + return data + offsetCp(); + } - template inline real_t* q(real_t* const data) const { return data + offsetCp() + strideParLiquid(); } - template inline real_t const* q(real_t const* const data) const { return data + offsetCp() + strideParLiquid(); } + template inline real_t* q(real_t* const data) const + { + return data + offsetCp() + strideParLiquid(); + } + template inline real_t const* q(real_t const* const data) const + { + return data + offsetCp() + strideParLiquid(); + } - template inline real_t* jf(real_t* const data) const { return data + offsetJf(); } - template inline real_t const* jf(real_t const* const data) const { return data + offsetJf(); } + template inline real_t* jf(real_t* const data) const + { + return data + offsetJf(); + } + template inline real_t const* jf(real_t const* const data) const + { + return data + offsetJf(); + } // Return specific variable in state vector - template inline real_t& c(real_t* const data, unsigned int col, unsigned int rad, unsigned int comp) const { return data[offsetC() + comp + col * strideColAxialCell() + rad * strideColRadialCell()]; } - template inline const real_t& c(real_t const* const data, unsigned int col, unsigned int rad, unsigned int comp) const { return data[offsetC() + comp + col * strideColAxialCell() + rad * strideColRadialCell()]; } + template + inline real_t& c(real_t* const data, unsigned int col, unsigned int rad, unsigned int comp) const + { + return data[offsetC() + comp + col * strideColAxialCell() + rad * strideColRadialCell()]; + } + template + inline const real_t& c(real_t const* const data, unsigned int col, unsigned int rad, unsigned int comp) const + { + return data[offsetC() + comp + col * strideColAxialCell() + rad * strideColRadialCell()]; + } protected: const Discretization& _disc; @@ -403,27 +540,77 @@ class GeneralRateModel2D : public UnitOperationBase class Exporter : public ISolutionExporter { public: - - Exporter(const Discretization& disc, const GeneralRateModel2D& model, double const* data) : _disc(disc), _idx(disc), _model(model), _data(data) { } + Exporter(const Discretization& disc, const GeneralRateModel2D& model, double const* data) + : _disc(disc), _idx(disc), _model(model), _data(data) + { + } Exporter(const Discretization&& disc, const GeneralRateModel2D& model, double const* data) = delete; - virtual bool hasParticleFlux() const CADET_NOEXCEPT { return true; } - virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT { return true; } - virtual bool hasSolidPhase() const CADET_NOEXCEPT { return _disc.strideBound[_disc.nParType] > 0; } - virtual bool hasVolume() const CADET_NOEXCEPT { return false; } - virtual bool isParticleLumped() const CADET_NOEXCEPT { return false; } - virtual bool hasPrimaryExtent() const CADET_NOEXCEPT { return true; } - - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } - virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT { return _disc.nCol; } - virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT { return _disc.nRad; } - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return _disc.nRad; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return _disc.nRad; } - virtual unsigned int numParticleTypes() const CADET_NOEXCEPT { return _disc.nParType; } - virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT { return _disc.nParCell[parType]; } - virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT { return _disc.strideBound[parType]; } - virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT { return _disc.nComp * _disc.nCol * _disc.nRad; } - virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return _disc.nCol * _disc.nRad * _disc.nParCell[parType] * _disc.nComp; } + virtual bool hasParticleFlux() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasSolidPhase() const CADET_NOEXCEPT + { + return _disc.strideBound[_disc.nParType] > 0; + } + virtual bool hasVolume() const CADET_NOEXCEPT + { + return false; + } + virtual bool isParticleLumped() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasPrimaryExtent() const CADET_NOEXCEPT + { + return true; + } + + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } + virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT + { + return _disc.nCol; + } + virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT + { + return _disc.nRad; + } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return _disc.nRad; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return _disc.nRad; + } + virtual unsigned int numParticleTypes() const CADET_NOEXCEPT + { + return _disc.nParType; + } + virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.nParCell[parType]; + } + virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.strideBound[parType]; + } + virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nCol * _disc.nRad; + } + virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.nCol * _disc.nRad * _disc.nParCell[parType] * _disc.nComp; + } virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT { unsigned int nDofPerParType = 0; @@ -431,7 +618,10 @@ class GeneralRateModel2D : public UnitOperationBase nDofPerParType += _disc.nParCell[i]; return _disc.nCol * _disc.nRad * _disc.nComp * nDofPerParType; } - virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return _disc.nCol * _disc.nRad * _disc.nParCell[parType] * _disc.strideBound[parType]; } + virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.nCol * _disc.nRad * _disc.nParCell[parType] * _disc.strideBound[parType]; + } virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT { unsigned int nDofPerParType = 0; @@ -439,8 +629,14 @@ class GeneralRateModel2D : public UnitOperationBase nDofPerParType += _disc.nParCell[i] * _disc.strideBound[i]; return _disc.nCol * _disc.nRad * nDofPerParType; } - virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT { return _disc.nComp * _disc.nCol * _disc.nRad * _disc.nParType; } - virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT { return 0; } + virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nCol * _disc.nRad * _disc.nParType; + } + virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT + { + return 0; + } virtual int writeMobilePhase(double* buffer) const; virtual int writeSolidPhase(double* buffer) const; @@ -449,7 +645,10 @@ class GeneralRateModel2D : public UnitOperationBase virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const; virtual int writeParticleFlux(double* buffer) const; virtual int writeParticleFlux(unsigned int parType, double* buffer) const; - virtual int writeVolume(double* buffer) const { return 0; } + virtual int writeVolume(double* buffer) const + { + return 0; + } virtual int writeInlet(unsigned int port, double* buffer) const; virtual int writeInlet(double* buffer) const; virtual int writeOutlet(unsigned int port, double* buffer) const; @@ -488,4 +687,4 @@ class GeneralRateModel2D : public UnitOperationBase } // namespace model } // namespace cadet -#endif // LIBCADET_GENERALRATEMODEL2D_HPP_ +#endif // LIBCADET_GENERALRATEMODEL2D_HPP_ diff --git a/src/libcadet/model/GeneralRateModelBuilder.cpp b/src/libcadet/model/GeneralRateModelBuilder.cpp index 6a85e7571..8421350a1 100644 --- a/src/libcadet/model/GeneralRateModelBuilder.cpp +++ b/src/libcadet/model/GeneralRateModelBuilder.cpp @@ -6,89 +6,93 @@ #include "LoggingUtils.hpp" #include "Logging.hpp" - namespace cadet { - namespace model - { +namespace model +{ - IUnitOperation* selectAxialFlowDiscretizationGRM(UnitOpIdx uoId, IParameterProvider& paramProvider) - { - IUnitOperation* model = nullptr; +IUnitOperation* selectAxialFlowDiscretizationGRM(UnitOpIdx uoId, IParameterProvider& paramProvider) +{ + IUnitOperation* model = nullptr; - paramProvider.pushScope("discretization"); + paramProvider.pushScope("discretization"); - if (paramProvider.exists("SPATIAL_METHOD")) { + if (paramProvider.exists("SPATIAL_METHOD")) + { - const std::string discName = paramProvider.getString("SPATIAL_METHOD"); + const std::string discName = paramProvider.getString("SPATIAL_METHOD"); #ifdef ENABLE_DG - if (discName == "DG") - model = new GeneralRateModelDG(uoId); - else if (discName == "FV") + if (discName == "DG") + model = new GeneralRateModelDG(uoId); + else if (discName == "FV") #else - if (discName == "FV") + if (discName == "FV") #endif - model = createAxialFVGRM(uoId); - else - { - LOG(Error) << "Unknown discretization type " << discName << " for unit " << uoId; - } - } - else { - model = createAxialFVGRM(uoId); - } - - paramProvider.popScope(); - - return model; + model = createAxialFVGRM(uoId); + else + { + LOG(Error) << "Unknown discretization type " << discName << " for unit " << uoId; } + } + else + { + model = createAxialFVGRM(uoId); + } - IUnitOperation* selectRadialFlowDiscretizationGRM(UnitOpIdx uoId, IParameterProvider& paramProvider) - { - IUnitOperation* model = nullptr; + paramProvider.popScope(); - paramProvider.pushScope("discretization"); + return model; +} - if (paramProvider.exists("SPATIAL_METHOD")) { +IUnitOperation* selectRadialFlowDiscretizationGRM(UnitOpIdx uoId, IParameterProvider& paramProvider) +{ + IUnitOperation* model = nullptr; - const std::string discName = paramProvider.getString("SPATIAL_METHOD"); + paramProvider.pushScope("discretization"); - if (discName == "DG") - { - LOG(Error) << "Radial flow not implemented for DG discretization yet, was called for unit " << uoId; - } - else if (discName == "FV") - model = createRadialFVGRM(uoId); - else - { - LOG(Error) << "Unknown discretization type " << discName << " for unit " << uoId; - } - } - else { - model = createRadialFVGRM(uoId); - } + if (paramProvider.exists("SPATIAL_METHOD")) + { - paramProvider.popScope(); + const std::string discName = paramProvider.getString("SPATIAL_METHOD"); - return model; + if (discName == "DG") + { + LOG(Error) << "Radial flow not implemented for DG discretization yet, was called for unit " << uoId; } - - void registerGeneralRateModel(std::unordered_map>& models) + else if (discName == "FV") + model = createRadialFVGRM(uoId); + else { - typedef GeneralRateModel AxialGRM; - typedef GeneralRateModel RadialGRM; + LOG(Error) << "Unknown discretization type " << discName << " for unit " << uoId; + } + } + else + { + model = createRadialFVGRM(uoId); + } + + paramProvider.popScope(); + + return model; +} + +void registerGeneralRateModel( + std::unordered_map>& models) +{ + typedef GeneralRateModel AxialGRM; + typedef GeneralRateModel RadialGRM; #ifdef ENABLE_DG - models[GeneralRateModelDG::identifier()] = selectAxialFlowDiscretizationGRM; - models["GRM_DG"] = selectAxialFlowDiscretizationGRM; + models[GeneralRateModelDG::identifier()] = selectAxialFlowDiscretizationGRM; + models["GRM_DG"] = selectAxialFlowDiscretizationGRM; #endif - models[AxialGRM::identifier()] = selectAxialFlowDiscretizationGRM; - models["GRM"] = selectAxialFlowDiscretizationGRM; + models[AxialGRM::identifier()] = selectAxialFlowDiscretizationGRM; + models["GRM"] = selectAxialFlowDiscretizationGRM; - models[RadialGRM::identifier()] = selectRadialFlowDiscretizationGRM; - models["RGRM"] = selectRadialFlowDiscretizationGRM; - } + models[RadialGRM::identifier()] = selectRadialFlowDiscretizationGRM; + models["RGRM"] = selectRadialFlowDiscretizationGRM; +} - } // namespace model +} // namespace model -} // namespace cadet \ No newline at end of file +} // namespace cadet diff --git a/src/libcadet/model/GeneralRateModelDG-InitialConditions.cpp b/src/libcadet/model/GeneralRateModelDG-InitialConditions.cpp index 5f14bfe19..32979b68c 100644 --- a/src/libcadet/model/GeneralRateModelDG-InitialConditions.cpp +++ b/src/libcadet/model/GeneralRateModelDG-InitialConditions.cpp @@ -27,7 +27,7 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include +#include #endif namespace cadet @@ -36,11 +36,14 @@ namespace cadet namespace model { -int GeneralRateModelDG::multiplexInitialConditions(const cadet::ParameterId& pId, unsigned int adDirection, double adValue) +int GeneralRateModelDG::multiplexInitialConditions(const cadet::ParameterId& pId, unsigned int adDirection, + double adValue) { if (_singleBinding) { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { _sensParams.insert(&_initCp[pId.component]); for (unsigned int t = 0; t < _disc.nParType; ++t) @@ -49,18 +52,23 @@ int GeneralRateModelDG::multiplexInitialConditions(const cadet::ParameterId& pId else if (pId.name == hashString("INIT_CP")) return -1; - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { _sensParams.insert(&_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState]); for (unsigned int t = 0; t < _disc.nParType; ++t) - _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState].setADValue(adDirection, adValue); + _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState] + .setADValue(adDirection, adValue); } else if (pId.name == hashString("INIT_Q")) return -1; } else { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { _sensParams.insert(&_initCp[pId.particleType * _disc.nComp + pId.component]); _initCp[pId.particleType * _disc.nComp + pId.component].setADValue(adDirection, adValue); @@ -68,10 +76,16 @@ int GeneralRateModelDG::multiplexInitialConditions(const cadet::ParameterId& pId else if (pId.name == hashString("INIT_CP")) return -1; - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { - _sensParams.insert(&_initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState]); - _initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState].setADValue(adDirection, adValue); + _sensParams.insert( + &_initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState]); + _initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState] + .setADValue(adDirection, adValue); } else if (pId.name == hashString("INIT_Q")) return -1; @@ -83,7 +97,9 @@ int GeneralRateModelDG::multiplexInitialConditions(const cadet::ParameterId& pId { if (_singleBinding) { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { if (checkSens && !contains(_sensParams, &_initCp[pId.component])) return -1; @@ -94,20 +110,27 @@ int GeneralRateModelDG::multiplexInitialConditions(const cadet::ParameterId& pId else if (pId.name == hashString("INIT_CP")) return -1; - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { - if (checkSens && !contains(_sensParams, &_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState])) + if (checkSens && + !contains(_sensParams, + &_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState])) return -1; for (unsigned int t = 0; t < _disc.nParType; ++t) - _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState].setValue(val); + _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState] + .setValue(val); } else if (pId.name == hashString("INIT_Q")) return -1; } else { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { if (checkSens && !contains(_sensParams, &_initCp[pId.particleType * _disc.nComp + pId.component])) return -1; @@ -117,12 +140,19 @@ int GeneralRateModelDG::multiplexInitialConditions(const cadet::ParameterId& pId else if (pId.name == hashString("INIT_CP")) return -1; - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { - if (checkSens && !contains(_sensParams, &_initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState])) + if (checkSens && + !contains(_sensParams, + &_initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState])) return -1; - _initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState].setValue(val); + _initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState] + .setValue(val); } else if (pId.name == hashString("INIT_Q")) return -1; @@ -143,7 +173,8 @@ void GeneralRateModelDG::applyInitialCondition(const SimulationState& simState) if (!_initStateDot.empty()) { std::fill(simState.vecStateYdot, simState.vecStateYdot + idxr.offsetC(), 0.0); - std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), simState.vecStateYdot + idxr.offsetC()); + std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), + simState.vecStateYdot + idxr.offsetC()); } else std::fill(simState.vecStateYdot, simState.vecStateYdot + numDofs(), 0.0); @@ -166,7 +197,7 @@ void GeneralRateModelDG::applyInitialCondition(const SimulationState& simState) { for (unsigned int point = 0; point < _disc.nPoints; ++point) { - const unsigned int offset = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ point }); + const unsigned int offset = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{point}); // Loop over particle nodes for (unsigned int shell = 0; shell < _disc.nParPoints[type]; ++shell) @@ -199,7 +230,8 @@ void GeneralRateModelDG::readInitialCondition(IParameterProvider& paramProvider) // Check if INIT_STATE contains the full state and its time derivative if (initState.size() >= 2 * numPureDofs()) - _initStateDot = std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); + _initStateDot = + std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); return; } @@ -215,7 +247,8 @@ void GeneralRateModelDG::readInitialCondition(IParameterProvider& paramProvider) { const std::vector initCp = paramProvider.getDoubleArray("INIT_CP"); - if (((initCp.size() < _disc.nComp) && _singleBinding) || ((initCp.size() < _disc.nComp * _disc.nParType) && !_singleBinding)) + if (((initCp.size() < _disc.nComp) && _singleBinding) || + ((initCp.size() < _disc.nComp * _disc.nParType) && !_singleBinding)) throw InvalidParameterException("INIT_CP does not contain enough values for all components"); if (!_singleBinding) @@ -239,7 +272,9 @@ void GeneralRateModelDG::readInitialCondition(IParameterProvider& paramProvider) if (initQ.empty() || (_disc.strideBound[_disc.nParType] == 0)) return; - if ((_disc.strideBound[_disc.nParType] > 0) && (((initQ.size() < _disc.strideBound[_disc.nParType]) && !_singleBinding) || ((initQ.size() < _disc.strideBound[0]) && _singleBinding))) + if ((_disc.strideBound[_disc.nParType] > 0) && + (((initQ.size() < _disc.strideBound[_disc.nParType]) && !_singleBinding) || + ((initQ.size() < _disc.strideBound[0]) && _singleBinding))) throw InvalidParameterException("INIT_Q does not contain enough values for all bound states"); if (!_singleBinding) @@ -297,17 +332,24 @@ void GeneralRateModelDG::readInitialCondition(IParameterProvider& paramProvider) * @param [in] errorTol Error tolerance for algebraic equations * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ -void GeneralRateModelDG::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModelDG::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); Indexer idxr(_disc); - // initialization for inexact integration DG discretization of particle mass balance not supported. This would require the consideration of the additional algebraic constraints, - // but the general performance (stiffness due to the additional algebraic constraints) of this scheme does not justify the effort here. - for (unsigned int type = 0; type < _disc.nParType; type++) { - if (_disc.parExactInt[type] == false) { - LOG(Error) << "No consistent initialization for inexact integration DG discretization in particles (cf. par_exact_integration). If consistent initialization is required, change to exact integration."; + // initialization for inexact integration DG discretization of particle mass balance not supported. This would + // require the consideration of the additional algebraic constraints, but the general performance (stiffness due to + // the additional algebraic constraints) of this scheme does not justify the effort here. + for (unsigned int type = 0; type < _disc.nParType; type++) + { + if (_disc.parExactInt[type] == false) + { + LOG(Error) + << "No consistent initialization for inexact integration DG discretization in particles (cf. " + "par_exact_integration). If consistent initialization is required, change to exact integration."; return; } } @@ -343,22 +385,27 @@ void GeneralRateModelDG::consistentInitialState(const SimulationTime& simTime, d bndStartIdx += _disc.nBound[_disc.nComp * type + comp]; } - const linalg::ConstMaskArray mask{ qsMask.data(), static_cast(_disc.nComp + _disc.strideBound[type]) }; + const linalg::ConstMaskArray mask{qsMask.data(), static_cast(_disc.nComp + _disc.strideBound[type])}; const int probSize = linalg::numMaskActive(mask); - #ifdef CADET_PARALLELIZE - BENCH_SCOPE(_timerConsistentInitPar); +#ifdef CADET_PARALLELIZE + BENCH_SCOPE(_timerConsistentInitPar); tbb::parallel_for(std::size_t(0), static_cast(_disc.nPoints), [&](std::size_t pblk) - #else +#else for (unsigned int pblk = 0; pblk < _disc.nPoints; ++pblk) - #endif +#endif { LinearBufferAllocator tlmAlloc = threadLocalMem.get(); // Reuse memory of sparse matrix for dense matrix - linalg::DenseMatrixView fullJacobianMatrix(_globalJacDisc.valuePtr() + _globalJacDisc.outerIndexPtr()[idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ static_cast(pblk) })], nullptr, mask.len, mask.len); - - // z coordinate (column length normed to 1) of current node - needed in externally dependent adsorption kinetic + linalg::DenseMatrixView fullJacobianMatrix( + _globalJacDisc.valuePtr() + + _globalJacDisc.outerIndexPtr()[idxr.offsetCp(ParticleTypeIndex{type}, + ParticleIndex{static_cast(pblk)})], + nullptr, mask.len, mask.len); + + // z coordinate (column length normed to 1) of current node - needed in externally dependent adsorption + // kinetic const double z = _convDispOp.relativeCoordinate(pblk); // Get workspace memory @@ -383,24 +430,31 @@ void GeneralRateModelDG::consistentInitialState(const SimulationTime& simTime, d linalg::DenseMatrixView jacobianMatrix(jacobianMem, _globalJacDisc.outerIndexPtr(), probSize, probSize); const parts::cell::CellParameters cellResParams = makeCellResidualParams(type, mask.mask + _disc.nComp); - // This loop cannot be run in parallel without creating a Jacobian matrix for each thread which would increase memory usage - const int localOffsetToParticle = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ static_cast(pblk) }); + // This loop cannot be run in parallel without creating a Jacobian matrix for each thread which would + // increase memory usage + const int localOffsetToParticle = + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); for (unsigned int node = 0; node < _disc.nParPoints[type]; ++node) { const int localOffsetInParticle = static_cast(node) * idxr.strideParNode(type); // Get pointer to q variables in a shell of particle pblk - double* const qShell = vecStateY + localOffsetToParticle + localOffsetInParticle + idxr.strideParLiquid(); - active* const localAdRes = adJac.adRes ? adJac.adRes + localOffsetToParticle + localOffsetInParticle : nullptr; - active* const localAdY = adJac.adY ? adJac.adY + localOffsetToParticle + localOffsetInParticle : nullptr; + double* const qShell = + vecStateY + localOffsetToParticle + localOffsetInParticle + idxr.strideParLiquid(); + active* const localAdRes = + adJac.adRes ? adJac.adRes + localOffsetToParticle + localOffsetInParticle : nullptr; + active* const localAdY = + adJac.adY ? adJac.adY + localOffsetToParticle + localOffsetInParticle : nullptr; // r (particle) coordinate of current node - const double r = static_cast(_disc.deltaR[type]) * std::floor(node / _disc.nParNode[type]) - + 0.5 * static_cast(_disc.deltaR[type]) * (1 + _disc.parNodes[type][node % _disc.nParNode[type]]); - const ColumnPosition colPos{ z, 0.0, r }; + const double r = static_cast(_disc.deltaR[type]) * std::floor(node / _disc.nParNode[type]) + + 0.5 * static_cast(_disc.deltaR[type]) * + (1 + _disc.parNodes[type][node % _disc.nParNode[type]]); + const ColumnPosition colPos{z, 0.0, r}; // Determine whether nonlinear solver is required - if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - idxr.strideParLiquid(), tlmAlloc)) + if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - idxr.strideParLiquid(), tlmAlloc)) continue; // Extract initial values from current state @@ -408,16 +462,17 @@ void GeneralRateModelDG::consistentInitialState(const SimulationTime& simTime, d // Save values of conserved moieties const double epsQ = 1.0 - static_cast(_parPorosity[type]); - linalg::conservedMoietiesFromPartitionedMask(mask, _disc.nBound + type * _disc.nComp, _disc.nComp, qShell - _disc.nComp, conservedQuants, static_cast(_parPorosity[type]), epsQ); + linalg::conservedMoietiesFromPartitionedMask(mask, _disc.nBound + type * _disc.nComp, _disc.nComp, + qShell - _disc.nComp, conservedQuants, + static_cast(_parPorosity[type]), epsQ); std::function jacFunc; if (localAdY && localAdRes) { - jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) - { - // Copy over state vector to AD state vector (without changing directional values to keep seed vectors) - // and initialize residuals with zero (also resetting directional values) + jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) { + // Copy over state vector to AD state vector (without changing directional values to keep seed + // vectors) and initialize residuals with zero (also resetting directional values) ad::copyToAd(qShell - _disc.nComp, localAdY, mask.len); // @todo Check if this is necessary ad::resetAd(localAdRes, mask.len); @@ -426,26 +481,31 @@ void GeneralRateModelDG::consistentInitialState(const SimulationTime& simTime, d linalg::applyVectorSubset(x, mask, localAdY); // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, localAdY, nullptr, localAdRes, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); -// todo check Jacobian with AD -//#ifdef CADET_CHECK_ANALYTIC_JACOBIAN -// std::copy_n(qShell - _disc.nComp, mask.len, fullX); -// linalg::applyVectorSubset(x, mask, fullX); -// -// // Compute analytic Jacobian -// parts::cell::residualKernel( -// simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc -// ); -// -// // Compare -// const double diff = ad::compareDenseJacobianWithBandedAd( -// localAdRes - localOffsetInParticle, localOffsetInParticle, adJac.adDirOffset, _jacP[type * _disc.nPoints].lowerBandwidth(), -// _jacP[type * _disc.nPoints].lowerBandwidth(), _jacP[type * _disc.nPoints].upperBandwidth(), fullJacobianMatrix -// ); -// LOG(Debug) << "MaxDiff: " << diff; -//#endif + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, localAdY, nullptr, localAdRes, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); + // todo check Jacobian with AD + // #ifdef CADET_CHECK_ANALYTIC_JACOBIAN + // std::copy_n(qShell - _disc.nComp, mask.len, fullX); + // linalg::applyVectorSubset(x, mask, fullX); + // + // // Compute analytic Jacobian + // parts::cell::residualKernel( + // simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + // cellResParams, tlmAlloc + // ); + // + // // Compare + // const double diff = ad::compareDenseJacobianWithBandedAd( + // localAdRes - localOffsetInParticle, localOffsetInParticle, + // adJac.adDirOffset, _jacP[type * _disc.nPoints].lowerBandwidth(), + // _jacP[type * _disc.nPoints].lowerBandwidth(), _jacP[type * _disc.nPoints].upperBandwidth(), + // fullJacobianMatrix + // ); + // LOG(Debug) << "MaxDiff: " << diff; + // #endif // Extract Jacobian from AD // Read particle Jacobian entries from dedicated AD directions @@ -456,13 +516,14 @@ void GeneralRateModelDG::consistentInitialState(const SimulationTime& simTime, d { for (unsigned int par = 0; par < _disc.nPoints; par++) { - const int eqOffset_res = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ par }); - const int eqOffset_mat = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ par }); + const int eqOffset_res = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); + const int eqOffset_mat = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); for (unsigned int phase = 0; phase < idxr.strideParBlock(type); phase++) { for (unsigned int phaseTo = 0; phaseTo < idxr.strideParBlock(type); phaseTo++) { - _globalJac.coeffRef(eqOffset_mat + phase, eqOffset_mat + phaseTo) = adRes[eqOffset_res + phase].getADValue(offsetParticleTypeDirs + phaseTo); + _globalJac.coeffRef(eqOffset_mat + phase, eqOffset_mat + phaseTo) = + adRes[eqOffset_res + phase].getADValue(offsetParticleTypeDirs + phaseTo); } } } @@ -506,16 +567,16 @@ void GeneralRateModelDG::consistentInitialState(const SimulationTime& simTime, d } else { - jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) - { + jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) { // Prepare input vector by overwriting masked items std::copy_n(qShell - _disc.nComp, mask.len, fullX); linalg::applyVectorSubset(x, mask, fullX); // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); // Extract Jacobian from full Jacobian mat.setAll(0.0); @@ -555,16 +616,16 @@ void GeneralRateModelDG::consistentInitialState(const SimulationTime& simTime, d // Apply nonlinear solver _nonlinearSolver->solve( - [&](double const* const x, double* const r) - { + [&](double const* const x, double* const r) { // Prepare input vector by overwriting masked items std::copy_n(qShell - _disc.nComp, mask.len, fullX); linalg::applyVectorSubset(x, mask, fullX); // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); // Extract values from residual linalg::selectVectorSubset(fullResidual, mask, r); @@ -604,7 +665,8 @@ void GeneralRateModelDG::consistentInitialState(const SimulationTime& simTime, d linalg::applyVectorSubset(solution, mask, qShell - idxr.strideParLiquid()); // Refine / correct solution - _binding[type]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - idxr.strideParLiquid(), tlmAlloc); + _binding[type]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - idxr.strideParLiquid(), tlmAlloc); } // reset jacobian pattern @@ -657,9 +719,12 @@ void GeneralRateModelDG::consistentInitialState(const SimulationTime& simTime, d * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] vecStateY Consistently initialized state vector - * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent state time derivatives. + * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent + * state time derivatives. */ -void GeneralRateModelDG::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModelDG::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -684,7 +749,7 @@ void GeneralRateModelDG::consistentInitialTimeDerivative(const SimulationTime& s for (unsigned int blk = 0; blk < _disc.nPoints * _disc.nComp; blk++, ++jacBlk) jacBlk[0] = 1.0; - // Process the particle blocks + // Process the particle blocks #ifdef CADET_PARALLELIZE BENCH_START(_timerConsistentInitPar); tbb::parallel_for(std::size_t(0), static_cast(_disc.nPoints * _disc.nParType), [&](std::size_t pblk) @@ -699,10 +764,11 @@ void GeneralRateModelDG::consistentInitialTimeDerivative(const SimulationTime& s const double z = _convDispOp.relativeCoordinate(pblk); // Assemble - linalg::BandedEigenSparseRowIterator jacPar(_globalJacDisc, idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ par })); + linalg::BandedEigenSparseRowIterator jacPar(_globalJacDisc, + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par})); LinearBufferAllocator tlmAlloc = threadLocalMem.get(); - double* const dFluxDt = _tempState + idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ par }); + double* const dFluxDt = _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); for (unsigned int j = 0; j < _disc.nParPoints[type]; ++j) { @@ -712,23 +778,29 @@ void GeneralRateModelDG::consistentInitialTimeDerivative(const SimulationTime& s continue; // Get iterators to beginning of solid phase - linalg::BandedEigenSparseRowIterator jacSolidOrig(_globalJac, idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ par }) + j * static_cast(idxr.strideParNode(type)) + static_cast(idxr.strideParLiquid())); + linalg::BandedEigenSparseRowIterator jacSolidOrig( + _globalJac, idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}) + + j * static_cast(idxr.strideParNode(type)) + + static_cast(idxr.strideParLiquid())); linalg::BandedEigenSparseRowIterator jacSolid = jacPar - idxr.strideParBound(type); int const* const mask = _binding[type]->reactionQuasiStationarity(); - double* const qShellDot = vecStateYdot + idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ par }) + static_cast(j) * idxr.strideParNode(type) + idxr.strideParLiquid(); + double* const qShellDot = vecStateYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}) + + static_cast(j) * idxr.strideParNode(type) + idxr.strideParLiquid(); // Obtain derivative of fluxes wrt. time std::fill_n(dFluxDt, _disc.strideBound[type], 0.0); if (_binding[type]->dependsOnTime()) { - // r (particle) coordinate of current node (particle radius normed to 1) - needed in externally dependent adsorption kinetic - const double r = (static_cast(_disc.deltaR[type]) * std::floor(j / _disc.nParNode[type]) - + 0.5 * static_cast(_disc.deltaR[type]) * (1 + _disc.parNodes[type][j % _disc.nParNode[type]])) - / (static_cast(_parRadius[type]) - static_cast(_parCoreRadius[type])); + // r (particle) coordinate of current node (particle radius normed to 1) - needed in externally + // dependent adsorption kinetic + const double r = (static_cast(_disc.deltaR[type]) * std::floor(j / _disc.nParNode[type]) + + 0.5 * static_cast(_disc.deltaR[type]) * + (1 + _disc.parNodes[type][j % _disc.nParNode[type]])) / + (static_cast(_parRadius[type]) - static_cast(_parCoreRadius[type])); _binding[type]->timeDerivativeQuasiStationaryFluxes(simTime.t, simTime.secIdx, - ColumnPosition{ z, 0.0, r }, - qShellDot - _disc.nComp, qShellDot, dFluxDt, tlmAlloc); + ColumnPosition{z, 0.0, r}, qShellDot - _disc.nComp, + qShellDot, dFluxDt, tlmAlloc); } // Copy row from original Jacobian (without time derivatives) and set right hand side @@ -760,11 +832,8 @@ void GeneralRateModelDG::consistentInitialTimeDerivative(const SimulationTime& s { LOG(Error) << "Solve() failed"; } - } - - /** * @brief Computes approximately / partially consistent initial values (state variables without their time derivatives) * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have @@ -806,10 +875,13 @@ void GeneralRateModelDG::consistentInitialTimeDerivative(const SimulationTime& s * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) * @param [in] errorTol Error tolerance for algebraic equations */ -void GeneralRateModelDG::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModelDG::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) { if (isSectionDependent(_parDiffusionMode) || isSectionDependent(_parSurfDiffusionMode)) - LOG(Warning) << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; + LOG(Warning) + << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; BENCH_SCOPE(_timerConsistentInit); @@ -831,28 +903,34 @@ void GeneralRateModelDG::leanConsistentInitialState(const SimulationTime& simTim { LinearBufferAllocator tlmAlloc = threadLocalMem.get(); - // z coordinate (column length normed to 1) of current node - needed in externally dependent adsorption kinetic + // z coordinate (column length normed to 1) of current node - needed in externally dependent adsorption + // kinetic const double z = _convDispOp.relativeCoordinate(pblk); - const int localOffsetToParticle = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ static_cast(pblk) }); + const int localOffsetToParticle = + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); for (std::size_t shell = 0; shell < static_cast(_disc.nParPoints[type]); ++shell) { // Get pointer to q variables in a shell of particle pblk - const int localOffsetInParticle = static_cast(shell) * idxr.strideParNode(type) + idxr.strideParLiquid(); + const int localOffsetInParticle = + static_cast(shell) * idxr.strideParNode(type) + idxr.strideParLiquid(); double* const qShell = vecStateY + localOffsetToParticle + localOffsetInParticle; // r (particle) coordinate of current node - const double r = static_cast(_disc.deltaR[type]) * std::floor(shell / _disc.nParNode[type]) - + 0.5 * static_cast(_disc.deltaR[type]) * (1 + _disc.parNodes[type][shell % _disc.nParNode[type]]); - const ColumnPosition colPos{ z, 0.0, r}; - - // Perform consistent initialization that does not require a full fledged nonlinear solver (that may fail or damage the current state vector) - if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - idxr.strideParLiquid(), tlmAlloc)) + const double r = + static_cast(_disc.deltaR[type]) * std::floor(shell / _disc.nParNode[type]) + + 0.5 * static_cast(_disc.deltaR[type]) * + (1 + _disc.parNodes[type][shell % _disc.nParNode[type]]); + const ColumnPosition colPos{z, 0.0, r}; + + // Perform consistent initialization that does not require a full fledged nonlinear solver (that may + // fail or damage the current state vector) + if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - idxr.strideParLiquid(), tlmAlloc)) continue; } } CADET_PARFOR_END; } } - } /** @@ -893,13 +971,18 @@ void GeneralRateModelDG::leanConsistentInitialState(const SimulationTime& simTim * * @param [in] t Current time point * @param [in] vecStateY (Lean) consistently initialized state vector - * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time derivatives. - * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during execution of the function. + * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time + * derivatives. + * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during + * execution of the function. */ -void GeneralRateModelDG::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModelDG::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem) { if (isSectionDependent(_parDiffusionMode) || isSectionDependent(_parSurfDiffusionMode)) - LOG(Warning) << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; + LOG(Warning) + << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; BENCH_SCOPE(_timerConsistentInit); @@ -914,7 +997,7 @@ void GeneralRateModelDG::leanConsistentInitialTimeDerivative(double t, double co double* const resSlice = res + idxr.offsetC(); // Handle bulk block - solveBulkTimeDerivativeSystem(SimulationTime{ t, 0u }, resSlice); + solveBulkTimeDerivativeSystem(SimulationTime{t, 0u}, resSlice); // Note that we have solved with the *positive* residual as right hand side // instead of the *negative* one. Fortunately, we are dealing with linear systems, @@ -922,7 +1005,6 @@ void GeneralRateModelDG::leanConsistentInitialTimeDerivative(double t, double co double* const yDotSlice = vecStateYdot + idxr.offsetC(); for (unsigned int i = 0; i < _disc.nPoints * _disc.nComp; ++i) yDotSlice[i] = -resSlice[i]; - } void GeneralRateModelDG::initializeSensitivityStates(const std::vector& vecSensY) const @@ -945,7 +1027,7 @@ void GeneralRateModelDG::initializeSensitivityStates(const std::vector& { for (unsigned int point = 0; point < _disc.nPoints; ++point) { - const unsigned int offset = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ point }); + const unsigned int offset = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{point}); // Loop over particle cells for (unsigned int shell = 0; shell < _disc.nParPoints[type]; ++shell) @@ -971,24 +1053,25 @@ void GeneralRateModelDG::initializeSensitivityStates(const std::vector& * @brief Computes consistent initial values and time derivatives of sensitivity subsystems * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, * the sensitivity system for a parameter @f$ p @f$ reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of this linear DAE, @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ - * have to be consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the time - * derivative \f$ \dot{s}_0 \f$ such that they are consistent. + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of this linear DAE, @f$ s_0 = + * \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ have to be + * consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the + * time derivative \f$ \dot{s}_0 \f$ such that they are consistent. * - * The process follows closely the one of consistentInitialConditions() and, in fact, is a linearized version of it. - * This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ are - * related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them - * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p}. @f$ - *
                    - *
                  1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria). - * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG). Let @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at this point, we have - * \f[ \left( \frac{\partial F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, y_0, \dot{y}_0) \right)_{\mathcal{I}_a} = 0. \f]
                  2. - *
                  3. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. - * However, because of the algebraic equations, we need additional conditions to fully determine + * The process follows closely the one of consistentInitialConditions() and, in fact, is a linearized version + * of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ + * are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them + * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial + * \dot{y}_0}{\partial p}. @f$
                    1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, + * reaction equilibria). No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG). Let + * @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at this point, we have \f[ \left( \frac{\partial + * F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, y_0, \dot{y}_0) \right)_{\mathcal{I}_a} = 0. + * \f]
                    2. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations + * hold. However, because of the algebraic equations, we need additional conditions to fully determine * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the - * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting system - * has a similar structure as the system Jacobian. + * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting + * system has a similar structure as the system Jacobian. * @f[ \begin{align} * \left[\begin{array}{c|ccc|c} * \dot{J}_0 & & & & \\ @@ -1004,8 +1087,9 @@ void GeneralRateModelDG::initializeSensitivityStates(const std::vector& * @f$ J_{i,f} @f$ matrices in the right column are missing. * * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. - * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] - * which is 0 for algebraic equations (@f$ -\frac{\partial^2 F}{\partial t \partial p}@f$, to be more precise). + * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - + * \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] which is 0 for algebraic equations (@f$ -\frac{\partial^2 + * F}{\partial t \partial p}@f$, to be more precise). * * The linear system is solved by backsubstitution. First, the diagonal blocks are solved in parallel. * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                    3. @@ -1018,8 +1102,11 @@ void GeneralRateModelDG::initializeSensitivityStates(const std::vector& * @param [in] adRes Pointer to residual vector of AD datatypes with parameter sensitivities * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ -void GeneralRateModelDG::consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModelDG::consistentInitialSensitivity(const SimulationTime& simTime, + const ConstSimulationState& simState, + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -1043,7 +1130,7 @@ void GeneralRateModelDG::consistentInitialSensitivity(const SimulationTime& simT continue; int const* const qsMask = _binding[type]->reactionQuasiStationarity(); - const linalg::ConstMaskArray mask{ qsMask, static_cast(_disc.strideBound[type]) }; + const linalg::ConstMaskArray mask{qsMask, static_cast(_disc.strideBound[type])}; const int probSize = linalg::numMaskActive(mask); #ifdef CADET_PARALLELIZE @@ -1054,7 +1141,13 @@ void GeneralRateModelDG::consistentInitialSensitivity(const SimulationTime& simT #endif { // Reuse memory of band matrix for dense matrix - linalg::DenseMatrixView jacobianMatrix(_globalJacDisc.valuePtr() + _globalJacDisc.outerIndexPtr()[idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ static_cast(pblk) }) - idxr.offsetC()] + pblk * probSize * probSize, nullptr, probSize, probSize); + linalg::DenseMatrixView jacobianMatrix( + _globalJacDisc.valuePtr() + + _globalJacDisc.outerIndexPtr()[idxr.offsetCp(ParticleTypeIndex{type}, + ParticleIndex{static_cast(pblk)}) - + idxr.offsetC()] + + pblk * probSize * probSize, + nullptr, probSize, probSize); // Get workspace memory LinearBufferAllocator tlmAlloc = threadLocalMem.get(); @@ -1065,14 +1158,20 @@ void GeneralRateModelDG::consistentInitialSensitivity(const SimulationTime& simT BufferedArray rhsUnmaskedBuffer = tlmAlloc.array(idxr.strideParBound(type)); double* const rhsUnmasked = static_cast(rhsUnmaskedBuffer); - double* const maskedMultiplier = _tempState + idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ static_cast(pblk) }); - double* const scaleFactors = _tempState + idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ static_cast(pblk) }); + double* const maskedMultiplier = + _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); + double* const scaleFactors = + _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); for (unsigned int shell = 0; shell < _disc.nParPoints[type]; ++shell) { - const int jacRowOffset = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ static_cast(pblk) }) + static_cast(shell) * idxr.strideParNode(type) + _disc.nComp; + const int jacRowOffset = + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}) + + static_cast(shell) * idxr.strideParNode(type) + _disc.nComp; const int jacColOffset = jacRowOffset; - const int localQOffset = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ static_cast(pblk) }) + static_cast(shell) * idxr.strideParNode(type) + idxr.strideParLiquid(); + const int localQOffset = + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}) + + static_cast(shell) * idxr.strideParNode(type) + idxr.strideParLiquid(); // Extract subproblem Jacobian from full Jacobian jacobianMatrix.setAll(0.0); @@ -1082,13 +1181,16 @@ void GeneralRateModelDG::consistentInitialSensitivity(const SimulationTime& simT linalg::selectVectorSubset(sensYdot + localQOffset, mask, rhs); // Zero out masked elements - std::copy_n(sensY + localQOffset - idxr.strideParLiquid(), idxr.strideParNode(type), maskedMultiplier); + std::copy_n(sensY + localQOffset - idxr.strideParLiquid(), idxr.strideParNode(type), + maskedMultiplier); linalg::fillVectorSubset(maskedMultiplier + _disc.nComp, mask, 0.0); // Assemble right hand side Eigen::Map maskedMultiplier_eigen(maskedMultiplier, idxr.strideParBlock(type)); Eigen::Map rhsUnmasked_eigen(rhsUnmasked, idxr.strideParBlock(type)); - rhsUnmasked_eigen = _globalJac.block(jacRowOffset, jacColOffset, idxr.strideParBlock(type), idxr.strideParBlock(type)) * maskedMultiplier_eigen; + rhsUnmasked_eigen = _globalJac.block(jacRowOffset, jacColOffset, idxr.strideParBlock(type), + idxr.strideParBlock(type)) * + maskedMultiplier_eigen; linalg::vectorSubsetAdd(rhsUnmasked, mask, -1.0, 1.0, rhs); // Precondition @@ -1116,7 +1218,8 @@ void GeneralRateModelDG::consistentInitialSensitivity(const SimulationTime& simT // Assemble bulk block double* vPtr = _globalJacDisc.valuePtr(); - for (int k = 0; k < _globalJacDisc.nonZeros(); k++) { + for (int k = 0; k < _globalJacDisc.nonZeros(); k++) + { vPtr[k] = 0.0; } _convDispOp.addTimeDerivativeToJacobian(1.0, _globalJacDisc, idxr.offsetC()); @@ -1133,7 +1236,9 @@ void GeneralRateModelDG::consistentInitialSensitivity(const SimulationTime& simT const unsigned int par = pblk % _disc.nPoints; // Assemble - linalg::BandedEigenSparseRowIterator jacPar(_globalJacDisc, idxr.offsetCp(ParticleTypeIndex{ static_cast(type) }, ParticleIndex{ static_cast(pblk) })); + linalg::BandedEigenSparseRowIterator jacPar( + _globalJacDisc, idxr.offsetCp(ParticleTypeIndex{static_cast(type)}, + ParticleIndex{static_cast(pblk)})); for (unsigned int j = 0; j < _disc.nParPoints[type]; ++j) { @@ -1145,11 +1250,15 @@ void GeneralRateModelDG::consistentInitialSensitivity(const SimulationTime& simT if (_binding[type]->hasQuasiStationaryReactions()) { // Get iterators to beginning of solid phase - linalg::BandedEigenSparseRowIterator jacSolidOrig(_globalJac, idxr.offsetCp(ParticleTypeIndex{ static_cast(type) }, ParticleIndex{ static_cast(pblk) }) + j * idxr.strideParNode(type) + idxr.strideParLiquid()); + linalg::BandedEigenSparseRowIterator jacSolidOrig( + _globalJac, idxr.offsetCp(ParticleTypeIndex{static_cast(type)}, + ParticleIndex{static_cast(pblk)}) + + j * idxr.strideParNode(type) + idxr.strideParLiquid()); linalg::BandedEigenSparseRowIterator jacSolid = jacPar - idxr.strideParBound(type); int const* const mask = _binding[type]->reactionQuasiStationarity(); - double* const qShellDot = sensYdot + idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ par }) + static_cast(j) * idxr.strideParNode(type) + idxr.strideParLiquid(); + double* const qShellDot = sensYdot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}) + + static_cast(j) * idxr.strideParNode(type) + idxr.strideParLiquid(); // Copy row from original Jacobian and set right hand side for (int i = 0; i < idxr.strideParBound(type); ++i, ++jacSolid, ++jacSolidOrig) @@ -1189,11 +1298,11 @@ void GeneralRateModelDG::consistentInitialSensitivity(const SimulationTime& simT #ifdef CADET_PARALLELIZE BENCH_STOP(_timerConsistentInitPar); #endif - } } -void GeneralRateModelDG::solveBulkTimeDerivativeSystem(const SimulationTime& simTime, double* const rhs) { +void GeneralRateModelDG::solveBulkTimeDerivativeSystem(const SimulationTime& simTime, double* const rhs) +{ Indexer idxr(_disc); @@ -1206,8 +1315,10 @@ void GeneralRateModelDG::solveBulkTimeDerivativeSystem(const SimulationTime& sim const int gapCell = idxr.strideColNode() - static_cast(_disc.nComp) * idxr.strideColComp(); linalg::BandedEigenSparseRowIterator jac(_globalJacDisc, idxr.offsetC()); - for (unsigned int point = 0; point < _disc.nPoints; ++point, jac += gapCell) { - for (unsigned int comp = 0; comp < _disc.nComp; ++comp, ++jac) { + for (unsigned int point = 0; point < _disc.nPoints; ++point, jac += gapCell) + { + for (unsigned int comp = 0; comp < _disc.nComp; ++comp, ++jac) + { // dc_b / dt in transport equation jac[0] += alpha; } @@ -1217,15 +1328,17 @@ void GeneralRateModelDG::solveBulkTimeDerivativeSystem(const SimulationTime& sim _linearSolver->analyzePattern(_globalJacDisc.block(idxr.offsetC(), idxr.offsetC(), bulkRows, bulkRows)); _linearSolver->factorize(_globalJacDisc.block(idxr.offsetC(), idxr.offsetC(), bulkRows, bulkRows)); - if (_linearSolver->info() != Success) { + if (_linearSolver->info() != Success) + { LOG(Error) << "factorization failed in sensitivity initialization"; } Eigen::Map ret_vec(rhs, bulkRows); ret_vec = _linearSolver->solve(ret_vec); - // Use the factors to solve the linear system - if (_linearSolver->info() != Success) { + // Use the factors to solve the linear system + if (_linearSolver->info() != Success) + { LOG(Error) << "solve failed in sensitivity initialization"; } @@ -1237,20 +1350,20 @@ void GeneralRateModelDG::solveBulkTimeDerivativeSystem(const SimulationTime& sim * @brief Computes approximately / partially consistent initial values and time derivatives of sensitivity subsystems * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, * the sensitivity system for a parameter @f$ p @f$ reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of this linear DAE, @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ - * have to be consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the time - * derivative \f$ \dot{s}_0 \f$ such that they are consistent. + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of this linear DAE, @f$ s_0 = + * \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ have to be + * consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the + * time derivative \f$ \dot{s}_0 \f$ such that they are consistent. * - * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized version of it. - * This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ are - * related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them - * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p}. @f$ - *
                        - *
                      1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations). - * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                      2. - *
                      3. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. - * However, because of the algebraic equations, we need additional conditions to fully determine + * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized + * version of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ + * \dot{s}_0 \f$ are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by + * differentiating them with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = + * \frac{\partial \dot{y}_0}{\partial p}. @f$
                        1. Keep state and time derivative vectors as they are (i.e., do not + * solve algebraic equations). No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for + * DG).
                        2. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations + * hold. However, because of the algebraic equations, we need additional conditions to fully determine * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting * equations are stated below: @@ -1264,8 +1377,9 @@ void GeneralRateModelDG::solveBulkTimeDerivativeSystem(const SimulationTime& sim * where @f$ \dot{J}_0 @f$ denotes the bulk block Jacobian with respect to @f$ \dot{y}@f$. * * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. - * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] - * which is 0 for algebraic equations (@f$ -\frac{\partial^2 F}{\partial t \partial p}@f$, to be more precise). + * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - + * \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] which is 0 for algebraic equations (@f$ -\frac{\partial^2 + * F}{\partial t \partial p}@f$, to be more precise). * * The linear system is solved by backsubstitution. First, the bulk block is solved. * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                        3. @@ -1278,11 +1392,15 @@ void GeneralRateModelDG::solveBulkTimeDerivativeSystem(const SimulationTime& sim * @param [in] adRes Pointer to residual vector of AD datatypes with parameter sensitivities * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ -void GeneralRateModelDG::leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +void GeneralRateModelDG::leanConsistentInitialSensitivity(const SimulationTime& simTime, + const ConstSimulationState& simState, + std::vector& vecSensY, + std::vector& vecSensYdot, active const* const adRes, + util::ThreadLocalStorage& threadLocalMem) { if (isSectionDependent(_parDiffusionMode) || isSectionDependent(_parSurfDiffusionMode)) - LOG(Warning) << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; + LOG(Warning) + << "Lean consistent initialization is not appropriate for section-dependent pore and surface diffusion"; BENCH_SCOPE(_timerConsistentInit); @@ -1314,6 +1432,6 @@ void GeneralRateModelDG::leanConsistentInitialSensitivity(const SimulationTime& } } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/GeneralRateModelDG-LinearSolver.cpp b/src/libcadet/model/GeneralRateModelDG-LinearSolver.cpp index 7e0ff12e0..5a555cd99 100644 --- a/src/libcadet/model/GeneralRateModelDG-LinearSolver.cpp +++ b/src/libcadet/model/GeneralRateModelDG-LinearSolver.cpp @@ -24,11 +24,11 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include - #include +#include +#include - typedef tbb::flow::continue_node< tbb::flow::continue_msg > node_t; - typedef const tbb::flow::continue_msg & msg_t; +typedef tbb::flow::continue_node node_t; +typedef const tbb::flow::continue_msg& msg_t; #endif namespace cadet @@ -39,12 +39,16 @@ namespace model /** * @brief Computes the solution of the linear system involving the system Jacobian - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + b \f] * has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the - * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) \f$, - * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p rhs. + * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) + \f$, + * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p + rhs. * - * The full Jacobian @f$ J = \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) @f$ is given by + * The full Jacobian @f$ J = \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} + \right) @f$ is given by * @f[ \begin{align} J = \left[\begin{array}{c|ccc|c} @@ -85,13 +89,16 @@ namespace model * Note that @f$ J_f = I @f$ is the identity matrix and that the off-diagonal blocks @f$ J_{i,f} @f$ * and @f$ J_{f,i} @f$ for @f$ i = 0, \dots, N_{z} @f$ are sparse. * - * Exploiting the decomposition, the solution procedure @f$ x = J^{-1}b = \left( LU \right)^{-1}b = U^{-1} L^{-1} b @f$ + * Exploiting the decomposition, the solution procedure @f$ x = J^{-1}b = \left( LU \right)^{-1}b = U^{-1} + L^{-1} b @f$ * works as follows: * -# Factorize the diagonal blocks @f$ J_0, \dots, J_{N_z} @f$ - * -# Solve @f$ y = L^{-1} b @f$ by forward substitution. This is accomplished by first solving the diagonal + * -# Solve @f$ y = L^{-1} b @f$ by forward substitution. This is accomplished by first solving the + diagonal * blocks independently, that is, * @f[ y_i = J_{i}^{-1} b_i. @f] - * Then, calculate the flux-part @f$ y_f @f$ by substituting in the already calculated solutions @f$ y_i @f$: + * Then, calculate the flux-part @f$ y_f @f$ by substituting in the already calculated solutions @f$ y_i + @f$: * @f[ y_f = b_f - \sum_{i=0}^{N_z} J_{f,i} y_i. @f] * -# Solve the Schur-complement @f$ S x_f = y_f @f$ using an iterative method that only requires * matrix-vector products. The already inverted diagonal blocks @f$ J_i^{-1} @f$ come in handy here. @@ -104,11 +111,12 @@ namespace model * @param [in] outerTol Error tolerance for the solution of the linear system from outer Newton iteration * @param [in,out] rhs On entry the right hand side of the linear equation system, on exit the solution * @param [in] weight Vector with error weights - * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is evaluated + * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is + evaluated * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ -int GeneralRateModelDG::linearSolve(double t, double alpha, double outerTol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) +int GeneralRateModelDG::linearSolve(double t, double alpha, double outerTol, double* const rhs, + double const* const weight, const ConstSimulationState& simState) { BENCH_SCOPE(_timerLinearSolve); @@ -143,9 +151,12 @@ int GeneralRateModelDG::linearSolve(double t, double alpha, double outerTol, dou // Inlet at z = 0 for forward flow, at z = L for backward flow. unsigned int offInlet = _convDispOp.forwardFlow() ? 0 : (_disc.nCol - 1u) * idxr.strideColCell(); - for (int comp = 0; comp < _disc.nComp; comp++) { - for (int node = 0; node < (_disc.exactInt ? _disc.nNodes : 1); node++) { - r[idxr.offsetC() + offInlet + comp * idxr.strideColComp() + node * idxr.strideColNode()] += _jacInlet(node, 0) * r[comp]; + for (int comp = 0; comp < _disc.nComp; comp++) + { + for (int node = 0; node < (_disc.exactInt ? _disc.nNodes : 1); node++) + { + r[idxr.offsetC() + offInlet + comp * idxr.strideColComp() + node * idxr.strideColNode()] += + _jacInlet(node, 0) * r[comp]; } } @@ -165,47 +176,57 @@ int GeneralRateModelDG::linearSolve(double t, double alpha, double outerTol, dou /** * @brief Assembles bulk Jacobian @f$ J_i @f$ (@f$ i > 0 @f$) of the time-discretized equations - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The system Jacobian of the original equations, - * \f[ \frac{\partial F}{\partial y}, \f] - * is already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible - * for adding - * \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] - * to the system Jacobian, which yields the Jacobian of the time-discretized equations - * \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] - * when a BDF method is used. The time integrator needs to solve this equation for @f$ y_0 @f$, which requires - * the solution of the linear system mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + * b \f] has to be solved. The system Jacobian of the original equations, \f[ \frac{\partial F}{\partial y}, \f] is + * already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible for + * adding \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] to the system Jacobian, which yields the Jacobian of the + * time-discretized equations \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] when a BDF method is used. + * The time integrator needs to solve this equation for @f$ y_0 @f$, which requires the solution of the linear system + * mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). * * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) */ -void GeneralRateModelDG::assembleDiscretizedGlobalJacobian(double alpha, Indexer idxr) { +void GeneralRateModelDG::assembleDiscretizedGlobalJacobian(double alpha, Indexer idxr) +{ /* add static (per section) jacobian without inlet */ _globalJacDisc = _globalJac; // Add time derivatives to particle shells - for (unsigned int parType = 0; parType < _disc.nParType; parType++) { - for (unsigned int colNode = 0; colNode < _disc.nPoints; colNode++) { + for (unsigned int parType = 0; parType < _disc.nParType; parType++) + { + for (unsigned int colNode = 0; colNode < _disc.nPoints; colNode++) + { - linalg::BandedEigenSparseRowIterator jac(_globalJacDisc, idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode })); + linalg::BandedEigenSparseRowIterator jac(_globalJacDisc, + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode})); // If special case for inexact integration DG scheme: - // Do not add time derivative to particle mass balance equation at inner particle boundary for mass balance(s) - if (!_disc.parExactInt[parType] && _parGeomSurfToVol[parType] != _disc.SurfVolRatioSlab && _parCoreRadius[parType] == 0.0) { + // Do not add time derivative to particle mass balance equation at inner particle boundary for mass + // balance(s) + if (!_disc.parExactInt[parType] && _parGeomSurfToVol[parType] != _disc.SurfVolRatioSlab && + _parCoreRadius[parType] == 0.0) + { // we still need to add the derivative for the binding // move iterator to solid phase jac += idxr.strideParLiquid(); // Solid phase - for (unsigned int comp = 0; comp < _disc.nComp; ++comp) { + for (unsigned int comp = 0; comp < _disc.nComp; ++comp) + { for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; ++bnd, ++jac) { // Add derivative with respect to dynamic states to Jacobian - if (_binding[parType]->reactionQuasiStationarity()[idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd]) + if (_binding[parType]->reactionQuasiStationarity() + [idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + bnd]) continue; - // surface diffusion + kinetic binding -> additional DG-discretized mass balance equation for solid, for which the (inexact integration) discretization special case also holds - else if (_hasSurfaceDiffusion[parType] - && static_cast(getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], _disc.curSection)[_disc.nBoundBeforeType[parType] + getOffsetSurfDiff(parType, comp, bnd)]) != 0.0) + // surface diffusion + kinetic binding -> additional DG-discretized mass balance equation for + // solid, for which the (inexact integration) discretization special case also holds + else if (_hasSurfaceDiffusion[parType] && + static_cast(getSectionDependentSlice( + _parSurfDiffusion, _disc.strideBound[_disc.nParType], + _disc.curSection)[_disc.nBoundBeforeType[parType] + + getOffsetSurfDiff(parType, comp, bnd)]) != 0.0) continue; // Add derivative with respect to dq / dt to Jacobian @@ -213,7 +234,8 @@ void GeneralRateModelDG::assembleDiscretizedGlobalJacobian(double alpha, Indexer } } } - else { // else, treat boundary node "normally" + else + { // else, treat boundary node "normally" addTimeDerivativeToJacobianParticleShell(jac, idxr, alpha, parType); } @@ -229,7 +251,8 @@ void GeneralRateModelDG::assembleDiscretizedGlobalJacobian(double alpha, Indexer // add the remaining bulk time derivatives to global jacobian. linalg::BandedEigenSparseRowIterator jac(_globalJacDisc, idxr.offsetC()); - for (; jac.row() < idxr.offsetCp(); ++jac) { + for (; jac.row() < idxr.offsetCp(); ++jac) + { jac[0] += alpha; // main diagonal } } @@ -244,12 +267,16 @@ void GeneralRateModelDG::assembleDiscretizedGlobalJacobian(double alpha, Indexer * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) * @param [in] parType Index of the particle type */ -void GeneralRateModelDG::addTimeDerivativeToJacobianParticleShell(linalg::BandedEigenSparseRowIterator& jac, const Indexer& idxr, double alpha, unsigned int parType) +void GeneralRateModelDG::addTimeDerivativeToJacobianParticleShell(linalg::BandedEigenSparseRowIterator& jac, + const Indexer& idxr, double alpha, + unsigned int parType) { - parts::cell::addTimeDerivativeToJacobianParticleShell(jac, alpha, static_cast(_parPorosity[parType]), _disc.nComp, _disc.nBound + _disc.nComp * parType, - _poreAccessFactor.data() + _disc.nComp * parType, _disc.strideBound[parType], _disc.boundOffset + _disc.nComp * parType, _binding[parType]->reactionQuasiStationarity()); + parts::cell::addTimeDerivativeToJacobianParticleShell( + jac, alpha, static_cast(_parPorosity[parType]), _disc.nComp, _disc.nBound + _disc.nComp * parType, + _poreAccessFactor.data() + _disc.nComp * parType, _disc.strideBound[parType], + _disc.boundOffset + _disc.nComp * parType, _binding[parType]->reactionQuasiStationarity()); } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/GeneralRateModelDG.cpp b/src/libcadet/model/GeneralRateModelDG.cpp index 6506fbe6a..9401eabbc 100644 --- a/src/libcadet/model/GeneralRateModelDG.cpp +++ b/src/libcadet/model/GeneralRateModelDG.cpp @@ -40,7 +40,7 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include +#include #endif namespace cadet @@ -53,12 +53,10 @@ constexpr double SurfVolRatioSphere = 3.0; constexpr double SurfVolRatioCylinder = 2.0; constexpr double SurfVolRatioSlab = 1.0; - -GeneralRateModelDG::GeneralRateModelDG(UnitOpIdx unitOpIdx) : UnitOperationBase(unitOpIdx), - _hasSurfaceDiffusion(0, false), _dynReactionBulk(nullptr), - _globalJac(), _globalJacDisc(), _jacInlet(), _hasParDepSurfDiffusion(false), - _analyticJac(true), _jacobianAdDirs(0), _factorizeJacobian(false), _tempState(nullptr), - _initC(0), _initCp(0), _initQ(0), _initState(0), _initStateDot(0) +GeneralRateModelDG::GeneralRateModelDG(UnitOpIdx unitOpIdx) + : UnitOperationBase(unitOpIdx), _hasSurfaceDiffusion(0, false), _dynReactionBulk(nullptr), _globalJac(), + _globalJacDisc(), _jacInlet(), _hasParDepSurfDiffusion(false), _analyticJac(true), _jacobianAdDirs(0), + _factorizeJacobian(false), _tempState(nullptr), _initC(0), _initCp(0), _initQ(0), _initState(0), _initStateDot(0) { } @@ -118,10 +116,9 @@ unsigned int GeneralRateModelDG::numPureDofs() const CADET_NOEXCEPT // Column bulk DOFs: nPoints * nComp // Particle DOFs: nPoints particles each having nComp (liquid phase) + sum boundStates (solid phase) DOFs // in each shell; there are nPar shells - return _disc.nPoints * _disc.nComp + _disc.parTypeOffset[_disc.nParType]; + return _disc.nPoints * _disc.nComp + _disc.parTypeOffset[_disc.nParType]; } - bool GeneralRateModelDG::usesAD() const CADET_NOEXCEPT { #ifdef CADET_CHECK_ANALYTIC_JACOBIAN @@ -160,9 +157,11 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP paramProvider.pushScope("discretization"); - _linearSolver = cadet::linalg::setLinearSolver(paramProvider.exists("LINEAR_SOLVER") ? paramProvider.getString("LINEAR_SOLVER") : "SparseLU"); + _linearSolver = cadet::linalg::setLinearSolver( + paramProvider.exists("LINEAR_SOLVER") ? paramProvider.getString("LINEAR_SOLVER") : "SparseLU"); - if (!newNBoundInterface && paramProvider.exists("NBOUND")) // done here and in this order for backwards compatibility + if (!newNBoundInterface && + paramProvider.exists("NBOUND")) // done here and in this order for backwards compatibility nBound = paramProvider.getIntArray("NBOUND"); else { @@ -171,7 +170,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP paramProvider.pushScope("discretization"); } if (nBound.size() < _disc.nComp) - throw InvalidParameterException("Field NBOUND contains too few elements (NCOMP = " + std::to_string(_disc.nComp) + " required)"); + throw InvalidParameterException( + "Field NBOUND contains too few elements (NCOMP = " + std::to_string(_disc.nComp) + " required)"); if (paramProvider.exists("POLYDEG")) _disc.polyDeg = paramProvider.getInt("POLYDEG"); @@ -180,7 +180,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP if (paramProvider.getInt("POLYDEG") < 1) throw InvalidParameterException("Polynomial degree must be at least 1!"); else if (_disc.polyDeg < 3) - LOG(Warning) << "Polynomial degree > 2 in bulk discretization (cf. POLYDEG) is always recommended for performance reasons."; + LOG(Warning) << "Polynomial degree > 2 in bulk discretization (cf. POLYDEG) is always recommended for " + "performance reasons."; _disc.nNodes = _disc.polyDeg + 1; @@ -199,9 +200,11 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP int polynomial_integration_mode = 0; if (paramProvider.exists("EXACT_INTEGRATION")) polynomial_integration_mode = paramProvider.getInt("EXACT_INTEGRATION"); - _disc.exactInt = static_cast(polynomial_integration_mode); // only integration mode 0 applies the inexact collocated diagonal LGL mass matrix - - if (!newNPartypeInterface && paramProvider.exists("NPARTYPE")) // done here and in this order for backwards compatibility + _disc.exactInt = static_cast( + polynomial_integration_mode); // only integration mode 0 applies the inexact collocated diagonal LGL mass matrix + + if (!newNPartypeInterface && + paramProvider.exists("NPARTYPE")) // done here and in this order for backwards compatibility _disc.nParType = paramProvider.getInt("NPARTYPE"); else if (newNPartypeInterface) { @@ -231,11 +234,12 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP if ((std::any_of(ParNelem.begin(), ParNelem.end(), [](int value) { return value < 1; }))) throw InvalidParameterException("Particle number of elements must be at least 1!"); - if(paramProvider.exists("PAR_EXACT_INTEGRATION")) + if (paramProvider.exists("PAR_EXACT_INTEGRATION")) parExactInt = paramProvider.getBoolArray("PAR_EXACT_INTEGRATION"); if ((std::any_of(parExactInt.begin(), parExactInt.end(), [](bool value) { return !value; }))) - LOG(Warning) << "Inexact integration method (cf. PAR_EXACT_INTEGRATION) in particles might add severe! stiffness to the system and disables consistent initialization!"; + LOG(Warning) << "Inexact integration method (cf. PAR_EXACT_INTEGRATION) in particles might add severe! " + "stiffness to the system and disables consistent initialization!"; if (parPolyDeg.size() == 1) { @@ -244,7 +248,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP std::fill(_disc.parPolyDeg, _disc.parPolyDeg + _disc.nParType, parPolyDeg[0]); } else if (parPolyDeg.size() < _disc.nParType) - throw InvalidParameterException("Field PAR_POLYDEG must have 1 or NPARTYPE (" + std::to_string(_disc.nParType) + ") entries"); + throw InvalidParameterException("Field PAR_POLYDEG must have 1 or NPARTYPE (" + + std::to_string(_disc.nParType) + ") entries"); else std::copy_n(parPolyDeg.begin(), _disc.nParType, _disc.parPolyDeg); if (ParNelem.size() == 1) @@ -254,7 +259,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP std::fill(_disc.nParCell, _disc.nParCell + _disc.nParType, ParNelem[0]); } else if (ParNelem.size() < _disc.nParType) - throw InvalidParameterException("Field PAR_NELEM must have 1 or NPARTYPE (" + std::to_string(_disc.nParType) + ") entries"); + throw InvalidParameterException("Field PAR_NELEM must have 1 or NPARTYPE (" + + std::to_string(_disc.nParType) + ") entries"); else std::copy_n(ParNelem.begin(), _disc.nParType, _disc.nParCell); if (parExactInt.size() == 1) @@ -264,7 +270,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP std::fill(_disc.parExactInt, _disc.parExactInt + _disc.nParType, parExactInt[0]); } else if (parExactInt.size() < _disc.nParType) - throw InvalidParameterException("Field PAR_EXACT_INTEGRATION must have 1 or NPARTYPE (" + std::to_string(_disc.nParType) + ") entries"); + throw InvalidParameterException("Field PAR_EXACT_INTEGRATION must have 1 or NPARTYPE (" + + std::to_string(_disc.nParType) + ") entries"); else std::copy_n(parExactInt.begin(), _disc.nParType, _disc.parExactInt); } @@ -272,7 +279,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP { const std::vector nParPoints = paramProvider.getIntArray("NPAR"); if ((nParPoints.size() > 1) && (nParPoints.size() < _disc.nParType)) - throw InvalidParameterException("Field NPAR must have 1 or NPARTYPE (" + std::to_string(_disc.nParType) + ") entries"); + throw InvalidParameterException("Field NPAR must have 1 or NPARTYPE (" + std::to_string(_disc.nParType) + + ") entries"); for (unsigned int par = 0; par < _disc.nParType; par++) parPolyDeg[par] = std::max(1, std::min(nParPoints[par] - 1, 4)); @@ -289,7 +297,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP std::copy_n(parGSM.begin(), _disc.nParType, _disc.parGSM); for (int type = 0; type < _disc.nParType; type++) if (_disc.parGSM[type] && _disc.nParCell[type] != 1) - throw InvalidParameterException("Field PAR_NELEM must equal one to use a GSM discretization in the corresponding particle type"); + throw InvalidParameterException( + "Field PAR_NELEM must equal one to use a GSM discretization in the corresponding particle type"); } else // Use GSM as default for particle discretization { @@ -342,9 +351,10 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP unsigned int nTotalParPoints = 0; for (unsigned int j = 1; j < _disc.nParType + 1; ++j) { - _disc.parTypeOffset[j] = _disc.parTypeOffset[j-1] + (_disc.nComp + _disc.strideBound[j-1]) * _disc.nParPoints[j-1] * _disc.nPoints; - _disc.nParPointsBeforeType[j] = _disc.nParPointsBeforeType[j-1] + _disc.nParPoints[j-1]; - nTotalParPoints += _disc.nParPoints[j-1]; + _disc.parTypeOffset[j] = _disc.parTypeOffset[j - 1] + + (_disc.nComp + _disc.strideBound[j - 1]) * _disc.nParPoints[j - 1] * _disc.nPoints; + _disc.nParPointsBeforeType[j] = _disc.nParPointsBeforeType[j - 1] + _disc.nParPoints[j - 1]; + nTotalParPoints += _disc.nParPoints[j - 1]; } _disc.nParPointsBeforeType[_disc.nParType] = nTotalParPoints; @@ -363,7 +373,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP pdt.resize(_disc.nParType, pdt[0]); } else if (pdt.size() < _disc.nParType) - throw InvalidParameterException("Field PAR_DISC_TYPE contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field PAR_DISC_TYPE contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); for (unsigned int i = 0; i < _disc.nParType; ++i) { @@ -385,7 +396,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP pg.resize(_disc.nParType, pg[0]); } else if (pg.size() < _disc.nParType) - throw InvalidParameterException("Field PAR_GEOM contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field PAR_GEOM contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); for (unsigned int i = 0; i < _disc.nParType; ++i) { @@ -396,7 +408,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP else if (pg[i] == "SLAB") _parGeomSurfToVol[i] = SurfVolRatioSlab; else - throw InvalidParameterException("Unknown particle geometry type \"" + pg[i] + "\" at index " + std::to_string(i) + " of field PAR_GEOM"); + throw InvalidParameterException("Unknown particle geometry type \"" + pg[i] + "\" at index " + + std::to_string(i) + " of field PAR_GEOM"); } } paramProvider.pushScope("discretization"); @@ -405,7 +418,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP { _parDiscVector = paramProvider.getDoubleArray("PAR_DISC_VECTOR"); if (_parDiscVector.size() < nTotalParPoints + _disc.nParType) - throw InvalidParameterException("Field PAR_DISC_VECTOR contains too few elements (Sum [NPAR + 1] = " + std::to_string(nTotalParPoints + _disc.nParType) + " required)"); + throw InvalidParameterException("Field PAR_DISC_VECTOR contains too few elements (Sum [NPAR + 1] = " + + std::to_string(nTotalParPoints + _disc.nParType) + " required)"); } // Determine whether analytic Jacobian should be used but don't set it right now. @@ -422,7 +436,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP _initQ.resize(nTotalBound); // Determine whether surface diffusion optimization is applied (decreases Jacobian size) //@TODO? - const bool optimizeParticleJacobianBandwidth = paramProvider.exists("OPTIMIZE_PAR_BANDWIDTH") ? paramProvider.getBool("OPTIMIZE_PAR_BANDWIDTH") : true; + const bool optimizeParticleJacobianBandwidth = + paramProvider.exists("OPTIMIZE_PAR_BANDWIDTH") ? paramProvider.getBool("OPTIMIZE_PAR_BANDWIDTH") : true; // Create nonlinear solver for consistent initialization configureNonlinearSolver(paramProvider); @@ -439,7 +454,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP _singleParDepSurfDiffusion = true; if (!_singleParDepSurfDiffusion && (psdDepNames.size() < _disc.nParType)) - throw InvalidParameterException("Field PAR_SURFDIFFUSION_DEP contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field PAR_SURFDIFFUSION_DEP contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); else if (_singleParDepSurfDiffusion && (psdDepNames.size() != 1)) throw InvalidParameterException("Field PAR_SURFDIFFUSION_DEP requires (only) 1 element"); @@ -458,7 +474,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP throw InvalidParameterException("Unknown parameter dependence " + psdDepNames[0]); _parDepSurfDiffusion = std::vector(_disc.nParType, pd); - parSurfDiffDepConfSuccess = pd->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound, _disc.boundOffset); + parSurfDiffDepConfSuccess = + pd->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound, _disc.boundOffset); _hasParDepSurfDiffusion = true; } } @@ -475,10 +492,14 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP if (!_parDepSurfDiffusion[i]) throw InvalidParameterException("Unknown parameter dependence " + psdDepNames[i]); - parSurfDiffDepConfSuccess = _parDepSurfDiffusion[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, _disc.boundOffset + i * _disc.nComp) && parSurfDiffDepConfSuccess; + parSurfDiffDepConfSuccess = _parDepSurfDiffusion[i]->configureModelDiscretization( + paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, + _disc.boundOffset + i * _disc.nComp) && + parSurfDiffDepConfSuccess; } - _hasParDepSurfDiffusion = std::any_of(_parDepSurfDiffusion.cbegin(), _parDepSurfDiffusion.cend(), [](IParameterStateDependence const* pd) -> bool { return pd; }); + _hasParDepSurfDiffusion = std::any_of(_parDepSurfDiffusion.cbegin(), _parDepSurfDiffusion.cend(), + [](IParameterStateDependence const* pd) -> bool { return pd; }); } } else @@ -525,7 +546,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP } unsigned int strideColNode = _disc.nComp; - const bool transportSuccess = _convDispOp.configureModelDiscretization(paramProvider, helper, _disc.nComp, polynomial_integration_mode, _disc.nCol, _disc.polyDeg, strideColNode); + const bool transportSuccess = _convDispOp.configureModelDiscretization( + paramProvider, helper, _disc.nComp, polynomial_integration_mode, _disc.nCol, _disc.polyDeg, strideColNode); _disc.curSection = -1; @@ -533,7 +555,7 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP clearBindingModels(); _binding = std::vector(_disc.nParType, nullptr); - std::vector bindModelNames = { "NONE" }; + std::vector bindModelNames = {"NONE"}; if (paramProvider.exists("ADSORPTION_MODEL")) bindModelNames = paramProvider.getStringArray("ADSORPTION_MODEL"); @@ -546,7 +568,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP } if (!_singleBinding && (bindModelNames.size() < _disc.nParType)) - throw InvalidParameterException("Field ADSORPTION_MODEL contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field ADSORPTION_MODEL contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); else if (_singleBinding && (bindModelNames.size() != 1)) throw InvalidParameterException("Field ADSORPTION_MODEL requires (only) 1 element"); @@ -564,8 +587,12 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP if (!_binding[i]) throw InvalidParameterException("Unknown binding model " + bindModelNames[i]); - MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", _singleBinding, i, _disc.nParType == 1, _binding[i]->usesParamProviderInDiscretizationConfig()); - bindingConfSuccess = _binding[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, _disc.boundOffset + i * _disc.nComp) && bindingConfSuccess; + MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", _singleBinding, i, _disc.nParType == 1, + _binding[i]->usesParamProviderInDiscretizationConfig()); + bindingConfSuccess = + _binding[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, + _disc.boundOffset + i * _disc.nComp) && + bindingConfSuccess; } } @@ -583,7 +610,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP if (_dynReactionBulk->usesParamProviderInDiscretizationConfig()) paramProvider.pushScope("reaction_bulk"); - reactionConfSuccess = _dynReactionBulk->configureModelDiscretization(paramProvider, _disc.nComp, nullptr, nullptr); + reactionConfSuccess = + _dynReactionBulk->configureModelDiscretization(paramProvider, _disc.nComp, nullptr, nullptr); if (_dynReactionBulk->usesParamProviderInDiscretizationConfig()) paramProvider.popScope(); @@ -605,7 +633,8 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP } if (!_singleDynReaction && (dynReactModelNames.size() < _disc.nParType)) - throw InvalidParameterException("Field REACTION_MODEL_PARTICLES contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field REACTION_MODEL_PARTICLES contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); else if (_singleDynReaction && (dynReactModelNames.size() != 1)) throw InvalidParameterException("Field REACTION_MODEL_PARTICLES requires (only) 1 element"); @@ -622,8 +651,13 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP if (!_dynReaction[i]) throw InvalidParameterException("Unknown dynamic reaction model " + dynReactModelNames[i]); - MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", _singleDynReaction, i, _disc.nParType == 1, _dynReaction[i]->usesParamProviderInDiscretizationConfig()); - reactionConfSuccess = _dynReaction[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, _disc.boundOffset + i * _disc.nComp) && reactionConfSuccess; + MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", _singleDynReaction, i, + _disc.nParType == 1, + _dynReaction[i]->usesParamProviderInDiscretizationConfig()); + reactionConfSuccess = _dynReaction[i]->configureModelDiscretization( + paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, + _disc.boundOffset + i * _disc.nComp) && + reactionConfSuccess; } } } @@ -646,7 +680,7 @@ bool GeneralRateModelDG::configureModelDiscretization(IParameterProvider& paramP return transportSuccess && parSurfDiffDepConfSuccess && bindingConfSuccess && reactionConfSuccess; } - + bool GeneralRateModelDG::configure(IParameterProvider& paramProvider) { _parameters.clear(); @@ -655,12 +689,15 @@ bool GeneralRateModelDG::configure(IParameterProvider& paramProvider) // Read geometry parameters _colPorosity = paramProvider.getDouble("COL_POROSITY"); - _singleParRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parRadius, "PAR_RADIUS", _disc.nParType, _unitOpIdx); - _singleParPorosity = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parPorosity, "PAR_POROSITY", _disc.nParType, _unitOpIdx); + _singleParRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parRadius, "PAR_RADIUS", + _disc.nParType, _unitOpIdx); + _singleParPorosity = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parPorosity, "PAR_POROSITY", + _disc.nParType, _unitOpIdx); // Let PAR_CORERADIUS default to 0.0 for backwards compatibility if (paramProvider.exists("PAR_CORERADIUS")) - _singleParCoreRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parCoreRadius, "PAR_CORERADIUS", _disc.nParType, _unitOpIdx); + _singleParCoreRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parCoreRadius, + "PAR_CORERADIUS", _disc.nParType, _unitOpIdx); else { _singleParCoreRadius = true; @@ -682,7 +719,8 @@ bool GeneralRateModelDG::configure(IParameterProvider& paramProvider) // Expand to all axial cells _parTypeVolFrac.resize(_disc.nPoints * _disc.nParType, 1.0); for (unsigned int i = 1; i < _disc.nPoints; ++i) - std::copy(_parTypeVolFrac.begin(), _parTypeVolFrac.begin() + _disc.nParType, _parTypeVolFrac.begin() + _disc.nParType * i); + std::copy(_parTypeVolFrac.begin(), _parTypeVolFrac.begin() + _disc.nParType, + _parTypeVolFrac.begin() + _disc.nParType * i); } else _axiallyConstantParTypeVolFrac = false; @@ -695,30 +733,40 @@ bool GeneralRateModelDG::configure(IParameterProvider& paramProvider) // Check whether all sizes are matched if (_disc.nParType != _parRadius.size()) - throw InvalidParameterException("Number of elements in field PAR_RADIUS does not match number of particle types"); + throw InvalidParameterException( + "Number of elements in field PAR_RADIUS does not match number of particle types"); if (_disc.nParType * _disc.nPoints != _parTypeVolFrac.size()) - throw InvalidParameterException("Number of elements in field PAR_TYPE_VOLFRAC does not match number of particle types times number of axial cells"); + throw InvalidParameterException("Number of elements in field PAR_TYPE_VOLFRAC does not match number of " + "particle types times number of axial cells"); if (_disc.nParType != _parPorosity.size()) - throw InvalidParameterException("Number of elements in field PAR_POROSITY does not match number of particle types"); + throw InvalidParameterException( + "Number of elements in field PAR_POROSITY does not match number of particle types"); if (_disc.nParType != _parCoreRadius.size()) - throw InvalidParameterException("Number of elements in field PAR_CORERADIUS does not match number of particle types"); + throw InvalidParameterException( + "Number of elements in field PAR_CORERADIUS does not match number of particle types"); // Check that particle volume fractions sum to 1.0 for (unsigned int i = 0; i < _disc.nPoints; ++i) { - const double volFracSum = std::accumulate(_parTypeVolFrac.begin() + i * _disc.nParType, _parTypeVolFrac.begin() + (i+1) * _disc.nParType, 0.0, + const double volFracSum = std::accumulate( + _parTypeVolFrac.begin() + i * _disc.nParType, _parTypeVolFrac.begin() + (i + 1) * _disc.nParType, 0.0, [](double a, const active& b) -> double { return a + static_cast(b); }); if (std::abs(1.0 - volFracSum) > 1e-10) - throw InvalidParameterException("Sum of field PAR_TYPE_VOLFRAC differs from 1.0 (is " + std::to_string(volFracSum) + ") in axial cell " + std::to_string(i)); + throw InvalidParameterException("Sum of field PAR_TYPE_VOLFRAC differs from 1.0 (is " + + std::to_string(volFracSum) + ") in axial cell " + std::to_string(i)); } // Read vectorial parameters (which may also be section dependent; transport) - _filmDiffusionMode = readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _filmDiffusion, "FILM_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); - _parDiffusionMode = readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _parDiffusion, "PAR_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); + _filmDiffusionMode = readAndRegisterMultiplexCompTypeSecParam( + paramProvider, _parameters, _filmDiffusion, "FILM_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); + _parDiffusionMode = readAndRegisterMultiplexCompTypeSecParam( + paramProvider, _parameters, _parDiffusion, "PAR_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); _disc.offsetSurfDiff = new unsigned int[_disc.strideBound[_disc.nParType]]; if (paramProvider.exists("PAR_SURFDIFFUSION")) - _parSurfDiffusionMode = readAndRegisterMultiplexBndCompTypeSecParam(paramProvider, _parameters, _parSurfDiffusion, "PAR_SURFDIFFUSION", _disc.nParType, _disc.nComp, _disc.strideBound, _disc.nBound, _unitOpIdx); + _parSurfDiffusionMode = readAndRegisterMultiplexBndCompTypeSecParam( + paramProvider, _parameters, _parSurfDiffusion, "PAR_SURFDIFFUSION", _disc.nParType, _disc.nComp, + _disc.strideBound, _disc.nBound, _unitOpIdx); else { _parSurfDiffusionMode = MultiplexMode::Component; @@ -730,7 +778,8 @@ bool GeneralRateModelDG::configure(IParameterProvider& paramProvider) { if (_singleParDepSurfDiffusion && _parDepSurfDiffusion[0]) { - parSurfDiffDepConfSuccess = _parDepSurfDiffusion[0]->configure(paramProvider, _unitOpIdx, ParTypeIndep, "PAR_SURFDIFFUSION"); + parSurfDiffDepConfSuccess = + _parDepSurfDiffusion[0]->configure(paramProvider, _unitOpIdx, ParTypeIndep, "PAR_SURFDIFFUSION"); } else if (!_singleParDepSurfDiffusion) { @@ -739,20 +788,34 @@ bool GeneralRateModelDG::configure(IParameterProvider& paramProvider) if (!_parDepSurfDiffusion[i]) continue; - parSurfDiffDepConfSuccess = _parDepSurfDiffusion[i]->configure(paramProvider, _unitOpIdx, i, "PAR_SURFDIFFUSION") && parSurfDiffDepConfSuccess; + parSurfDiffDepConfSuccess = + _parDepSurfDiffusion[i]->configure(paramProvider, _unitOpIdx, i, "PAR_SURFDIFFUSION") && + parSurfDiffDepConfSuccess; } } } - if ((_filmDiffusion.size() < _disc.nComp * _disc.nParType) || (_filmDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) - throw InvalidParameterException("Number of elements in field FILM_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + ")"); - if ((_parDiffusion.size() < _disc.nComp * _disc.nParType) || (_parDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) - throw InvalidParameterException("Number of elements in field PAR_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + ")"); - if ((_parSurfDiffusion.size() < _disc.strideBound[_disc.nParType]) || ((_disc.strideBound[_disc.nParType] > 0) && (_parSurfDiffusion.size() % _disc.strideBound[_disc.nParType] != 0))) - throw InvalidParameterException("Number of elements in field PAR_SURFDIFFUSION is not a positive multiple of NTOTALBND (" + std::to_string(_disc.strideBound[_disc.nParType]) + ")"); + if ((_filmDiffusion.size() < _disc.nComp * _disc.nParType) || + (_filmDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) + throw InvalidParameterException( + "Number of elements in field FILM_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + + std::to_string(_disc.nComp * _disc.nParType) + ")"); + if ((_parDiffusion.size() < _disc.nComp * _disc.nParType) || + (_parDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) + throw InvalidParameterException( + "Number of elements in field PAR_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + + std::to_string(_disc.nComp * _disc.nParType) + ")"); + if ((_parSurfDiffusion.size() < _disc.strideBound[_disc.nParType]) || + ((_disc.strideBound[_disc.nParType] > 0) && + (_parSurfDiffusion.size() % _disc.strideBound[_disc.nParType] != 0))) + throw InvalidParameterException( + "Number of elements in field PAR_SURFDIFFUSION is not a positive multiple of NTOTALBND (" + + std::to_string(_disc.strideBound[_disc.nParType]) + ")"); if (paramProvider.exists("PORE_ACCESSIBILITY")) - _poreAccessFactorMode = readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _poreAccessFactor, "PORE_ACCESSIBILITY", _disc.nParType, _disc.nComp, _unitOpIdx); + _poreAccessFactorMode = + readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _poreAccessFactor, + "PORE_ACCESSIBILITY", _disc.nParType, _disc.nComp, _unitOpIdx); else { _poreAccessFactorMode = MultiplexMode::ComponentType; @@ -760,35 +823,54 @@ bool GeneralRateModelDG::configure(IParameterProvider& paramProvider) } if (_disc.nComp * _disc.nParType != _poreAccessFactor.size()) - throw InvalidParameterException("Number of elements in field PORE_ACCESSIBILITY differs from NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + ")"); + throw InvalidParameterException( + "Number of elements in field PORE_ACCESSIBILITY differs from NCOMP * NPARTYPE (" + + std::to_string(_disc.nComp * _disc.nParType) + ")"); // Add parameters to map - _parameters[makeParamId(hashString("COL_POROSITY"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_colPorosity; + _parameters[makeParamId(hashString("COL_POROSITY"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_colPorosity; if (_axiallyConstantParTypeVolFrac) { // Register only the first nParType items for (unsigned int i = 0; i < _disc.nParType; ++i) - _parameters[makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, i, BoundStateIndep, ReactionIndep, SectionIndep)] = &_parTypeVolFrac[i]; + _parameters[makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, i, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_parTypeVolFrac[i]; } else - registerParam2DArray(_parameters, _parTypeVolFrac, [=](bool multi, unsigned cell, unsigned int type) { return makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, type, BoundStateIndep, ReactionIndep, cell); }, _disc.nParType); + registerParam2DArray( + _parameters, _parTypeVolFrac, + [=](bool multi, unsigned cell, unsigned int type) { + return makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, type, BoundStateIndep, + ReactionIndep, cell); + }, + _disc.nParType); // Calculate the particle radial discretization variables (_parCellSize, _parCenterRadius, etc.) _disc.deltaR = new active[_disc.offsetMetric[_disc.nParType]]; updateRadialDisc(); // Register initial conditions parameters - registerParam1DArray(_parameters, _initC, [=](bool multi, unsigned int comp) { return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep); }); + registerParam1DArray(_parameters, _initC, [=](bool multi, unsigned int comp) { + return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep); + }); if (_singleBinding) { for (unsigned int c = 0; c < _disc.nComp; ++c) - _parameters[makeParamId(hashString("INIT_CP"), _unitOpIdx, c, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_initCp[c]; + _parameters[makeParamId(hashString("INIT_CP"), _unitOpIdx, c, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep)] = &_initCp[c]; } else - registerParam2DArray(_parameters, _initCp, [=](bool multi, unsigned int type, unsigned int comp) { return makeParamId(hashString("INIT_CP"), _unitOpIdx, comp, type, BoundStateIndep, ReactionIndep, SectionIndep); }, _disc.nComp); - + registerParam2DArray( + _parameters, _initCp, + [=](bool multi, unsigned int type, unsigned int comp) { + return makeParamId(hashString("INIT_CP"), _unitOpIdx, comp, type, BoundStateIndep, ReactionIndep, + SectionIndep); + }, + _disc.nComp); if (!_binding.empty()) { @@ -832,8 +914,8 @@ bool GeneralRateModelDG::configure(IParameterProvider& paramProvider) { for (unsigned int type = 0; type < _disc.nParType; ++type) { - if (!_binding[type] || !_binding[type]->requiresConfiguration()) - continue; + if (!_binding[type] || !_binding[type]->requiresConfiguration()) + continue; MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", type, _disc.nParType == 1, true); bindingConfSuccess = _binding[type]->configure(paramProvider, _unitOpIdx, type) && bindingConfSuccess; @@ -855,26 +937,29 @@ bool GeneralRateModelDG::configure(IParameterProvider& paramProvider) if (_dynReaction[0] && _dynReaction[0]->requiresConfiguration()) { MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", true); - dynReactionConfSuccess = _dynReaction[0]->configure(paramProvider, _unitOpIdx, ParTypeIndep) && dynReactionConfSuccess; + dynReactionConfSuccess = + _dynReaction[0]->configure(paramProvider, _unitOpIdx, ParTypeIndep) && dynReactionConfSuccess; } } else { for (unsigned int type = 0; type < _disc.nParType; ++type) { - if (!_dynReaction[type] || !_dynReaction[type]->requiresConfiguration()) - continue; + if (!_dynReaction[type] || !_dynReaction[type]->requiresConfiguration()) + continue; MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", type, _disc.nParType == 1, true); - dynReactionConfSuccess = _dynReaction[type]->configure(paramProvider, _unitOpIdx, type) && dynReactionConfSuccess; + dynReactionConfSuccess = + _dynReaction[type]->configure(paramProvider, _unitOpIdx, type) && dynReactionConfSuccess; } } // jaobian pattern set after binding and particle surface diffusion are configured setJacobianPattern_GRM(_globalJac, 0, _dynReactionBulk); _globalJacDisc = _globalJac; - // the solver repetitively solves the linear system with a static pattern of the jacobian (set above). - // The goal of analyzePattern() is to reorder the nonzero elements of the matrix, such that the factorization step creates less fill-in + // the solver repetitively solves the linear system with a static pattern of the jacobian (set above). + // The goal of analyzePattern() is to reorder the nonzero elements of the matrix, such that the factorization step + // creates less fill-in _linearSolver->analyzePattern(_globalJacDisc.block(_disc.nComp, _disc.nComp, numPureDofs(), numPureDofs())); return transportSuccess && parSurfDiffDepConfSuccess && bindingConfSuccess && dynReactionConfSuccess; @@ -891,7 +976,8 @@ unsigned int GeneralRateModelDG::threadLocalMemorySize() const CADET_NOEXCEPT lms.fitBlock(_binding[i]->workspaceSize(_disc.nComp, _disc.strideBound[i], _disc.nBound + i * _disc.nComp)); if (_dynReaction[i] && _dynReaction[i]->requiresWorkspace()) - lms.fitBlock(_dynReaction[i]->workspaceSize(_disc.nComp, _disc.strideBound[i], _disc.nBound + i * _disc.nComp)); + lms.fitBlock( + _dynReaction[i]->workspaceSize(_disc.nComp, _disc.strideBound[i], _disc.nBound + i * _disc.nComp)); } if (_dynReactionBulk && _dynReactionBulk->requiresWorkspace()) @@ -925,10 +1011,11 @@ unsigned int GeneralRateModelDG::threadLocalMemorySize() const CADET_NOEXCEPT unsigned int GeneralRateModelDG::numAdDirsForJacobian() const CADET_NOEXCEPT { - // The global DG Jacobian is banded around the main diagonal and has additional (also banded) entries for film diffusion. - // To feasibly seed and reconstruct the Jacobian, we create dedicated active directions for the bulk and each particle type (see @ todo) + // The global DG Jacobian is banded around the main diagonal and has additional (also banded) entries for film + // diffusion. To feasibly seed and reconstruct the Jacobian, we create dedicated active directions for the bulk and + // each particle type (see @ todo) Indexer idxr(_disc); - + int sumParBandwidth = 0; for (unsigned int type = 0; type < _disc.nParType; ++type) { @@ -953,15 +1040,18 @@ void GeneralRateModelDG::useAnalyticJacobian(const bool analyticJac) #endif } -void GeneralRateModelDG::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) +void GeneralRateModelDG::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac) { // calculate offsets between surface diffusion storage and state vector order orderSurfDiff(); Indexer idxr(_disc); - // todo: only reset jacobian pattern if it changes, i.e. once in configuration and then only for changes in SurfDiff+kinetic binding. - setJacobianPattern_GRM(_globalJac, 0, _dynReactionBulk); + // todo: only reset jacobian pattern if it changes, i.e. once in configuration and then only for changes in + // SurfDiff+kinetic binding. + setJacobianPattern_GRM(_globalJac, 0, _dynReactionBulk); _globalJacDisc = _globalJac; _convDispOp.notifyDiscontinuousSectionTransition(t, secIdx, _jacInlet); @@ -1009,22 +1099,27 @@ void GeneralRateModelDG::prepareADvectors(const AdJacobianParams& adJac) const Indexer idxr(_disc); - // The global DG Jacobian is banded around the main diagonal and has additional (also banded, but offset) entries for film diffusion, - // i.e. banded AD vector seeding is not sufficient (as it is for the FV Jacobians, see @puttmann2016 and the DG LRM Jacobian). - // The compressed vectorial AD seeding and Jacobian construction is described in the following. - // The global DG Jacobian is banded around the main diagonal and has additional (also banded) entries for film diffusion. - // To feasibly seed and reconstruct the Jacobian (we need information for decompression), we create dedicated active directions for - // the bulk and each particle type. Particle AD directions are treated as dense (per particle block) since only nCells > 6 would - // justify band compression, which rarely ever happens. + // The global DG Jacobian is banded around the main diagonal and has additional (also banded, but offset) entries + // for film diffusion, i.e. banded AD vector seeding is not sufficient (as it is for the FV Jacobians, see + // @puttmann2016 and the DG LRM Jacobian). The compressed vectorial AD seeding and Jacobian construction is + // described in the following. The global DG Jacobian is banded around the main diagonal and has additional (also + // banded) entries for film diffusion. To feasibly seed and reconstruct the Jacobian (we need information for + // decompression), we create dedicated active directions for the bulk and each particle type. Particle AD directions + // are treated as dense (per particle block) since only nCells > 6 would justify band compression, which rarely ever + // happens. // We begin by seeding the (banded around main diagonal) bulk Jacobian block - // We have differing Jacobian structures for exact integration and collocation DG scheme, i.e. we need different seed vectors - // collocation DG: 2 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend on the next and last N_n liquid phase entries of same component) - // ex. int. DG: 4 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend on the next and last 2*N_n liquid phase entries of same component) - const int lowerBandwidth = (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); + // We have differing Jacobian structures for exact integration and collocation DG scheme, i.e. we need different + // seed vectors collocation DG: 2 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend + // on the next and last N_n liquid phase entries of same component) + // ex. int. DG: 4 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend on the next + // and last 2*N_n liquid phase entries of same component) + const int lowerBandwidth = + (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); const int upperBandwidth = lowerBandwidth; const int bulkRows = idxr.offsetCp() - idxr.offsetC(); - ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + _disc.nComp, adJac.adDirOffset, bulkRows, lowerBandwidth, upperBandwidth, lowerBandwidth); + ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + _disc.nComp, adJac.adDirOffset, bulkRows, lowerBandwidth, + upperBandwidth, lowerBandwidth); // We now seed the particle Jacobian blocks using the individual AD directions for each particle type. unsigned int adDirOffset = adJac.adDirOffset + _convDispOp.requiredADdirs(); @@ -1033,8 +1128,8 @@ void GeneralRateModelDG::prepareADvectors(const AdJacobianParams& adJac) const { for (unsigned int parBlock = 0; parBlock < _disc.nPoints; parBlock++) { - // move adVec pointer to start of current particle block - active* _adVec = adJac.adY + idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ parBlock }); + // move adVec pointer to start of current particle block + active* _adVec = adJac.adY + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{parBlock}); for (int eq = 0; eq < idxr.strideParBlock(type); ++eq) { @@ -1042,7 +1137,6 @@ void GeneralRateModelDG::prepareADvectors(const AdJacobianParams& adJac) const _adVec[eq].fillADValue(adJac.adDirOffset, 0.0); // Set direction _adVec[eq].setADValue(adDirOffset + eq, 1.0); - } } if (type < _disc.nParType - 1u) // move to dedicated DoFs of next particle type @@ -1061,14 +1155,16 @@ void GeneralRateModelDG::extractJacobianFromAD(active const* const adRes, unsign const active* const adVec = adRes + idxr.offsetC(); /* Extract bulk phase equations entries */ - const int lowerBandwidth = (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); + const int lowerBandwidth = + (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); const int upperBandwidth = lowerBandwidth; const int stride = lowerBandwidth + 1 + upperBandwidth; int diagDir = lowerBandwidth; const int bulkDoFs = idxr.offsetCp() - idxr.offsetC(); const int eqOffset = 0; const int matOffset = idxr.offsetC(); - ad::extractBandedBlockEigenJacobianFromAd(adVec, adDirOffset, diagDir, lowerBandwidth, upperBandwidth, eqOffset, bulkDoFs, _globalJac, matOffset); + ad::extractBandedBlockEigenJacobianFromAd(adVec, adDirOffset, diagDir, lowerBandwidth, upperBandwidth, eqOffset, + bulkDoFs, _globalJac, matOffset); /* Handle particle liquid and solid phase equations entries */ // Read particle Jacobian entries from dedicated AD directions @@ -1078,13 +1174,14 @@ void GeneralRateModelDG::extractJacobianFromAD(active const* const adRes, unsign { for (unsigned int par = 0; par < _disc.nPoints; par++) { - const int eqOffset_res = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ par }); - const int eqOffset_mat = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ par }); + const int eqOffset_res = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); + const int eqOffset_mat = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); for (unsigned int phase = 0; phase < idxr.strideParBlock(type); phase++) { for (unsigned int phaseTo = 0; phaseTo < idxr.strideParBlock(type); phaseTo++) { - _globalJac.coeffRef(eqOffset_mat + phase, eqOffset_mat + phaseTo) = adRes[eqOffset_res + phase].getADValue(offsetParticleTypeDirs + phaseTo); + _globalJac.coeffRef(eqOffset_mat + phase, eqOffset_mat + phaseTo) = + adRes[eqOffset_res + phase].getADValue(offsetParticleTypeDirs + phaseTo); } } } @@ -1107,61 +1204,71 @@ void GeneralRateModelDG::extractJacobianFromAD(active const* const adRes, unsign void GeneralRateModelDG::checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const { // todo write this function - //Indexer idxr(_disc); + // Indexer idxr(_disc); - //LOG(Debug) << "AD dir offset: " << adDirOffset << " DiagDirCol: " << _convDispOp.jacobian().lowerBandwidth() << " DiagDirPar: " << _jacP[0].lowerBandwidth(); + // LOG(Debug) << "AD dir offset: " << adDirOffset << " DiagDirCol: " << _convDispOp.jacobian().lowerBandwidth() << " + // DiagDirPar: " << _jacP[0].lowerBandwidth(); //// Column - //const double maxDiffCol = _convDispOp.checkAnalyticJacobianAgainstAd(adRes, adDirOffset); + // const double maxDiffCol = _convDispOp.checkAnalyticJacobianAgainstAd(adRes, adDirOffset); //// Particles - //double maxDiffPar = 0.0; - //for (unsigned int type = 0; type < _disc.nParType; ++type) + // double maxDiffPar = 0.0; + // for (unsigned int type = 0; type < _disc.nParType; ++type) //{ // for (unsigned int pblk = 0; pblk < _disc.nPoints; ++pblk) // { // linalg::BandMatrix& jacMat = _jacP[_disc.nPoints * type + pblk]; - // const double localDiff = ad::compareBandedJacobianWithAd(adRes + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}), adDirOffset, jacMat.lowerBandwidth(), jacMat); - // LOG(Debug) << "-> Par type " << type << " block " << pblk << " diff: " << localDiff; - // maxDiffPar = std::max(maxDiffPar, localDiff); + // const double localDiff = ad::compareBandedJacobianWithAd(adRes + idxr.offsetCp(ParticleTypeIndex{type}, + // ParticleIndex{pblk}), adDirOffset, jacMat.lowerBandwidth(), jacMat); LOG(Debug) << "-> Par type " << type + // << " block " << pblk << " diff: " << localDiff; maxDiffPar = std::max(maxDiffPar, localDiff); // } - //} + // } } #endif -int GeneralRateModelDG::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModelDG::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); _factorizeJacobian = true; if (_analyticJac) - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); else - return residualWithJacobian(simTime, ConstSimulationState{ simState.vecStateY, nullptr }, nullptr, adJac, threadLocalMem); + return residualWithJacobian(simTime, ConstSimulationState{simState.vecStateY, nullptr}, nullptr, adJac, + threadLocalMem); } -int GeneralRateModelDG::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModelDG::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); // Evaluate residual do not compute Jacobian or parameter sensitivities - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } -int GeneralRateModelDG::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModelDG::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); - //FDJac = calcFDJacobian(static_cast(simState.vecStateY), static_cast(simState.vecStateYdot), simTime, threadLocalMem, 2.0); // debug code + // FDJac = calcFDJacobian(static_cast(simState.vecStateY), static_cast(simState.vecStateYdot), simTime, threadLocalMem, 2.0); // debug code // Evaluate residual, use AD for Jacobian if required but do not evaluate parameter derivatives return residual(simTime, simState, res, adJac, threadLocalMem, true, false); } int GeneralRateModelDG::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, - const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity) + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, + bool updateJacobian, bool paramSensitivity) { if (updateJacobian) { @@ -1172,7 +1279,8 @@ int GeneralRateModelDG::residual(const SimulationTime& simTime, const ConstSimul { if (paramSensitivity) { - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -1181,7 +1289,8 @@ int GeneralRateModelDG::residual(const SimulationTime& simTime, const ConstSimul return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } else { @@ -1194,9 +1303,11 @@ int GeneralRateModelDG::residual(const SimulationTime& simTime, const ConstSimul // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -1217,15 +1328,18 @@ int GeneralRateModelDG::residual(const SimulationTime& simTime, const ConstSimul // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); // Only do comparison if we have a residuals vector (which is not always the case) if (res) { // Evaluate with analytical Jacobian which is stored in the band matrices - retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); // Compare AD with anaytic Jacobian checkAnalyticJacobianAgainstAd(adJac.adRes, adJac.adDirOffset); @@ -1245,7 +1359,8 @@ int GeneralRateModelDG::residual(const SimulationTime& simTime, const ConstSimul // @todo Check if this is necessary ad::resetAd(adJac.adRes, numDofs()); - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -1254,12 +1369,14 @@ int GeneralRateModelDG::residual(const SimulationTime& simTime, const ConstSimul return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } } template -int GeneralRateModelDG::residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModelDG::residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, + ResidualType* const res, util::ThreadLocalStorage& threadLocalMem) { if (wantRes) { @@ -1277,7 +1394,8 @@ int GeneralRateModelDG::residualImpl(double t, unsigned int secIdx, StateType co _disc.newStaticJac = false; - if (cadet_unlikely(!success)) { + if (cadet_unlikely(!success)) + { LOG(Error) << "Jacobian pattern did not fit the Jacobian estimation"; } } @@ -1291,15 +1409,21 @@ int GeneralRateModelDG::residualImpl(double t, unsigned int secIdx, StateType co { const unsigned int parType = pblk / _disc.nPoints; const unsigned int par = pblk % _disc.nPoints; - residualParticle(t, parType, par, secIdx, y, yDot, res, threadLocalMem); + residualParticle(t, parType, par, secIdx, y, yDot, res, + threadLocalMem); } // we need to add the DG discretized solid entries of the jacobian that get overwritten by the binding kernel. // These entries only exist for the GRM with surface diffusion - if (wantJac) { - for (unsigned int parType = 0; parType < _disc.nParType; parType++) { - if (_binding[parType]->hasDynamicReactions() && _hasSurfaceDiffusion[parType]) { - active const* const _parSurfDiff = getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + _disc.nBoundBeforeType[parType]; + if (wantJac) + { + for (unsigned int parType = 0; parType < _disc.nParType; parType++) + { + if (_binding[parType]->hasDynamicReactions() && _hasSurfaceDiffusion[parType]) + { + active const* const _parSurfDiff = + getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + + _disc.nBoundBeforeType[parType]; addSolidDGentries(parType, _parSurfDiff); } } @@ -1322,10 +1446,12 @@ int GeneralRateModelDG::residualImpl(double t, unsigned int secIdx, StateType co } template -int GeneralRateModelDG::residualBulk(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModelDG::residualBulk(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, + ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem) { if (wantRes) - _convDispOp.residual(*this, t, secIdx, yBase, yDotBase, resBase, typename cadet::ParamSens::enabled()); + _convDispOp.residual(*this, t, secIdx, yBase, yDotBase, resBase, + typename cadet::ParamSens::enabled()); if (!_dynReactionBulk || (_dynReactionBulk->numReactionsLiquid() == 0)) return 0; @@ -1334,7 +1460,8 @@ int GeneralRateModelDG::residualBulk(double t, unsigned int secIdx, StateType co LinearBufferAllocator tlmAlloc = threadLocalMem.get(); // Dynamic reactions - if (_dynReactionBulk) { + if (_dynReactionBulk) + { StateType const* y = yBase + idxr.offsetC(); @@ -1342,11 +1469,13 @@ int GeneralRateModelDG::residualBulk(double t, unsigned int secIdx, StateType co { for (unsigned int col = 0; col < _disc.nPoints; ++col, y += idxr.strideColNode()) { - const ColumnPosition colPos{ (0.5 + static_cast(col)) / static_cast(_disc.nPoints), 0.0, 0.0 }; + const ColumnPosition colPos{(0.5 + static_cast(col)) / static_cast(_disc.nPoints), 0.0, + 0.0}; linalg::BandedEigenSparseRowIterator jac(_globalJac, col * idxr.strideColNode()); // static_cast should be sufficient here, but this statement is also analyzed when wantJac = false - _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(y), -1.0, jac, tlmAlloc); + _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(y), -1.0, + jac, tlmAlloc); } return 0; @@ -1356,14 +1485,16 @@ int GeneralRateModelDG::residualBulk(double t, unsigned int secIdx, StateType co for (unsigned int col = 0; col < _disc.nPoints; ++col, y += idxr.strideColNode(), res += idxr.strideColNode()) { - const ColumnPosition colPos{ (0.5 + static_cast(col)) / static_cast(_disc.nPoints), 0.0, 0.0 }; + const ColumnPosition colPos{(0.5 + static_cast(col)) / static_cast(_disc.nPoints), 0.0, + 0.0}; _dynReactionBulk->residualLiquidAdd(t, secIdx, colPos, y, res, -1.0, tlmAlloc); if (wantJac) { linalg::BandedEigenSparseRowIterator jac(_globalJac, col * idxr.strideColNode()); // static_cast should be sufficient here, but this statement is also analyzed when wantJac = false - _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(y), -1.0, jac, tlmAlloc); + _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(y), -1.0, + jac, tlmAlloc); } } } @@ -1372,22 +1503,27 @@ int GeneralRateModelDG::residualBulk(double t, unsigned int secIdx, StateType co } template -int GeneralRateModelDG::residualParticle(double t, unsigned int parType, unsigned int colNode, unsigned int secIdx, StateType const* yBase, - double const* yDotBase, ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModelDG::residualParticle(double t, unsigned int parType, unsigned int colNode, unsigned int secIdx, + StateType const* yBase, double const* yDotBase, ResidualType* resBase, + util::ThreadLocalStorage& threadLocalMem) { Indexer idxr(_disc); LinearBufferAllocator tlmAlloc = threadLocalMem.get(); // Special case: individual treatment of time derivatives in particle mass balance at inner particle boundary node - bool specialCase = !_disc.parExactInt[parType] && (_parGeomSurfToVol[parType] != _disc.SurfVolRatioSlab && _parCoreRadius[parType] == 0.0); + bool specialCase = !_disc.parExactInt[parType] && + (_parGeomSurfToVol[parType] != _disc.SurfVolRatioSlab && _parCoreRadius[parType] == 0.0); // Prepare parameters - active const* const parDiff = getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + parType * _disc.nComp; + active const* const parDiff = + getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + parType * _disc.nComp; // Ordering of particle surface diffusion: // bnd0comp0, bnd0comp1, bnd0comp2, bnd1comp0, bnd1comp1, bnd1comp2 - active const* const parSurfDiff = getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + _disc.nBoundBeforeType[parType]; + active const* const parSurfDiff = + getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + + _disc.nBoundBeforeType[parType]; // z coordinate (column length normed to 1) of current node - needed in externally dependent adsorption kinetic const double z = _convDispOp.relativeCoordinate(colNode); @@ -1397,7 +1533,8 @@ int GeneralRateModelDG::residualParticle(double t, unsigned int parType, unsigne // and jac[1] is the first upper diagonal. We can also access the rows from left to // right beginning with the last lower diagonal moving towards the main diagonal and // continuing to the last upper diagonal by using the native() method. - linalg::BandedEigenSparseRowIterator jac(_globalJac, idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode })); + linalg::BandedEigenSparseRowIterator jac(_globalJac, + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode})); int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); const parts::cell::CellParameters cellResParams = makeCellResidualParams(parType, qsReaction); @@ -1408,31 +1545,43 @@ int GeneralRateModelDG::residualParticle(double t, unsigned int parType, unsigne { int cell = std::floor(par / _disc.nParNode[parType]); // local Pointers to current particle node, needed in residualKernel - StateType const* local_y = yBase + idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }) + par * idxr.strideParNode(parType); - double const* local_yDot = yDotBase ? yDotBase + idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }) + par * idxr.strideParNode(parType) : nullptr; - ResidualType* local_res = resBase ? resBase + idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }) + par * idxr.strideParNode(parType) : nullptr; - - // r (particle) coordinate of current node (particle radius normed to 1) - needed in externally dependent adsorption kinetic - const double r = static_cast( - (_disc.deltaR[_disc.offsetMetric[parType] + cell] * cell - + 0.5 * _disc.deltaR[_disc.offsetMetric[parType] + cell] * (1 + _disc.parNodes[parType][par % _disc.nParNode[parType]])) - / (_parRadius[parType] - _parCoreRadius[parType]) - ); - const ColumnPosition colPos{ z, 0.0, r }; + StateType const* local_y = yBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}) + + par * idxr.strideParNode(parType); + double const* local_yDot = yDotBase + ? yDotBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}) + + par * idxr.strideParNode(parType) + : nullptr; + ResidualType* local_res = resBase + ? resBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}) + + par * idxr.strideParNode(parType) + : nullptr; + + // r (particle) coordinate of current node (particle radius normed to 1) - needed in externally dependent + // adsorption kinetic + const double r = static_cast((_disc.deltaR[_disc.offsetMetric[parType] + cell] * cell + + 0.5 * _disc.deltaR[_disc.offsetMetric[parType] + cell] * + (1 + _disc.parNodes[parType][par % _disc.nParNode[parType]])) / + (_parRadius[parType] - _parCoreRadius[parType])); + const ColumnPosition colPos{z, 0.0, r}; // Handle time derivatives, binding, dynamic reactions. // Special case: Dont add time derivatives to inner boundary node for DG discretized mass balance equations. - // This can be achieved by setting yDot pointer to null before passing to residual kernel, and adding only the time derivative for dynamic binding + // This can be achieved by setting yDot pointer to null before passing to residual kernel, and adding only the + // time derivative for dynamic binding // TODO Check Treatment of reactions (do we need yDot then?) if (cadet_unlikely(par == 0 && specialCase)) { if (wantRes) - parts::cell::residualKernel( - t, secIdx, colPos, local_y, nullptr, local_res, jac, cellResParams, tlmAlloc // TODO Check Treatment of reactions (do we need yDot then?) + parts::cell::residualKernel( + t, secIdx, colPos, local_y, nullptr, local_res, jac, cellResParams, + tlmAlloc // TODO Check Treatment of reactions (do we need yDot then?) ); else - parts::cell::residualKernel( - t, secIdx, colPos, local_y, nullptr, local_res, jac, cellResParams, tlmAlloc // TODO Check Treatment of reactions (do we need yDot then?) + parts::cell::residualKernel( + t, secIdx, colPos, local_y, nullptr, local_res, jac, cellResParams, + tlmAlloc // TODO Check Treatment of reactions (do we need yDot then?) ); if (!wantRes) @@ -1449,8 +1598,9 @@ int GeneralRateModelDG::residualParticle(double t, unsigned int parType, unsigne if (cellResParams.qsReaction[idx]) continue; - // For kinetic bindings and surface diffusion, we have an additional DG-discretized mass balance eq. - // -> add time derivate at inner bonudary node only without surface diffusion + // For kinetic bindings and surface diffusion, we have an additional DG-discretized mass balance + // eq. + // -> add time derivate at inner bonudary node only without surface diffusion else if (_hasSurfaceDiffusion[parType]) continue; // Some bound states might still not be effected by surface diffusion @@ -1466,14 +1616,14 @@ int GeneralRateModelDG::residualParticle(double t, unsigned int parType, unsigne else { if (wantRes) - parts::cell::residualKernel( - t, secIdx, colPos, local_y, local_yDot, local_res, jac, cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + t, secIdx, colPos, local_y, local_yDot, local_res, jac, cellResParams, tlmAlloc); else { - parts::cell::residualKernel( - t, secIdx, colPos, local_y, local_yDot, local_res, jac, cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + t, secIdx, colPos, local_y, local_yDot, local_res, jac, cellResParams, tlmAlloc); } } @@ -1481,22 +1631,26 @@ int GeneralRateModelDG::residualParticle(double t, unsigned int parType, unsigne jac += idxr.strideParNode(parType); } - if(!wantRes) + if (!wantRes) return 0; // We still need to handle transport/diffusion // Get pointers to the particle block of the current column node, particle type - const StateType* c_p = yBase + idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }); - ResidualType* resC_p = resBase + idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }); + const StateType* c_p = yBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}); + ResidualType* resC_p = resBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}); // Mobile phase RHS // Get film diffusion flux at current node to compute boundary condition - active const* const filmDiff = getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + parType * _disc.nComp; - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - _disc.localFlux[comp] = filmDiff[comp] * (yBase[idxr.offsetC() + colNode * idxr.strideColNode() + comp] - - yBase[idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }) + (_disc.nParPoints[parType] - 1) * idxr.strideParNode(parType) + comp]); + active const* const filmDiff = + getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + parType * _disc.nComp; + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + _disc.localFlux[comp] = + filmDiff[comp] * (yBase[idxr.offsetC() + colNode * idxr.strideColNode() + comp] - + yBase[idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}) + + (_disc.nParPoints[parType] - 1) * idxr.strideParNode(parType) + comp]); } const int nNodes = _disc.nParNode[parType]; @@ -1516,37 +1670,54 @@ int GeneralRateModelDG::residualParticle(double t, unsigned int parType, unsigne // solve GSM-discretized particle mass balance // // ====================================================================================// - Eigen::Map, 0, InnerStride> resCp(resC_p + comp, nPoints, InnerStride(idxr.strideParNode(parType))); - Eigen::Map, 0, InnerStride> Cp(c_p + comp, nPoints, InnerStride(idxr.strideParNode(parType))); + Eigen::Map, 0, InnerStride> resCp( + resC_p + comp, nPoints, InnerStride(idxr.strideParNode(parType))); + Eigen::Map, 0, InnerStride> Cp( + c_p + comp, nPoints, InnerStride(idxr.strideParNode(parType))); // Use auxiliary variable to get c^p + \sum 1 / \Beta_p c^s - Eigen::Map, 0, InnerStride<>> sum_cp_cs(reinterpret_cast(&_disc.g_pSum[parType][0]), nPoints, InnerStride<>(1)); + Eigen::Map, 0, InnerStride<>> sum_cp_cs( + reinterpret_cast(&_disc.g_pSum[parType][0]), nPoints, InnerStride<>(1)); sum_cp_cs = static_cast(parDiff[comp]) * Cp.template cast(); for (int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) // some bound states might still not be effected by surface diffusion + if (parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != + 0.0) // some bound states might still not be effected by surface diffusion { - Eigen::Map, 0, InnerStride> c_s(c_p + _disc.nComp + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd, nPoints, InnerStride(idxr.strideParNode(parType))); - ParamType invBetaP = (1.0 - static_cast(_parPorosity[parType])) / (static_cast(_poreAccessFactor[_disc.nComp * parType + comp]) * static_cast(_parPorosity[parType])); - sum_cp_cs += invBetaP * static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) * c_s; - - /* For kinetic bindings with surface diffusion: add the additional DG-discretized particle mass balance equations to residual */ + Eigen::Map, 0, InnerStride> c_s( + c_p + _disc.nComp + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + bnd, + nPoints, InnerStride(idxr.strideParNode(parType))); + ParamType invBetaP = (1.0 - static_cast(_parPorosity[parType])) / + (static_cast(_poreAccessFactor[_disc.nComp * parType + comp]) * + static_cast(_parPorosity[parType])); + sum_cp_cs += + invBetaP * static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) * c_s; + + /* For kinetic bindings with surface diffusion: add the additional DG-discretized particle mass + * balance equations to residual */ if (!qsReaction[bnd]) { // Eigen access to current bound state residual - Eigen::Map, 0, InnerStride> resCs(resBase + idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }) + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd, + Eigen::Map, 0, InnerStride> resCs( + resBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}) + + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + bnd, nPoints, InnerStride(idxr.strideParNode(parType))); // Use auxiliary variable to get \Beta_p D_s c^s - Eigen::Map, 0, InnerStride<>> c_s_modified(reinterpret_cast(&_disc.g_p[parType][0]), nPoints, InnerStride<>(1)); + Eigen::Map, 0, InnerStride<>> c_s_modified( + reinterpret_cast(&_disc.g_p[parType][0]), nPoints, InnerStride<>(1)); // Apply squared inverse mapping and surface diffusion - c_s_modified = 2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType]]) * 2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType]]) * - static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) * c_s; + c_s_modified = 2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType]]) * 2.0 / + static_cast(_disc.deltaR[_disc.offsetMetric[parType]]) * + static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) * c_s; - Eigen::Map, 0, InnerStride> c_s_modified_const(&c_s_modified[0], nPoints, InnerStride(1)); + Eigen::Map, 0, InnerStride> c_s_modified_const( + &c_s_modified[0], nPoints, InnerStride(1)); parGSMVolumeIntegral(parType, c_s_modified_const, resCs); // Leave out the surface integral as we only have one element, i.e. we apply BC with zeros @@ -1555,14 +1726,15 @@ int GeneralRateModelDG::residualParticle(double t, unsigned int parType, unsigne } // Apply squared inverse mapping - sum_cp_cs *= 2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType]]) * 2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType]]); + sum_cp_cs *= 2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType]]) * 2.0 / + static_cast(_disc.deltaR[_disc.offsetMetric[parType]]); - Eigen::Map, 0, InnerStride> sum_cp_cs_const(&sum_cp_cs[0], nPoints, InnerStride(1)); + Eigen::Map, 0, InnerStride> sum_cp_cs_const( + &sum_cp_cs[0], nPoints, InnerStride(1)); parGSMVolumeIntegral(parType, sum_cp_cs_const, resCp); // Pass sum_cp_cs_const to match the DGSEM interface; nullptr might also be feasible parSurfaceIntegral(parType, sum_cp_cs_const, resCp, nNodes, 1u, false, comp); - } } else // DGSEM implementation @@ -1570,7 +1742,9 @@ int GeneralRateModelDG::residualParticle(double t, unsigned int parType, unsigne for (unsigned int comp = 0; comp < nComp; comp++) { // Component dependent (through access factor) inverse Beta_P - ParamType invBetaP = (1.0 - static_cast(_parPorosity[parType])) / (static_cast(_poreAccessFactor[_disc.nComp * parType + comp]) * static_cast(_parPorosity[parType])); + ParamType invBetaP = (1.0 - static_cast(_parPorosity[parType])) / + (static_cast(_poreAccessFactor[_disc.nComp * parType + comp]) * + static_cast(_parPorosity[parType])); // =====================================================================================================// // Solve auxiliary systems d_p g_p + d_s beta_p sum g_s= d (d_p c_p + d_s beta_p sum c_s) / d xi // @@ -1579,55 +1753,74 @@ int GeneralRateModelDG::residualParticle(double t, unsigned int parType, unsigne unsigned int strideCell = nNodes; unsigned int strideNode = 1u; // Reset cache for auxiliary variable - Eigen::Map, 0, InnerStride<>> _g_p(reinterpret_cast(&_disc.g_p[parType][0]), nPoints, InnerStride<>(1)); - Eigen::Map, 0, InnerStride<>> _g_pSum(reinterpret_cast(&_disc.g_pSum[parType][0]), nPoints, InnerStride<>(1)); + Eigen::Map, 0, InnerStride<>> _g_p( + reinterpret_cast(&_disc.g_p[parType][0]), nPoints, InnerStride<>(1)); + Eigen::Map, 0, InnerStride<>> _g_pSum( + reinterpret_cast(&_disc.g_pSum[parType][0]), nPoints, InnerStride<>(1)); _g_p.setZero(); _g_pSum.setZero(); - Eigen::Map, 0, InnerStride> cp(c_p + comp, _disc.nParPoints[parType], InnerStride(idxr.strideParNode(parType))); + Eigen::Map, 0, InnerStride> cp( + c_p + comp, _disc.nParPoints[parType], InnerStride(idxr.strideParNode(parType))); - // Handle surface diffusion: Compute auxiliary variable; For kinetic bindings: add additional mass balance to residual of respective bound state + // Handle surface diffusion: Compute auxiliary variable; For kinetic bindings: add additional mass balance + // to residual of respective bound state if (_hasSurfaceDiffusion[parType]) { for (int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) // some bound states might still not be effected by surface diffusion + if (parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != + 0.0) // some bound states might still not be effected by surface diffusion { // Get solid phase vector - Eigen::Map, 0, InnerStride> c_s(c_p + strideParLiquid + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd, + Eigen::Map, 0, InnerStride> c_s( + c_p + strideParLiquid + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + bnd, nPoints, InnerStride(strideParNode)); // Compute g_s = d c_s / d xi solve_auxiliary_DG(parType, c_s, strideCell, strideNode, comp); // Apply invBeta_p, d_s and add to sum -> gSum += d_s * invBeta_p * (D c - M^-1 B [c - c^*]) - _g_pSum += _g_p.template cast() * invBetaP * static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]); + _g_pSum += _g_p.template cast() * invBetaP * + static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]); - /* For kinetic bindings with surface diffusion: add the additional DG-discretized particle mass balance equations to residual */ + /* For kinetic bindings with surface diffusion: add the additional DG-discretized particle mass + * balance equations to residual */ if (!qsReaction[bnd]) { // Eigen access to current bound state residual - Eigen::Map, 0, InnerStride> resCs(resBase + idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }) + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd, + Eigen::Map, 0, InnerStride> resCs( + resBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}) + + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + bnd, nPoints, InnerStride(idxr.strideParNode(parType))); // Promote auxiliary variable storage from double to active if required // @todo is there a more efficient or elegant solution? if (std::is_same::value && std::is_same::value) - vectorPromoter(reinterpret_cast(&_g_p[0]), nPoints); // reinterpret_cast only required because statement is scanned also when StateType != double + vectorPromoter(reinterpret_cast(&_g_p[0]), + nPoints); // reinterpret_cast only required because statement is scanned + // also when StateType != double // Access auxiliary variable as ResidualType - Eigen::Map, 0, InnerStride<>> _g_p_ResType(reinterpret_cast(&_disc.g_p[parType][0]), nPoints, InnerStride<>(1)); + Eigen::Map, 0, InnerStride<>> _g_p_ResType( + reinterpret_cast(&_disc.g_p[parType][0]), nPoints, InnerStride<>(1)); applyParInvMap(_g_p_ResType, parType); _g_p_ResType *= static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]); // Eigen access to auxiliary variable of current bound state - Eigen::Map, 0, InnerStride> _g_p_ResType_const(&_g_p_ResType[0], nPoints, InnerStride(1)); + Eigen::Map, 0, InnerStride> _g_p_ResType_const( + &_g_p_ResType[0], nPoints, InnerStride(1)); - // Add - D_r * gs to the residual, including metric part.->res = invMap^2* [ -D_r * (d_s c^s) ] + // Add - D_r * gs to the residual, including metric part.->res = invMap^2* [ -D_r * (d_s + // c^s) ] parVolumeIntegral(parType, false, _g_p_ResType_const, resCs); - // Add M^-1 B (gs - gs^*) to the residual -> res = invMap^2 * [ - D_r * (d_s c^s) + M^-1 B (gs - gs^*) ] - parSurfaceIntegral(parType, _g_p_ResType_const, resCs, strideCell, strideNode, false, comp, true); + // Add M^-1 B (gs - gs^*) to the residual -> res = invMap^2 * [ - D_r * (d_s c^s) + M^-1 B + // (gs - gs^*) ] + parSurfaceIntegral(parType, _g_p_ResType_const, resCs, + strideCell, strideNode, false, comp, true); } } } @@ -1639,26 +1832,31 @@ int GeneralRateModelDG::residualParticle(double t, unsigned int parType, unsigne // Add particle diffusion part to auxiliary variable sum -> gSum += d_p * (D c - M^-1 B [c - c^*]) _g_pSum += _g_p * static_cast(parDiff[comp]); - // apply squared inverse mapping to sum of bound state auxiliary variables -> gSum = - invMap^2 * (d_p * c^p + sum_mi d_s invBeta_p c^s) + // apply squared inverse mapping to sum of bound state auxiliary variables -> gSum = - invMap^2 * (d_p * c^p + // + sum_mi d_s invBeta_p c^s) applyParInvMap(_g_pSum, parType); // ====================================================================================// // solve DG-discretized particle mass balance // // ====================================================================================// - /* Solve DG-discretized particle mass balance equation */ + /* Solve DG-discretized particle mass balance equation */ - Eigen::Map, 0, InnerStride> _g_pSum_const(&_g_pSum[0], nPoints, InnerStride(1)); + Eigen::Map, 0, InnerStride> _g_pSum_const( + &_g_pSum[0], nPoints, InnerStride(1)); // Eigen access to particle liquid residual - Eigen::Map, 0, InnerStride> resCp(resC_p + comp, nPoints, InnerStride(idxr.strideParNode(parType))); + Eigen::Map, 0, InnerStride> resCp( + resC_p + comp, nPoints, InnerStride(idxr.strideParNode(parType))); - // Add - D_r * (g_sum) to the residual, including metric part. -> res = - D_r * (d_p * c^p + invBeta_p sum_mi d_s c^s) + // Add - D_r * (g_sum) to the residual, including metric part. -> res = - D_r * (d_p * c^p + invBeta_p + // sum_mi d_s c^s) parVolumeIntegral(parType, false, _g_pSum_const, resCp); - // Add M^-1 B (g_sum - g_sum^*) to the residual -> res = - D_r * (d_p * c^p + invBeta_p sum_mi d_s c^s) + M^-1 B (g_sum - g_sum^*) - parSurfaceIntegral(parType, _g_pSum_const, resCp, strideCell, strideNode, false, comp); - + // Add M^-1 B (g_sum - g_sum^*) to the residual -> res = - D_r * (d_p * c^p + invBeta_p sum_mi d_s c^s) + + // M^-1 B (g_sum - g_sum^*) + parSurfaceIntegral(parType, _g_pSum_const, resCp, strideCell, strideNode, false, + comp); } } @@ -1666,7 +1864,8 @@ int GeneralRateModelDG::residualParticle(double t, unsigned int parType, unsigne } template -int GeneralRateModelDG::residualFlux(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, ResidualType* resBase) +int GeneralRateModelDG::residualFlux(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, + ResidualType* resBase) { Indexer idxr(_disc); @@ -1686,8 +1885,10 @@ int GeneralRateModelDG::residualFlux(double t, unsigned int secIdx, StateType co // Ordering of diffusion: // sec0type0comp0, sec0type0comp1, sec0type0comp2, sec0type1comp0, sec0type1comp1, sec0type1comp2, // sec1type0comp0, sec1type0comp1, sec1type0comp2, sec1type1comp0, sec1type1comp1, sec1type1comp2, ... - active const* const filmDiff = getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; - active const* const parDiff = getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const filmDiff = + getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const parDiff = + getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; const ParamType surfaceToVolumeRatio = _parGeomSurfToVol[type] / static_cast(_parRadius[type]); @@ -1700,34 +1901,37 @@ int GeneralRateModelDG::residualFlux(double t, unsigned int secIdx, StateType co const unsigned int colNode = i / _disc.nComp; const unsigned int comp = i - colNode * _disc.nComp; // + 1/Beta_c * (surfaceToVolumeRatio_{p,j}) * d_j * (k_f * [c_l - c_p]) - resCol[i] += static_cast(filmDiff[comp]) * jacCF_val * static_cast(_parTypeVolFrac[type + colNode * _disc.nParType]) - * (yCol[i] - yParType[colNode * idxr.strideParBlock(type) + (_disc.nParPoints[type] - 1) * idxr.strideParNode(type) + comp]); + resCol[i] += static_cast(filmDiff[comp]) * jacCF_val * + static_cast(_parTypeVolFrac[type + colNode * _disc.nParType]) * + (yCol[i] - yParType[colNode * idxr.strideParBlock(type) + + (_disc.nParPoints[type] - 1) * idxr.strideParNode(type) + comp]); } // Bead boundary condition is computed in residualParticle(). - } return 0; } -parts::cell::CellParameters GeneralRateModelDG::makeCellResidualParams(unsigned int parType, int const* qsReaction) const -{ - return parts::cell::CellParameters - { - _disc.nComp, - _disc.nBound + _disc.nComp * parType, - _disc.boundOffset + _disc.nComp * parType, - _disc.strideBound[parType], - qsReaction, - _parPorosity[parType], - _poreAccessFactor.data() + _disc.nComp * parType, - _binding[parType], - (_dynReaction[parType] && (_dynReaction[parType]->numReactionsCombined() > 0)) ? _dynReaction[parType] : nullptr - }; +parts::cell::CellParameters GeneralRateModelDG::makeCellResidualParams(unsigned int parType, + int const* qsReaction) const +{ + return parts::cell::CellParameters{_disc.nComp, + _disc.nBound + _disc.nComp * parType, + _disc.boundOffset + _disc.nComp * parType, + _disc.strideBound[parType], + qsReaction, + _parPorosity[parType], + _poreAccessFactor.data() + _disc.nComp * parType, + _binding[parType], + (_dynReaction[parType] && (_dynReaction[parType]->numReactionsCombined() > 0)) + ? _dynReaction[parType] + : nullptr}; } -int GeneralRateModelDG::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModelDG::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidualSens); @@ -1736,17 +1940,21 @@ int GeneralRateModelDG::residualSensFwdWithJacobian(const SimulationTime& simTim return residual(simTime, simState, nullptr, adJac, threadLocalMem, true, true); } -int GeneralRateModelDG::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem) +int GeneralRateModelDG::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidualSens); // Evaluate residual for all parameters using AD in vector mode - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adRes, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, adRes, threadLocalMem); } int GeneralRateModelDG::residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3) + const std::vector& yS, + const std::vector& ySdot, + const std::vector& resS, active const* adRes, + double* const tmp1, double* const tmp2, double* const tmp3) { BENCH_SCOPE(_timerResidualSens); @@ -1756,10 +1964,12 @@ int GeneralRateModelDG::residualSensFwdCombine(const SimulationTime& simTime, co for (std::size_t param = 0; param < yS.size(); ++param) { // Directional derivative (dF / dy) * s - multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, yS[param], 1.0, 0.0, tmp1); + multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, yS[param], 1.0, 0.0, + tmp1); // Directional derivative (dF / dyDot) * sDot - multiplyWithDerivativeJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, ySdot[param], tmp2); + multiplyWithDerivativeJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, ySdot[param], + tmp2); double* const ptrResS = resS[param]; @@ -1782,11 +1992,12 @@ int GeneralRateModelDG::residualSensFwdCombine(const SimulationTime& simTime, co return 0; } /** - * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, \dot{y}\right) @f$) + * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, + * \dot{y}\right) @f$) * @details Actually, the operation @f$ z = \alpha \frac{\partial F}{\partial y} x + \beta z @f$ is performed. * - * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ once - * before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. + * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ + * once before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. * @param [in] simTime Current simulation time point * @param [in] simState Simulation state vectors * @param [in] yS Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial y} @f$ @@ -1794,7 +2005,8 @@ int GeneralRateModelDG::residualSensFwdCombine(const SimulationTime& simTime, co * @param [in] beta Factor @f$ \beta @f$ in front of @f$ z @f$ * @param [in,out] ret Vector @f$ z @f$ which stores the result of the operation */ -void GeneralRateModelDG::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret) +void GeneralRateModelDG::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret) { Indexer idxr(_disc); @@ -1807,21 +2019,26 @@ void GeneralRateModelDG::multiplyWithJacobian(const SimulationTime& simTime, con // Main Jacobian Eigen::Map ret_vec(ret + idxr.offsetC(), numPureDofs()); Eigen::Map yS_vec(yS + idxr.offsetC(), numPureDofs()); - ret_vec = alpha * _globalJac.block(idxr.offsetC(), idxr.offsetC(), numPureDofs(), numPureDofs()) * yS_vec + beta * ret_vec; + ret_vec = alpha * _globalJac.block(idxr.offsetC(), idxr.offsetC(), numPureDofs(), numPureDofs()) * yS_vec + + beta * ret_vec; // Map inlet DOFs to the column inlet (first bulk cells) // Inlet at z = 0 for forward flow, at z = L for backward flow. unsigned int offInlet = _convDispOp.forwardFlow() ? 0 : (_disc.nCol - 1u) * idxr.strideColCell(); - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int node = 0; node < (_disc.exactInt ? _disc.nNodes : 1); node++) { - ret[idxr.offsetC() + offInlet + comp * idxr.strideColComp() + node * idxr.strideColNode()] += alpha * _jacInlet(node, 0) * yS[comp]; + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int node = 0; node < (_disc.exactInt ? _disc.nNodes : 1); node++) + { + ret[idxr.offsetC() + offInlet + comp * idxr.strideColComp() + node * idxr.strideColNode()] += + alpha * _jacInlet(node, 0) * yS[comp]; } } } /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ + * with a given vector * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). * @param [in] simTime Current simulation time point @@ -1829,7 +2046,9 @@ void GeneralRateModelDG::multiplyWithJacobian(const SimulationTime& simTime, con * @param [in] sDot Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ * @param [out] ret Vector @f$ z @f$ which stores the result of the operation */ -void GeneralRateModelDG::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret) +void GeneralRateModelDG::multiplyWithDerivativeJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, double const* sDot, + double* ret) { Indexer idxr(_disc); @@ -1855,14 +2074,16 @@ void GeneralRateModelDG::multiplyWithDerivativeJacobian(const SimulationTime& si int const* const qsReaction = _binding[type]->reactionQuasiStationarity(); // Particle shells - const int offsetCpType = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ pblk }); + const int offsetCpType = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}); for (unsigned int shell = 0; shell < _disc.nParPoints[type]; ++shell) { const int offsetCpShell = offsetCpType + shell * idxr.strideParNode(type); double const* const mobileSdot = sDot + offsetCpShell; double* const mobileRet = ret + offsetCpShell; - parts::cell::multiplyWithDerivativeJacobianKernel(mobileSdot, mobileRet, _disc.nComp, nBound, boundOffset, _disc.strideBound[type], qsReaction, 1.0, invBetaP); + parts::cell::multiplyWithDerivativeJacobianKernel(mobileSdot, mobileRet, _disc.nComp, nBound, + boundOffset, _disc.strideBound[type], + qsReaction, 1.0, invBetaP); } } } CADET_PARFOR_END; @@ -1920,7 +2141,8 @@ void GeneralRateModelDG::setEquidistantRadialDisc(unsigned int parType) const active radius = _parRadius[parType] - _parCoreRadius[parType]; const active dr = radius / static_cast(_disc.nParCell[parType]); - std::fill(_parCellSize.data() + _disc.offsetMetric[parType], _parCellSize.data() + _disc.offsetMetric[parType] + _disc.nParCell[parType], dr); + std::fill(_parCellSize.data() + _disc.offsetMetric[parType], + _parCellSize.data() + _disc.offsetMetric[parType] + _disc.nParCell[parType], dr); if (_parGeomSurfToVol[parType] == SurfVolRatioSphere) { @@ -1985,7 +2207,8 @@ void GeneralRateModelDG::setEquivolumeRadialDisc(unsigned int parType) { active r_out = _parRadius[parType]; active r_in = _parCoreRadius[parType]; - const active volumePerShell = (pow(_parRadius[parType], 3.0) - pow(_parCoreRadius[parType], 3.0)) / static_cast(_disc.nParCell[parType]); + const active volumePerShell = (pow(_parRadius[parType], 3.0) - pow(_parCoreRadius[parType], 3.0)) / + static_cast(_disc.nParCell[parType]); for (unsigned int cell = 0; cell < _disc.nParCell[parType]; ++cell) { @@ -2010,7 +2233,8 @@ void GeneralRateModelDG::setEquivolumeRadialDisc(unsigned int parType) { active r_out = _parRadius[parType]; active r_in = _parCoreRadius[parType]; - const active volumePerShell = (sqr(_parRadius[parType]) - sqr(_parCoreRadius[parType])) / static_cast(_disc.nParCell[parType]); + const active volumePerShell = + (sqr(_parRadius[parType]) - sqr(_parCoreRadius[parType])) / static_cast(_disc.nParCell[parType]); for (unsigned int cell = 0; cell < _disc.nParCell[parType]; ++cell) { @@ -2035,7 +2259,8 @@ void GeneralRateModelDG::setEquivolumeRadialDisc(unsigned int parType) { active r_out = _parRadius[parType]; active r_in = _parCoreRadius[parType]; - const active volumePerShell = (_parRadius[parType] - _parCoreRadius[parType]) / static_cast(_disc.nParCell[parType]); + const active volumePerShell = + (_parRadius[parType] - _parCoreRadius[parType]) / static_cast(_disc.nParCell[parType]); for (unsigned int cell = 0; cell < _disc.nParCell[parType]; ++cell) { @@ -2070,7 +2295,8 @@ void GeneralRateModelDG::setUserdefinedRadialDisc(unsigned int parType) active* const ptrInnerSurfAreaPerVolume = _parInnerSurfAreaPerVolume.data() + _disc.offsetMetric[parType]; // Care for the right ordering and include 0.0 / 1.0 if not already in the vector. - std::vector orderedInterfaces = std::vector(_parDiscVector.begin() + _disc.offsetMetric[parType] + parType, + std::vector orderedInterfaces = std::vector( + _parDiscVector.begin() + _disc.offsetMetric[parType] + parType, _parDiscVector.begin() + _disc.offsetMetric[parType] + parType + _disc.nParCell[parType] + 1); // Sort in descending order @@ -2082,7 +2308,9 @@ void GeneralRateModelDG::setUserdefinedRadialDisc(unsigned int parType) // Map [0, 1] -> [core radius, particle radius] via linear interpolation for (unsigned int cell = 0; cell < _disc.nParCell[parType]; ++cell) - orderedInterfaces[cell] = static_cast(orderedInterfaces[cell]) * (_parRadius[parType] - _parCoreRadius[parType]) + _parCoreRadius[parType]; + orderedInterfaces[cell] = + static_cast(orderedInterfaces[cell]) * (_parRadius[parType] - _parCoreRadius[parType]) + + _parCoreRadius[parType]; if (_parGeomSurfToVol[parType] == SurfVolRatioSphere) { @@ -2097,7 +2325,8 @@ void GeneralRateModelDG::setUserdefinedRadialDisc(unsigned int parType) ptrOuterSurfAreaPerVolume[cell] = 3.0 * sqr(orderedInterfaces[cell]) / vol; ptrInnerSurfAreaPerVolume[cell] = 3.0 * sqr(orderedInterfaces[cell + 1]) / vol; // Note that the DG particle shells are oppositely ordered compared to the FV particle shells - _disc.deltaR[_disc.offsetMetric[parType] + _disc.nParCell[parType] - (cell + 1)] = ptrOuterSurfAreaPerVolume[cell] - ptrInnerSurfAreaPerVolume[cell]; + _disc.deltaR[_disc.offsetMetric[parType] + _disc.nParCell[parType] - (cell + 1)] = + ptrOuterSurfAreaPerVolume[cell] - ptrInnerSurfAreaPerVolume[cell]; } } else if (_parGeomSurfToVol[parType] == SurfVolRatioCylinder) @@ -2113,7 +2342,8 @@ void GeneralRateModelDG::setUserdefinedRadialDisc(unsigned int parType) ptrOuterSurfAreaPerVolume[cell] = 2.0 * orderedInterfaces[cell] / vol; ptrInnerSurfAreaPerVolume[cell] = 2.0 * orderedInterfaces[cell + 1] / vol; // Note that the DG particle shells are oppositely ordered compared to the FV particle shells - _disc.deltaR[_disc.offsetMetric[parType] + _disc.nParCell[parType] - (cell + 1)] = ptrOuterSurfAreaPerVolume[cell] - ptrInnerSurfAreaPerVolume[cell]; + _disc.deltaR[_disc.offsetMetric[parType] + _disc.nParCell[parType] - (cell + 1)] = + ptrOuterSurfAreaPerVolume[cell] - ptrInnerSurfAreaPerVolume[cell]; } } else if (_parGeomSurfToVol[parType] == SurfVolRatioSlab) @@ -2129,16 +2359,18 @@ void GeneralRateModelDG::setUserdefinedRadialDisc(unsigned int parType) ptrOuterSurfAreaPerVolume[cell] = 1.0 / vol; ptrInnerSurfAreaPerVolume[cell] = 1.0 / vol; // Note that the DG particle shells are oppositely ordered compared to the FV particle shells - _disc.deltaR[_disc.offsetMetric[parType] + _disc.nParCell[parType] - (cell + 1)] = ptrOuterSurfAreaPerVolume[cell] - ptrInnerSurfAreaPerVolume[cell]; + _disc.deltaR[_disc.offsetMetric[parType] + _disc.nParCell[parType] - (cell + 1)] = + ptrOuterSurfAreaPerVolume[cell] - ptrInnerSurfAreaPerVolume[cell]; } } } -// todo: parameter sensitivities for particle radius. Here, we have the problem that every DG operator becomes an active type. -// alternatively (only for exact integration), we could store more matrices and compute the metric dependend calculations in the residual. -// inexact integration approach is deprecated anyways but would require active type DG operators since every entry is multiplied by an individual metric term, -// wherease for the exact integration approach we have sums of three matrices each multiplied by its own metric term, whcih could be applied iteratively to the residual/solution. -// Not needed for Slab, and only two matrices (exact integration approach) required for Cylinder. +// todo: parameter sensitivities for particle radius. Here, we have the problem that every DG operator becomes an active +// type. alternatively (only for exact integration), we could store more matrices and compute the metric dependend +// calculations in the residual. inexact integration approach is deprecated anyways but would require active type DG +// operators since every entry is multiplied by an individual metric term, wherease for the exact integration approach +// we have sums of three matrices each multiplied by its own metric term, whcih could be applied iteratively to the +// residual/solution. Not needed for Slab, and only two matrices (exact integration approach) required for Cylinder. // This approach should only be used when necessary, i.e. solely when particle radius parameter sensitivity is required. void GeneralRateModelDG::updateRadialDisc() { @@ -2149,9 +2381,10 @@ void GeneralRateModelDG::updateRadialDisc() { for (int cell = 0; cell < _disc.nParCell[parType]; cell++) { - _disc.deltaR[_disc.offsetMetric[parType] + cell] = (_parRadius[parType] - _parCoreRadius[parType]) / _disc.nParCell[parType]; + _disc.deltaR[_disc.offsetMetric[parType] + cell] = + (_parRadius[parType] - _parCoreRadius[parType]) / _disc.nParCell[parType]; } - setEquidistantRadialDisc(parType); + setEquidistantRadialDisc(parType); } else if (_parDiscType[parType] == ParticleDiscretizationMode::Equivolume) setEquivolumeRadialDisc(parType); @@ -2169,64 +2402,114 @@ void GeneralRateModelDG::updateRadialDisc() _disc.Ir[_disc.offsetMetric[parType] + cell] = Vector::Zero(_disc.nParNode[parType]); for (int node = 0; node < _disc.nParNode[parType]; node++) - _disc.Ir[_disc.offsetMetric[parType] + cell][node] = _disc.deltaR[_disc.offsetMetric[parType] + cell] / 2.0 * (_disc.parNodes[parType][node] + 1.0); + _disc.Ir[_disc.offsetMetric[parType] + cell][node] = + _disc.deltaR[_disc.offsetMetric[parType] + cell] / 2.0 * (_disc.parNodes[parType][node] + 1.0); _disc.Dr[_disc.offsetMetric[parType] + cell].resize(_disc.nParNode[parType], _disc.nParNode[parType]); _disc.Dr[_disc.offsetMetric[parType] + cell].setZero(); - active r_L = _parCoreRadius[parType] + cell * _disc.deltaR[_disc.offsetMetric[parType] + cell]; // left boundary of current cell + active r_L = _parCoreRadius[parType] + + cell * _disc.deltaR[_disc.offsetMetric[parType] + cell]; // left boundary of current cell - _disc.Ir[_disc.offsetMetric[parType] + cell] = _disc.Ir[_disc.offsetMetric[parType] + cell] + VectorXd::Ones(_disc.nParNode[parType]) * r_L; + _disc.Ir[_disc.offsetMetric[parType] + cell] = + _disc.Ir[_disc.offsetMetric[parType] + cell] + VectorXd::Ones(_disc.nParNode[parType]) * r_L; if (_parGeomSurfToVol[parType] == SurfVolRatioSphere) - _disc.Ir[_disc.offsetMetric[parType] + cell] = _disc.Ir[_disc.offsetMetric[parType] + cell].array().square(); + _disc.Ir[_disc.offsetMetric[parType] + cell] = + _disc.Ir[_disc.offsetMetric[parType] + cell].array().square(); else if (_parGeomSurfToVol[parType] == SurfVolRatioSlab) - _disc.Ir[_disc.offsetMetric[parType] + cell] = Vector::Ones(_disc.nParNode[parType]); // no metrics for slab + _disc.Ir[_disc.offsetMetric[parType] + cell] = + Vector::Ones(_disc.nParNode[parType]); // no metrics for slab // (D_r)_{i, j} = D_{i, j} * (r_j / r_i) [only needed for inexact integration] _disc.Dr[_disc.offsetMetric[parType] + cell] = _disc.parPolyDerM[parType]; - _disc.Dr[_disc.offsetMetric[parType] + cell].array().rowwise() *= _disc.Ir[_disc.offsetMetric[parType] + cell].array().template cast().transpose(); - _disc.Dr[_disc.offsetMetric[parType] + cell].array().colwise() *= _disc.Ir[_disc.offsetMetric[parType] + cell].array().template cast().cwiseInverse(); - - // compute mass matrices for exact integration based on particle geometry, via transformation to normalized Jacobi polynomials with weight function w - if (_parGeomSurfToVol[parType] == SurfVolRatioSphere) // r^2 = r_i^2 + (1 + \xi) * r_i * DeltaR_i / 2.0 + (1 + \xi)^2 * (DeltaR_i / 2.0)^2 + _disc.Dr[_disc.offsetMetric[parType] + cell].array().rowwise() *= + _disc.Ir[_disc.offsetMetric[parType] + cell].array().template cast().transpose(); + _disc.Dr[_disc.offsetMetric[parType] + cell].array().colwise() *= + _disc.Ir[_disc.offsetMetric[parType] + cell].array().template cast().cwiseInverse(); + + // compute mass matrices for exact integration based on particle geometry, via transformation to normalized + // Jacobi polynomials with weight function w + if (_parGeomSurfToVol[parType] == SurfVolRatioSphere) // r^2 = r_i^2 + (1 + \xi) * r_i * DeltaR_i / 2.0 + + // (1 + \xi)^2 * (DeltaR_i / 2.0)^2 { - _disc.parInvMM[_disc.offsetMetric[parType] + cell] = parts::dgtoolbox::mMatrix(_disc.parPolyDeg[parType], _disc.parNodes[parType], 0.0, 2.0) * pow((static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell]) / 2.0), 2.0); - if (cell > 0 || _parCoreRadius[parType] != 0.0) // following contributions are zero for first cell when R_c = 0 (no particle core) - _disc.parInvMM[_disc.offsetMetric[parType] + cell] += parts::dgtoolbox::mMatrix(_disc.parPolyDeg[parType], _disc.parNodes[parType], 0.0, 1.0) * (static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell]) * static_cast(r_L)) - + parts::dgtoolbox::mMatrix(_disc.parPolyDeg[parType], _disc.parNodes[parType], 0.0, 0.0) * pow(static_cast(r_L), 2.0); - - _disc.parInvMM[_disc.offsetMetric[parType] + cell] = _disc.parInvMM[_disc.offsetMetric[parType] + cell].inverse(); - _disc.minus_InvMM_ST[_disc.offsetMetric[parType] + cell] = -_disc.parInvMM[_disc.offsetMetric[parType] + cell] * _disc.parPolyDerM[parType].transpose() * _disc.parInvMM[_disc.offsetMetric[parType] + cell].inverse(); + _disc.parInvMM[_disc.offsetMetric[parType] + cell] = + parts::dgtoolbox::mMatrix(_disc.parPolyDeg[parType], _disc.parNodes[parType], 0.0, 2.0) * + pow((static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell]) / 2.0), 2.0); + if (cell > 0 || + _parCoreRadius[parType] != + 0.0) // following contributions are zero for first cell when R_c = 0 (no particle core) + _disc.parInvMM[_disc.offsetMetric[parType] + cell] += + parts::dgtoolbox::mMatrix(_disc.parPolyDeg[parType], _disc.parNodes[parType], 0.0, 1.0) * + (static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell]) * + static_cast(r_L)) + + parts::dgtoolbox::mMatrix(_disc.parPolyDeg[parType], _disc.parNodes[parType], 0.0, 0.0) * + pow(static_cast(r_L), 2.0); + + _disc.parInvMM[_disc.offsetMetric[parType] + cell] = + _disc.parInvMM[_disc.offsetMetric[parType] + cell].inverse(); + _disc.minus_InvMM_ST[_disc.offsetMetric[parType] + cell] = + -_disc.parInvMM[_disc.offsetMetric[parType] + cell] * _disc.parPolyDerM[parType].transpose() * + _disc.parInvMM[_disc.offsetMetric[parType] + cell].inverse(); // particle GSM specific second order stiffness matrix (single element, i.e. nParCell = 1) - _disc.secondOrderStiffnessM[parType] = std::pow(static_cast(_parCoreRadius[parType]), 2.0) * parts::dgtoolbox::secondOrderStiffnessMatrix(_disc.parPolyDeg[parType], 0.0, 0.0, _disc.parNodes[parType]); - _disc.secondOrderStiffnessM[parType] += static_cast(_disc.deltaR[_disc.offsetMetric[parType]]) * static_cast(_parCoreRadius[parType]) * parts::dgtoolbox::secondOrderStiffnessMatrix(_disc.parPolyDeg[parType], 0.0, 1.0, _disc.parNodes[parType]); - _disc.secondOrderStiffnessM[parType] += std::pow(static_cast(_disc.deltaR[_disc.offsetMetric[parType]]) / 2.0, 2.0) * parts::dgtoolbox::secondOrderStiffnessMatrix(_disc.parPolyDeg[parType], 0.0, 2.0, _disc.parNodes[parType]); + _disc.secondOrderStiffnessM[parType] = + std::pow(static_cast(_parCoreRadius[parType]), 2.0) * + parts::dgtoolbox::secondOrderStiffnessMatrix(_disc.parPolyDeg[parType], 0.0, 0.0, + _disc.parNodes[parType]); + _disc.secondOrderStiffnessM[parType] += + static_cast(_disc.deltaR[_disc.offsetMetric[parType]]) * + static_cast(_parCoreRadius[parType]) * + parts::dgtoolbox::secondOrderStiffnessMatrix(_disc.parPolyDeg[parType], 0.0, 1.0, + _disc.parNodes[parType]); + _disc.secondOrderStiffnessM[parType] += + std::pow(static_cast(_disc.deltaR[_disc.offsetMetric[parType]]) / 2.0, 2.0) * + parts::dgtoolbox::secondOrderStiffnessMatrix(_disc.parPolyDeg[parType], 0.0, 2.0, + _disc.parNodes[parType]); } else if (_parGeomSurfToVol[parType] == SurfVolRatioCylinder) // r = r_i + (1 + \xi) * DeltaR_i / 2.0 { - _disc.parInvMM[_disc.offsetMetric[parType] + cell] = parts::dgtoolbox::mMatrix(_disc.parPolyDeg[parType], _disc.parNodes[parType], 0.0, 1.0) * (static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell]) / 2.0); - if (cell > 0 || _parCoreRadius[parType] != 0.0) // following contribution is zero for first cell when R_c = 0 (no particle core) - _disc.parInvMM[_disc.offsetMetric[parType] + cell] += parts::dgtoolbox::mMatrix(_disc.parPolyDeg[parType], _disc.parNodes[parType], 0.0, 0.0) * static_cast(r_L); - - _disc.parInvMM[_disc.offsetMetric[parType] + cell] = _disc.parInvMM[_disc.offsetMetric[parType] + cell].inverse(); - _disc.minus_InvMM_ST[_disc.offsetMetric[parType] + cell] = -_disc.parInvMM[_disc.offsetMetric[parType] + cell] * _disc.parPolyDerM[parType].transpose() * _disc.parInvMM[_disc.offsetMetric[parType] + cell].inverse(); + _disc.parInvMM[_disc.offsetMetric[parType] + cell] = + parts::dgtoolbox::mMatrix(_disc.parPolyDeg[parType], _disc.parNodes[parType], 0.0, 1.0) * + (static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell]) / 2.0); + if (cell > 0 || + _parCoreRadius[parType] != + 0.0) // following contribution is zero for first cell when R_c = 0 (no particle core) + _disc.parInvMM[_disc.offsetMetric[parType] + cell] += + parts::dgtoolbox::mMatrix(_disc.parPolyDeg[parType], _disc.parNodes[parType], 0.0, 0.0) * + static_cast(r_L); + + _disc.parInvMM[_disc.offsetMetric[parType] + cell] = + _disc.parInvMM[_disc.offsetMetric[parType] + cell].inverse(); + _disc.minus_InvMM_ST[_disc.offsetMetric[parType] + cell] = + -_disc.parInvMM[_disc.offsetMetric[parType] + cell] * _disc.parPolyDerM[parType].transpose() * + _disc.parInvMM[_disc.offsetMetric[parType] + cell].inverse(); // particle GSM specific second order stiffness matrix (single element, i.e. nParCell = 1) - _disc.secondOrderStiffnessM[parType] = static_cast(_parCoreRadius[parType]) * parts::dgtoolbox::secondOrderStiffnessMatrix(_disc.parPolyDeg[parType], 0.0, 0.0, _disc.parNodes[parType]); - _disc.secondOrderStiffnessM[parType] += static_cast(_disc.deltaR[_disc.offsetMetric[parType]]) / 2.0 * parts::dgtoolbox::secondOrderStiffnessMatrix(_disc.parPolyDeg[parType], 0.0, 1.0, _disc.parNodes[parType]); + _disc.secondOrderStiffnessM[parType] = + static_cast(_parCoreRadius[parType]) * + parts::dgtoolbox::secondOrderStiffnessMatrix(_disc.parPolyDeg[parType], 0.0, 0.0, + _disc.parNodes[parType]); + _disc.secondOrderStiffnessM[parType] += + static_cast(_disc.deltaR[_disc.offsetMetric[parType]]) / 2.0 * + parts::dgtoolbox::secondOrderStiffnessMatrix(_disc.parPolyDeg[parType], 0.0, 1.0, + _disc.parNodes[parType]); } else if (_parGeomSurfToVol[parType] == SurfVolRatioSlab) // r = 1 { - _disc.minus_InvMM_ST[_disc.offsetMetric[parType] + cell] = -_disc.parInvMM[_disc.offsetMetric[parType] + cell] * _disc.parPolyDerM[parType].transpose() * _disc.parInvMM[_disc.offsetMetric[parType] + cell].inverse(); - - _disc.secondOrderStiffnessM[parType] = parts::dgtoolbox::secondOrderStiffnessMatrix(_disc.parPolyDeg[parType], 0.0, 0.0, _disc.parNodes[parType]); - _disc.parInvMM[_disc.offsetMetric[parType] + cell] = parts::dgtoolbox::invMMatrix(_disc.parPolyDeg[parType], _disc.parNodes[parType], 0.0, 0.0); + _disc.minus_InvMM_ST[_disc.offsetMetric[parType] + cell] = + -_disc.parInvMM[_disc.offsetMetric[parType] + cell] * _disc.parPolyDerM[parType].transpose() * + _disc.parInvMM[_disc.offsetMetric[parType] + cell].inverse(); + + _disc.secondOrderStiffnessM[parType] = parts::dgtoolbox::secondOrderStiffnessMatrix( + _disc.parPolyDeg[parType], 0.0, 0.0, _disc.parNodes[parType]); + _disc.parInvMM[_disc.offsetMetric[parType] + cell] = + parts::dgtoolbox::invMMatrix(_disc.parPolyDeg[parType], _disc.parNodes[parType], 0.0, 0.0); } } - _disc.minus_parInvMM_Ar[parType] = -_disc.parInvMM[_disc.offsetMetric[parType]] * _disc.secondOrderStiffnessM[parType]; + _disc.minus_parInvMM_Ar[parType] = + -_disc.parInvMM[_disc.offsetMetric[parType]] * _disc.secondOrderStiffnessM[parType]; } } @@ -2234,13 +2517,18 @@ bool GeneralRateModelDG::setParameter(const ParameterId& pId, double value) { if (pId.unitOperation == _unitOpIdx) { - if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, _poreAccessFactor, _disc.nParType, _disc.nComp, value, nullptr)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, + _poreAccessFactor, _disc.nParType, _disc.nComp, value, nullptr)) return true; - if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, _disc.nParType, _disc.nComp, value, nullptr)) + if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, + _disc.nParType, _disc.nComp, value, nullptr)) return true; - if (multiplexCompTypeSecParameterValue(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, _disc.nParType, _disc.nComp, value, nullptr)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, + _disc.nParType, _disc.nComp, value, nullptr)) return true; - if (multiplexBndCompTypeSecParameterValue(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, _disc.nBound, _disc.boundOffset, value, nullptr)) + if (multiplexBndCompTypeSecParameterValue(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, + _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, + _disc.nBound, _disc.boundOffset, value, nullptr)) return true; const int mpIc = multiplexInitialConditions(pId, value, false); if (mpIc > 0) @@ -2251,7 +2539,8 @@ bool GeneralRateModelDG::setParameter(const ParameterId& pId, double value) // Intercept changes to PAR_TYPE_VOLFRAC when not specified per axial cell (but once globally) if (_axiallyConstantParTypeVolFrac && (pId.name == hashString("PAR_TYPE_VOLFRAC"))) { - if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep)) + if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep)) return false; if (pId.particleType >= _disc.nParType) return false; @@ -2264,9 +2553,11 @@ bool GeneralRateModelDG::setParameter(const ParameterId& pId, double value) if (multiplexTypeParameterValue(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, value, nullptr)) return true; - if (multiplexTypeParameterValue(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, value, nullptr)) + if (multiplexTypeParameterValue(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, value, + nullptr)) return true; - if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, nullptr)) + if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, + nullptr)) return true; if (model::setParameter(pId, value, _parDepSurfDiffusion, _singleParDepSurfDiffusion)) @@ -2311,13 +2602,18 @@ void GeneralRateModelDG::setSensitiveParameterValue(const ParameterId& pId, doub { if (pId.unitOperation == _unitOpIdx) { - if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, _poreAccessFactor, _disc.nParType, _disc.nComp, value, &_sensParams)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, + _poreAccessFactor, _disc.nParType, _disc.nComp, value, &_sensParams)) return; - if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, _disc.nParType, _disc.nComp, value, &_sensParams)) + if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, + _disc.nParType, _disc.nComp, value, &_sensParams)) return; - if (multiplexCompTypeSecParameterValue(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, _disc.nParType, _disc.nComp, value, &_sensParams)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, + _disc.nParType, _disc.nComp, value, &_sensParams)) return; - if (multiplexBndCompTypeSecParameterValue(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, _disc.nBound, _disc.boundOffset, value, &_sensParams)) + if (multiplexBndCompTypeSecParameterValue(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, + _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, + _disc.nBound, _disc.boundOffset, value, &_sensParams)) return; if (multiplexInitialConditions(pId, value, true) != 0) return; @@ -2325,7 +2621,8 @@ void GeneralRateModelDG::setSensitiveParameterValue(const ParameterId& pId, doub // Intercept changes to PAR_TYPE_VOLFRAC when not specified per axial cell (but once globally) if (_axiallyConstantParTypeVolFrac && (pId.name == hashString("PAR_TYPE_VOLFRAC"))) { - if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep)) + if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep)) return; if (pId.particleType >= _disc.nParType) return; @@ -2339,14 +2636,18 @@ void GeneralRateModelDG::setSensitiveParameterValue(const ParameterId& pId, doub return; } - if (multiplexTypeParameterValue(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, value, &_sensParams)) + if (multiplexTypeParameterValue(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, value, + &_sensParams)) return; - if (multiplexTypeParameterValue(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, value, &_sensParams)) + if (multiplexTypeParameterValue(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, value, + &_sensParams)) return; - if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, &_sensParams)) + if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, + &_sensParams)) return; - if (model::setSensitiveParameterValue(pId, value, _sensParams, _parDepSurfDiffusion, _singleParDepSurfDiffusion)) + if (model::setSensitiveParameterValue(pId, value, _sensParams, _parDepSurfDiffusion, + _singleParDepSurfDiffusion)) return; if (_convDispOp.setSensitiveParameterValue(_sensParams, pId, value)) @@ -2364,25 +2665,31 @@ bool GeneralRateModelDG::setSensitiveParameter(const ParameterId& pId, unsigned { if (pId.unitOperation == _unitOpIdx) { - if (multiplexCompTypeSecParameterAD(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, _poreAccessFactor, _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) + if (multiplexCompTypeSecParameterAD(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, + _poreAccessFactor, _disc.nParType, _disc.nComp, adDirection, adValue, + _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexCompTypeSecParameterAD(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) + if (multiplexCompTypeSecParameterAD(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, + _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexCompTypeSecParameterAD(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) + if (multiplexCompTypeSecParameterAD(pId, hashString("PAR_DIFFUSION"), _parDiffusionMode, _parDiffusion, + _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexBndCompTypeSecParameterAD(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, _disc.nBound, _disc.boundOffset, adDirection, adValue, _sensParams)) + if (multiplexBndCompTypeSecParameterAD(pId, hashString("PAR_SURFDIFFUSION"), _parSurfDiffusionMode, + _parSurfDiffusion, _disc.nParType, _disc.nComp, _disc.strideBound, + _disc.nBound, _disc.boundOffset, adDirection, adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; @@ -2400,7 +2707,8 @@ bool GeneralRateModelDG::setSensitiveParameter(const ParameterId& pId, unsigned // Intercept changes to PAR_TYPE_VOLFRAC when not specified per axial cell (but once globally) if (_axiallyConstantParTypeVolFrac && (pId.name == hashString("PAR_TYPE_VOLFRAC"))) { - if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep)) + if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep)) return false; if (pId.particleType >= _disc.nParType) return false; @@ -2415,27 +2723,32 @@ bool GeneralRateModelDG::setSensitiveParameter(const ParameterId& pId, unsigned return true; } - if (multiplexTypeParameterAD(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, adDirection, adValue, _sensParams)) + if (multiplexTypeParameterAD(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, adDirection, adValue, + _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexTypeParameterAD(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, adDirection, adValue, _sensParams)) + if (multiplexTypeParameterAD(pId, hashString("PAR_CORERADIUS"), _singleParCoreRadius, _parCoreRadius, + adDirection, adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexTypeParameterAD(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, adDirection, adValue, _sensParams)) + if (multiplexTypeParameterAD(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, adDirection, + adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (model::setSensitiveParameter(pId, adDirection, adValue, _sensParams, _parDepSurfDiffusion, _singleParDepSurfDiffusion)) + if (model::setSensitiveParameter(pId, adDirection, adValue, _sensParams, _parDepSurfDiffusion, + _singleParDepSurfDiffusion)) { - LOG(Debug) << "Found parameter " << pId << " in surface diffusion parameter dependence: Dir " << adDirection << " is set to " << adValue; + LOG(Debug) << "Found parameter " << pId << " in surface diffusion parameter dependence: Dir " << adDirection + << " is set to " << adValue; return true; } @@ -2449,8 +2762,9 @@ bool GeneralRateModelDG::setSensitiveParameter(const ParameterId& pId, unsigned const bool result = UnitOperationBase::setSensitiveParameter(pId, adDirection, adValue); // Check whether particle radius or core radius has been set active and update radial discretization if necessary - // Note that we need to recompute the radial discretization variables (_parCellSize, _parCenterRadius, _parOuterSurfAreaPerVolume, _parInnerSurfAreaPerVolume) - // because their gradient has changed (although their nominal value has not changed). + // Note that we need to recompute the radial discretization variables (_parCellSize, _parCenterRadius, + // _parOuterSurfAreaPerVolume, _parInnerSurfAreaPerVolume) because their gradient has changed (although their + // nominal value has not changed). if ((pId.name == hashString("PAR_RADIUS")) || (pId.name == hashString("PAR_CORERADIUS"))) updateRadialDisc(); @@ -2519,7 +2833,7 @@ int GeneralRateModelDG::Exporter::writeSolidPhase(unsigned int parType, double* cadet_assert(parType < _disc.nParType); const unsigned int stride = _disc.nComp + _disc.strideBound[parType]; - double const* ptr = _data + _idx.offsetCp(ParticleTypeIndex{ parType }) + _disc.nComp; + double const* ptr = _data + _idx.offsetCp(ParticleTypeIndex{parType}) + _disc.nComp; for (unsigned int i = 0; i < _disc.nPoints; ++i) { for (unsigned int j = 0; j < _disc.nParPoints[parType]; ++j) @@ -2537,7 +2851,7 @@ int GeneralRateModelDG::Exporter::writeParticleMobilePhase(unsigned int parType, cadet_assert(parType < _disc.nParType); const unsigned int stride = _disc.nComp + _disc.strideBound[parType]; - double const* ptr = _data + _idx.offsetCp(ParticleTypeIndex{ parType }); + double const* ptr = _data + _idx.offsetCp(ParticleTypeIndex{parType}); for (unsigned int i = 0; i < _disc.nPoints; ++i) { for (unsigned int j = 0; j < _disc.nParPoints[parType]; ++j) @@ -2595,10 +2909,9 @@ int GeneralRateModelDG::Exporter::writeOutlet(double* buffer) const return _disc.nComp; } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet #include "model/GeneralRateModelDG-InitialConditions.cpp" #include "model/GeneralRateModelDG-LinearSolver.cpp" - diff --git a/src/libcadet/model/GeneralRateModelDG.hpp b/src/libcadet/model/GeneralRateModelDG.hpp index 145947f93..1f0760932 100644 --- a/src/libcadet/model/GeneralRateModelDG.hpp +++ b/src/libcadet/model/GeneralRateModelDG.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the general rate model (GRM). */ @@ -46,11 +46,11 @@ namespace model namespace parts { - namespace cell - { - struct CellParameters; - } +namespace cell +{ +struct CellParameters; } +} // namespace parts class IDynamicReactionModel; class IParameterStateDependence; @@ -58,12 +58,14 @@ class IParameterStateDependence; /** * @brief General rate model of liquid column chromatography * @details See @cite Guiochon2006, @cite Gu1995, @cite Felinger2004 - * + * * @f[\begin{align} - \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 c_i}{\partial z^2} - \frac{1 - \varepsilon_c}{\varepsilon_c} \frac{3}{r_p} j_{f,i} \\ - \frac{\partial c_{p,i}}{\partial t} + \frac{1 - \varepsilon_p}{\varepsilon_p} \frac{\partial q_{i}}{\partial t} &= D_{p,i} \left( \frac{\partial^2 c_{p,i}}{\partial r^2} + \frac{2}{r} \frac{\partial c_{p,i}}{\partial r} \right) + D_{s,i} \frac{1 - \varepsilon_p}{\varepsilon_p} \left( \frac{\partial^2 q_{i}}{\partial r^2} + \frac{2}{r} \frac{\partial q_{i}}{\partial r} \right) \\ - a \frac{\partial q_i}{\partial t} &= f_{\text{iso}}(c_p, q) -\end{align} @f] + \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 +c_i}{\partial z^2} - \frac{1 - \varepsilon_c}{\varepsilon_c} \frac{3}{r_p} j_{f,i} \\ + \frac{\partial c_{p,i}}{\partial t} + \frac{1 - \varepsilon_p}{\varepsilon_p} \frac{\partial q_{i}}{\partial t} &= +D_{p,i} \left( \frac{\partial^2 c_{p,i}}{\partial r^2} + \frac{2}{r} \frac{\partial c_{p,i}}{\partial r} \right) + +D_{s,i} \frac{1 - \varepsilon_p}{\varepsilon_p} \left( \frac{\partial^2 q_{i}}{\partial r^2} + \frac{2}{r} +\frac{\partial q_{i}}{\partial r} \right) \\ a \frac{\partial q_i}{\partial t} &= f_{\text{iso}}(c_p, q) \end{align} @f] @f[ \begin{align} j_{f,i} = k_{f,i} \left( c_i - c_{p,i} \left(\cdot, \cdot, r_p\right)\right) \end{align} @f] @@ -71,15 +73,15 @@ class IParameterStateDependence; @f[ \begin{align} u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partial z}(t,0) \\ \frac{\partial c_i}{\partial z}(t,L) &= 0 \\ -\varepsilon_p D_{p,i} \frac{\partial c_{p,i}}{\partial r}(\cdot, \cdot, r_p) + (1-\varepsilon_p) D_{s,i} \frac{\partial q_{i}}{\partial r}(\cdot, \cdot, r_p) &= j_{f,i} \\ -\frac{\partial c_{p,i}}{\partial r}(\cdot, \cdot, 0) &= 0 +\varepsilon_p D_{p,i} \frac{\partial c_{p,i}}{\partial r}(\cdot, \cdot, r_p) + (1-\varepsilon_p) D_{s,i} \frac{\partial +q_{i}}{\partial r}(\cdot, \cdot, r_p) &= j_{f,i} \\ \frac{\partial c_{p,i}}{\partial r}(\cdot, \cdot, 0) &= 0 \end{align} @f] - * Methods are described in @cite Breuer2023 (DGSEM discretization), @cite Puttmann2013 @cite Puttmann2016 (forward sensitivities, AD, band compression) + * Methods are described in @cite Breuer2023 (DGSEM discretization), @cite Puttmann2013 @cite Puttmann2016 (forward +sensitivities, AD, band compression) */ class GeneralRateModelDG : public UnitOperationBase { public: - GeneralRateModelDG(UnitOpIdx unitOpIdx); virtual ~GeneralRateModelDG() CADET_NOEXCEPT; @@ -88,60 +90,105 @@ class GeneralRateModelDG : public UnitOperationBase virtual bool usesAD() const CADET_NOEXCEPT; virtual unsigned int requiredADdirs() const CADET_NOEXCEPT; - virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT { return _unitOpIdx; } - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } + virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT + { + return _unitOpIdx; + } + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } virtual void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT; - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual bool canAccumulate() const CADET_NOEXCEPT { return false; } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual bool canAccumulate() const CADET_NOEXCEPT + { + return false; + } - static const char* identifier() { return "GENERAL_RATE_MODEL_DG"; } - virtual const char* unitOperationName() const CADET_NOEXCEPT { return identifier(); } + static const char* identifier() + { + return "GENERAL_RATE_MODEL_DG"; + } + virtual const char* unitOperationName() const CADET_NOEXCEPT + { + return identifier(); + } virtual bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper); virtual bool configure(IParameterProvider& paramProvider); - virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac); + virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac); virtual void useAnalyticJacobian(const bool analyticJac); virtual void reportSolution(ISolutionRecorder& recorder, double const* const solution) const; virtual void reportSolutionStructure(ISolutionRecorder& recorder) const; - virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem); - - virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3); + virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + + virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3); virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState); + const ConstSimulationState& simState); virtual void prepareADvectors(const AdJacobianParams& adJac) const; virtual void applyInitialCondition(const SimulationState& simState) const; virtual void readInitialCondition(IParameterProvider& paramProvider); - virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); void solveBulkTimeDerivativeSystem(const SimulationTime& simTime, double* const rhs); virtual void initializeSensitivityStates(const std::vector& vecSensY) const; virtual void consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem); virtual void leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual bool hasInlet() const CADET_NOEXCEPT { return true; } - virtual bool hasOutlet() const CADET_NOEXCEPT { return true; } + virtual bool hasInlet() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasOutlet() const CADET_NOEXCEPT + { + return true; + } virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT; virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT; @@ -149,14 +196,19 @@ class GeneralRateModelDG : public UnitOperationBase virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT; virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size); - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { } + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) + { + } virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut); - virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret); - virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret); + virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret); + virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* sDot, double* ret); - inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double* ret) + inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double* ret) { multiplyWithJacobian(simTime, simState, yS, 1.0, 0.0, ret); } @@ -176,60 +228,43 @@ class GeneralRateModelDG : public UnitOperationBase #ifdef CADET_BENCHMARK_MODE virtual std::vector benchmarkTimings() const { - return std::vector({ - static_cast(numDofs()), - _timerResidual.totalElapsedTime(), - _timerResidualPar.totalElapsedTime(), - _timerResidualSens.totalElapsedTime(), - _timerResidualSensPar.totalElapsedTime(), - _timerJacobianPar.totalElapsedTime(), - _timerConsistentInit.totalElapsedTime(), - _timerConsistentInitPar.totalElapsedTime(), - _timerLinearSolve.totalElapsedTime(), - _timerFactorize.totalElapsedTime(), - _timerFactorizePar.totalElapsedTime(), - _timerMatVec.totalElapsedTime(), - _timerGmres.totalElapsedTime(), - static_cast(_gmres.numIterations()) - }); + return std::vector({static_cast(numDofs()), _timerResidual.totalElapsedTime(), + _timerResidualPar.totalElapsedTime(), _timerResidualSens.totalElapsedTime(), + _timerResidualSensPar.totalElapsedTime(), _timerJacobianPar.totalElapsedTime(), + _timerConsistentInit.totalElapsedTime(), _timerConsistentInitPar.totalElapsedTime(), + _timerLinearSolve.totalElapsedTime(), _timerFactorize.totalElapsedTime(), + _timerFactorizePar.totalElapsedTime(), _timerMatVec.totalElapsedTime(), + _timerGmres.totalElapsedTime(), static_cast(_gmres.numIterations())}); } virtual char const* const* benchmarkDescriptions() const { static const char* const desc[] = { - "DOFs", - "Residual", - "ResidualPar", - "ResidualSens", - "ResidualSensPar", - "JacobianPar", - "ConsistentInit", - "ConsistentInitPar", - "LinearSolve", - "Factorize", - "FactorizePar", - "MatVec", - "Gmres", - "NumGMRESIter" - }; + "DOFs", "Residual", "ResidualPar", "ResidualSens", "ResidualSensPar", "JacobianPar", + "ConsistentInit", "ConsistentInitPar", "LinearSolve", "Factorize", "FactorizePar", "MatVec", + "Gmres", "NumGMRESIter"}; return desc; } #endif protected: - class Indexer; - int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity); + int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, + bool paramSensitivity); template - int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); + int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, + ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); template - int residualBulk(double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, util::ThreadLocalStorage& threadLocalMem); + int residualBulk(double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, + util::ThreadLocalStorage& threadLocalMem); template - int residualParticle(double t, unsigned int parType, unsigned int colCell, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, util::ThreadLocalStorage& threadLocalMem); + int residualParticle(double t, unsigned int parType, unsigned int colCell, unsigned int secIdx, StateType const* y, + double const* yDot, ResidualType* res, util::ThreadLocalStorage& threadLocalMem); template int residualFlux(double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res); @@ -243,7 +278,8 @@ class GeneralRateModelDG : public UnitOperationBase void setUserdefinedRadialDisc(unsigned int parType); void updateRadialDisc(); - void addTimeDerivativeToJacobianParticleShell(linalg::BandedEigenSparseRowIterator& jac, const Indexer& idxr, double alpha, unsigned int parType); + void addTimeDerivativeToJacobianParticleShell(linalg::BandedEigenSparseRowIterator& jac, const Indexer& idxr, + double alpha, unsigned int parType); unsigned int numAdDirsForJacobian() const CADET_NOEXCEPT; @@ -260,80 +296,96 @@ class GeneralRateModelDG : public UnitOperationBase struct Discretization { - unsigned int nComp; //!< Number of components - unsigned int nCol; //!< Number of column cells - unsigned int polyDeg; //!< polynomial degree of column elements - unsigned int nNodes; //!< Number of nodes per column cell - unsigned int nPoints; //!< Number of discrete column Points - bool exactInt; //!< 1 for exact integration, 0 for inexact LGL quadrature - unsigned int nParType; //!< Number of particle types + unsigned int nComp; //!< Number of components + unsigned int nCol; //!< Number of column cells + unsigned int polyDeg; //!< polynomial degree of column elements + unsigned int nNodes; //!< Number of nodes per column cell + unsigned int nPoints; //!< Number of discrete column Points + bool exactInt; //!< 1 for exact integration, 0 for inexact LGL quadrature + unsigned int nParType; //!< Number of particle types unsigned int* nParCell; //!< Array with number of radial cells in each particle type - unsigned int* nParPointsBeforeType; //!< Array with total number of radial points before a particle type (cumulative sum of nParPoints), additional last element contains total number of particle shells + unsigned int* + nParPointsBeforeType; //!< Array with total number of radial points before a particle type (cumulative sum + //!< of nParPoints), additional last element contains total number of particle shells unsigned int* parPolyDeg; //!< polynomial degree of particle elements - unsigned int* nParNode; //!< Array with number of radial nodes per cell in each particle type + unsigned int* nParNode; //!< Array with number of radial nodes per cell in each particle type unsigned int* nParPoints; //!< Array with number of radial nodes per cell in each particle type - bool* parExactInt; //!< 1 for exact integration, 0 for inexact LGL quadrature for each particle type + bool* parExactInt; //!< 1 for exact integration, 0 for inexact LGL quadrature for each particle type bool* parGSM; //!< specifies whether (single element) Galerkin spectral method should be used in particles - unsigned int* parTypeOffset; //!< Array with offsets (in particle block) to particle type, additional last element contains total number of particle DOFs - unsigned int* nBound; //!< Array with number of bound states for each component and particle type (particle type major ordering) - unsigned int* boundOffset; //!< Array with offset to the first bound state of each component in the solid phase (particle type major ordering) - unsigned int* strideBound; //!< Total number of bound states for each particle type, additional last element contains total number of bound states for all types - unsigned int* nBoundBeforeType; //!< Array with number of bound states before a particle type (cumulative sum of strideBound) + unsigned int* parTypeOffset; //!< Array with offsets (in particle block) to particle type, additional last + //!< element contains total number of particle DOFs + unsigned int* nBound; //!< Array with number of bound states for each component and particle type (particle type + //!< major ordering) + unsigned int* boundOffset; //!< Array with offset to the first bound state of each component in the solid phase + //!< (particle type major ordering) + unsigned int* strideBound; //!< Total number of bound states for each particle type, additional last element + //!< contains total number of bound states for all types + unsigned int* nBoundBeforeType; //!< Array with number of bound states before a particle type (cumulative sum of + //!< strideBound) bool newStaticJac; //!< determines wether static analytical jacobian is to be computed // parameter unsigned int* offsetSurfDiff; //!< particle surface diffusion (may be section and component dependent) - int curSection; //!< current time section index + int curSection; //!< current time section index - const double SurfVolRatioSlab = 1.0; //!< Surface to volume ratio for a slab-shaped particle + const double SurfVolRatioSlab = 1.0; //!< Surface to volume ratio for a slab-shaped particle const double SurfVolRatioCylinder = 2.0; //!< Surface to volume ratio for a cylindrical particle - const double SurfVolRatioSphere = 3.0; //!< Surface to volume ratio for a spherical particle + const double SurfVolRatioSphere = 3.0; //!< Surface to volume ratio for a spherical particle /* DG specifics */ - - active* deltaR; //!< equidistant particle element spacing for each particle type - Eigen::VectorXd* parNodes; //!< Array with positions of nodes in radial reference element for each particle + + active* deltaR; //!< equidistant particle element spacing for each particle type + Eigen::VectorXd* parNodes; //!< Array with positions of nodes in radial reference element for each particle Eigen::MatrixXd* parPolyDerM; //!< Array with polynomial derivative Matrix for each particle - Eigen::MatrixXd* minus_InvMM_ST; //!< equals minus inverse mass matrix times transposed stiffness matrix. Required solely for exact integration DG discretization of particle equation + Eigen::MatrixXd* + minus_InvMM_ST; //!< equals minus inverse mass matrix times transposed stiffness matrix. Required solely for + //!< exact integration DG discretization of particle equation Eigen::VectorXd* parInvWeights; //!< Array with weights for LGL quadrature of size nNodes for each particle - Eigen::MatrixXd* parInvMM; //!< dense inverse mass matrix for exact integration of integrals with metrics, for each particle - Eigen::MatrixXd* parInvMM_Leg; //!< dense inverse mass matrix (Legendre) for exact integration of integral without metric, for each particle + Eigen::MatrixXd* + parInvMM; //!< dense inverse mass matrix for exact integration of integrals with metrics, for each particle + Eigen::MatrixXd* parInvMM_Leg; //!< dense inverse mass matrix (Legendre) for exact integration of integral + //!< without metric, for each particle Eigen::MatrixXd* secondOrderStiffnessM; //!< specific second order stiffness matrix - Eigen::MatrixXd* minus_parInvMM_Ar; //!< inverse mass matrix times specific second order stiffness matrix - Eigen::Vector* Ir; //!< metric part for each particle type and cell, particle type major ordering - Eigen::MatrixXd* Dr; //!< derivative matrices including metrics for each particle type and cell, particle type major ordering - Eigen::VectorXi offsetMetric; //!< offset required to access metric dependent DG operator storage of Ir, Dr -> summed up nCells of all previous parTypes + Eigen::MatrixXd* minus_parInvMM_Ar; //!< inverse mass matrix times specific second order stiffness matrix + Eigen::Vector* + Ir; //!< metric part for each particle type and cell, particle type major ordering + Eigen::MatrixXd* + Dr; //!< derivative matrices including metrics for each particle type and cell, particle type major ordering + Eigen::VectorXi offsetMetric; //!< offset required to access metric dependent DG operator storage of Ir, Dr -> + //!< summed up nCells of all previous parTypes Eigen::MatrixXd* DGjacParDispBlocks; //!< particle dispersion blocks of DG jacobian - Eigen::Vector* g_p; //!< auxiliary variable g = dc_p / dr - Eigen::Vector* g_pSum; //!< auxiliary variable g = sum_{k \in p, s_i} dc_k / dr + Eigen::Vector* g_p; //!< auxiliary variable g = dc_p / dr + Eigen::Vector* g_pSum; //!< auxiliary variable g = sum_{k \in p, s_i} dc_k / dr Eigen::Vector* surfaceFluxParticle; //!< stores the surface flux values for each particle active* localFlux; //!< stores the local (at respective particle) film diffusion flux /** - * @brief allocates memory for DG operators and computes those that are metric independent. Also allocates required containers needed for the DG discretization. - */ - void initializeDG() { + * @brief allocates memory for DG operators and computes those that are metric independent. Also allocates + * required containers needed for the DG discretization. + */ + void initializeDG() + { /* Allocate space for DG operators and containers */ - + newStaticJac = true; // particles - nParNode = new unsigned int [nParType]; - nParPoints = new unsigned int [nParType]; + nParNode = new unsigned int[nParType]; + nParPoints = new unsigned int[nParType]; g_p = new Vector[nParType]; g_pSum = new Vector[nParType]; surfaceFluxParticle = new Vector[nParType]; - parNodes = new VectorXd [nParType]; - parInvWeights = new VectorXd [nParType]; - parInvMM_Leg = new MatrixXd [nParType]; + parNodes = new VectorXd[nParType]; + parInvWeights = new VectorXd[nParType]; + parInvMM_Leg = new MatrixXd[nParType]; parPolyDerM = new MatrixXd[nParType]; localFlux = new active[nComp]; - for (int parType = 0; parType < nParType; parType++) + for (int parType = 0; parType < nParType; parType++) { nParNode[parType] = parPolyDeg[parType] + 1u; nParPoints[parType] = nParNode[parType] * nParCell[parType]; @@ -354,7 +406,8 @@ class GeneralRateModelDG : public UnitOperationBase } offsetMetric = VectorXi::Zero(nParType + 1); - for (int parType = 1; parType <= nParType; parType++) { + for (int parType = 1; parType <= nParType; parType++) + { offsetMetric[parType] += nParCell[parType - 1]; } Dr = new MatrixXd[offsetMetric[nParType]]; @@ -363,8 +416,9 @@ class GeneralRateModelDG : public UnitOperationBase parInvMM = new MatrixXd[offsetMetric[nParType]]; secondOrderStiffnessM = new MatrixXd[nParType]; minus_parInvMM_Ar = new MatrixXd[nParType]; - - /* compute metric independent DG operators for bulk and particles. Note that metric dependent DG operators are computet in updateRadialDisc(). */ + + /* compute metric independent DG operators for bulk and particles. Note that metric dependent DG operators + * are computet in updateRadialDisc(). */ for (int parType = 0; parType < nParType; parType++) { @@ -374,7 +428,8 @@ class GeneralRateModelDG : public UnitOperationBase } } - void initializeDGjac(std::vector parGeomSurfToVol) { + void initializeDGjac(std::vector parGeomSurfToVol) + { // particle jacobian blocks (each is unique) DGjacParDispBlocks = new MatrixXd[std::accumulate(nParCell, nParCell + nParType, 0)]; @@ -384,49 +439,66 @@ class GeneralRateModelDG : public UnitOperationBase for (unsigned int block = 0; block < nParCell[type]; block++) { if (parGSM[type]) - DGjacParDispBlocks[offsetMetric[type] + block] = GSMjacobianParDispBlock(type, parGeomSurfToVol[type]); + DGjacParDispBlocks[offsetMetric[type] + block] = + GSMjacobianParDispBlock(type, parGeomSurfToVol[type]); else - DGjacParDispBlocks[offsetMetric[type] + block] = DGjacobianParDispBlock(block + 1u, type, parGeomSurfToVol[type]); + DGjacParDispBlocks[offsetMetric[type] + block] = + DGjacobianParDispBlock(block + 1u, type, parGeomSurfToVol[type]); } } } private: - /** * @brief calculates the DG Jacobian auxiliary block * @param [in] exInt true if exact integration DG scheme * @param [in] cellIdx cell index */ - MatrixXd getParGBlock(unsigned int cellIdx, unsigned int parType) { + MatrixXd getParGBlock(unsigned int cellIdx, unsigned int parType) + { // Auxiliary Block [ d g(c) / d c ], additionally depends on boundary entries of neighbouring cells MatrixXd gBlock = MatrixXd::Zero(nParNode[parType], nParNode[parType] + 2); gBlock.block(0, 1, nParNode[parType], nParNode[parType]) = parPolyDerM[parType]; - if (parExactInt[parType]) { - if (cellIdx == 0 || cellIdx == nParCell[parType] + 1) { // cellIdx out of bounds + if (parExactInt[parType]) + { + if (cellIdx == 0 || cellIdx == nParCell[parType] + 1) + { // cellIdx out of bounds return MatrixXd::Zero(nParNode[parType], nParNode[parType] + 2); } - if (cellIdx != 1 && cellIdx != nParCell[parType]) { // inner cell - gBlock.block(0, 0, nParNode[parType], 1) -= 0.5 * parInvMM_Leg[parType].block(0, 0, nParNode[parType], 1); - gBlock.block(0, 1, nParNode[parType], 1) += 0.5 * parInvMM_Leg[parType].block(0, 0, nParNode[parType], 1); - gBlock.block(0, nParNode[parType], nParNode[parType], 1) -= 0.5 * parInvMM_Leg[parType].block(0, nParNode[parType] - 1, nParNode[parType], 1); - gBlock.block(0, nParNode[parType] + 1, nParNode[parType], 1) += 0.5 * parInvMM_Leg[parType].block(0, nParNode[parType] - 1, nParNode[parType], 1); + if (cellIdx != 1 && cellIdx != nParCell[parType]) + { // inner cell + gBlock.block(0, 0, nParNode[parType], 1) -= + 0.5 * parInvMM_Leg[parType].block(0, 0, nParNode[parType], 1); + gBlock.block(0, 1, nParNode[parType], 1) += + 0.5 * parInvMM_Leg[parType].block(0, 0, nParNode[parType], 1); + gBlock.block(0, nParNode[parType], nParNode[parType], 1) -= + 0.5 * parInvMM_Leg[parType].block(0, nParNode[parType] - 1, nParNode[parType], 1); + gBlock.block(0, nParNode[parType] + 1, nParNode[parType], 1) += + 0.5 * parInvMM_Leg[parType].block(0, nParNode[parType] - 1, nParNode[parType], 1); } - else if (cellIdx == 1u) { // left boundary cell + else if (cellIdx == 1u) + { // left boundary cell if (cellIdx == nParCell[parType]) // special case one cell return gBlock * 2.0 / static_cast(deltaR[offsetMetric[parType] + (cellIdx - 1)]); - gBlock.block(0, nParNode[parType], nParNode[parType], 1) -= 0.5 * parInvMM_Leg[parType].block(0, nParNode[parType] - 1, nParNode[parType], 1); - gBlock.block(0, nParNode[parType] + 1, nParNode[parType], 1) += 0.5 * parInvMM_Leg[parType].block(0, nParNode[parType] - 1, nParNode[parType], 1); + gBlock.block(0, nParNode[parType], nParNode[parType], 1) -= + 0.5 * parInvMM_Leg[parType].block(0, nParNode[parType] - 1, nParNode[parType], 1); + gBlock.block(0, nParNode[parType] + 1, nParNode[parType], 1) += + 0.5 * parInvMM_Leg[parType].block(0, nParNode[parType] - 1, nParNode[parType], 1); } - else if (cellIdx == nParCell[parType]) { // right boundary cell - gBlock.block(0, 0, nParNode[parType], 1) -= 0.5 * parInvMM_Leg[parType].block(0, 0, nParNode[parType], 1); - gBlock.block(0, 1, nParNode[parType], 1) += 0.5 * parInvMM_Leg[parType].block(0, 0, nParNode[parType], 1); + else if (cellIdx == nParCell[parType]) + { // right boundary cell + gBlock.block(0, 0, nParNode[parType], 1) -= + 0.5 * parInvMM_Leg[parType].block(0, 0, nParNode[parType], 1); + gBlock.block(0, 1, nParNode[parType], 1) += + 0.5 * parInvMM_Leg[parType].block(0, 0, nParNode[parType], 1); } gBlock *= 2.0 / static_cast(deltaR[offsetMetric[parType] + (cellIdx - 1)]); } - else { - // inexact integration not maintained due to inferior performance. Code is part of calcParticleCollocationDGSEMJacobian() + else + { + // inexact integration not maintained due to inferior performance. Code is part of + // calcParticleCollocationDGSEMJacobian() } return gBlock; @@ -438,37 +510,50 @@ class GeneralRateModelDG : public UnitOperationBase * @param [in] middleG neighbour auxiliary block * @param [in] rightG neighbour auxiliary block */ - Eigen::MatrixXd parAuxBlockGstar(unsigned int cellIdx, unsigned int parType, MatrixXd leftG, MatrixXd middleG, MatrixXd rightG) { + Eigen::MatrixXd parAuxBlockGstar(unsigned int cellIdx, unsigned int parType, MatrixXd leftG, MatrixXd middleG, + MatrixXd rightG) + { - // auxiliary block [ d g^* / d c ], depends on whole previous and subsequent cell plus first entries of subsubsequent cells + // auxiliary block [ d g^* / d c ], depends on whole previous and subsequent cell plus first entries of + // subsubsequent cells MatrixXd gStarDC = MatrixXd::Zero(nParNode[parType], 3 * nParNode[parType] + 2); // NOTE: N = polyDeg - // indices gStarDC : 0 , 1 , ..., nNodes; nNodes+1, ..., 2 * nNodes; 2*nNodes+1, ..., 3 * nNodes; 3*nNodes+1 - // derivative index j : -(N+1)-1, -(N+1),... , -1 ; 0 , ..., N ; N + 1 , ..., 2N + 2 ; 2(N+1) +1 - // auxiliary block [d g^* / d c] - if (cellIdx != 1) { - gStarDC.block(0, nParNode[parType], 1, nParNode[parType] + 2) += middleG.block(0, 0, 1, nParNode[parType] + 2); - gStarDC.block(0, 0, 1, nParNode[parType] + 2) += leftG.block(nParNode[parType] - 1, 0, 1, nParNode[parType] + 2); + // indices gStarDC : 0 , 1 , ..., nNodes; nNodes+1, ..., 2 * nNodes; 2*nNodes+1, ..., 3 * + // nNodes; 3*nNodes+1 derivative index j : -(N+1)-1, -(N+1),... , -1 ; 0 , ..., N ; N + // + 1 , ..., 2N + 2 ; 2(N+1) +1 auxiliary block [d g^* / d c] + if (cellIdx != 1) + { + gStarDC.block(0, nParNode[parType], 1, nParNode[parType] + 2) += + middleG.block(0, 0, 1, nParNode[parType] + 2); + gStarDC.block(0, 0, 1, nParNode[parType] + 2) += + leftG.block(nParNode[parType] - 1, 0, 1, nParNode[parType] + 2); } - if (cellIdx != nParCell[parType]) { - gStarDC.block(nParNode[parType] - 1, nParNode[parType], 1, nParNode[parType] + 2) += middleG.block(nParNode[parType] - 1, 0, 1, nParNode[parType] + 2); - gStarDC.block(nParNode[parType] - 1, 2 * nParNode[parType], 1, nParNode[parType] + 2) += rightG.block(0, 0, 1, nParNode[parType] + 2); + if (cellIdx != nParCell[parType]) + { + gStarDC.block(nParNode[parType] - 1, nParNode[parType], 1, nParNode[parType] + 2) += + middleG.block(nParNode[parType] - 1, 0, 1, nParNode[parType] + 2); + gStarDC.block(nParNode[parType] - 1, 2 * nParNode[parType], 1, nParNode[parType] + 2) += + rightG.block(0, 0, 1, nParNode[parType] + 2); } gStarDC *= 0.5; return gStarDC; } - Eigen::MatrixXd getParBMatrix(int parType, int cell, double parGeomSurfToVol) { + Eigen::MatrixXd getParBMatrix(int parType, int cell, double parGeomSurfToVol) + { // also known as "lifting" matrix and includes metric dependent terms for particle discretization MatrixXd B = MatrixXd::Zero(nParNode[parType], nParNode[parType]); - if (parGeomSurfToVol == SurfVolRatioSlab) { + if (parGeomSurfToVol == SurfVolRatioSlab) + { B(0, 0) = -1.0; B(nParNode[parType] - 1, nParNode[parType] - 1) = 1.0; } - else { + else + { B(0, 0) = -static_cast(Ir[offsetMetric[parType] + (cell - 1)][0]); - B(nParNode[parType] - 1, nParNode[parType] - 1) = static_cast(Ir[offsetMetric[parType] + (cell - 1)][nParNode[parType] - 1]); + B(nParNode[parType] - 1, nParNode[parType] - 1) = + static_cast(Ir[offsetMetric[parType] + (cell - 1)][nParNode[parType] - 1]); } return B; @@ -478,15 +563,19 @@ class GeneralRateModelDG : public UnitOperationBase * @param [in] parType particle type index * @param [in] parGeomSurfToVol particle geometry */ - Eigen::MatrixXd GSMjacobianParDispBlock(unsigned int parType, double parGeomSurfToVol) { + Eigen::MatrixXd GSMjacobianParDispBlock(unsigned int parType, double parGeomSurfToVol) + { MatrixXd dispBlock; - // We have to match the DGSEM interface, where the dispersion block [ d RHS_disp / d c ] depends on whole previous and subsequent cell plus first entries of subsubsequent cells + // We have to match the DGSEM interface, where the dispersion block [ d RHS_disp / d c ] depends on whole + // previous and subsequent cell plus first entries of subsubsequent cells dispBlock = MatrixXd::Zero(nParNode[parType], 3 * nParNode[parType] + 2); - dispBlock.block(0, nParNode[parType] + 1, nParNode[parType], nParNode[parType]) = minus_parInvMM_Ar[parType]; - dispBlock *= 2.0 / static_cast(deltaR[offsetMetric[parType]]) * 2.0 / static_cast(deltaR[offsetMetric[parType]]); + dispBlock.block(0, nParNode[parType] + 1, nParNode[parType], nParNode[parType]) = + minus_parInvMM_Ar[parType]; + dispBlock *= 2.0 / static_cast(deltaR[offsetMetric[parType]]) * 2.0 / + static_cast(deltaR[offsetMetric[parType]]); return -dispBlock; // *-1 for residual } @@ -496,29 +585,35 @@ class GeneralRateModelDG : public UnitOperationBase * @param [in] parType particle type index * @param [in] parGeomSurfToVol particle geometry */ - Eigen::MatrixXd DGjacobianParDispBlock(unsigned int cellIdx, unsigned int parType, double parGeomSurfToVol) { + Eigen::MatrixXd DGjacobianParDispBlock(unsigned int cellIdx, unsigned int parType, double parGeomSurfToVol) + { MatrixXd dispBlock; - // Inner dispersion block [ d RHS_disp / d c ], depends on whole previous and subsequent cell plus first entries of subsubsequent cells + // Inner dispersion block [ d RHS_disp / d c ], depends on whole previous and subsequent cell plus first + // entries of subsubsequent cells dispBlock = MatrixXd::Zero(nParNode[parType], 3 * nParNode[parType] + 2); if (parExactInt[parType]) { MatrixXd B = getParBMatrix(parType, cellIdx, parGeomSurfToVol); // "Lifting" matrix - MatrixXd gBlock = getParGBlock(cellIdx, parType); // current cell auxiliary block matrix - MatrixXd gStarDC = parAuxBlockGstar(cellIdx, parType, getParGBlock(cellIdx - 1, parType), gBlock, getParGBlock(cellIdx + 1, parType)); // Numerical flux block + MatrixXd gBlock = getParGBlock(cellIdx, parType); // current cell auxiliary block matrix + MatrixXd gStarDC = parAuxBlockGstar(cellIdx, parType, getParGBlock(cellIdx - 1, parType), gBlock, + getParGBlock(cellIdx + 1, parType)); // Numerical flux block if (parGeomSurfToVol != SurfVolRatioSlab) // weak form DGSEM required - dispBlock.block(0, nParNode[parType], nParNode[parType], nParNode[parType] + 2) = minus_InvMM_ST[offsetMetric[parType] + (cellIdx - 1)] * gBlock; + dispBlock.block(0, nParNode[parType], nParNode[parType], nParNode[parType] + 2) = + minus_InvMM_ST[offsetMetric[parType] + (cellIdx - 1)] * gBlock; else // strong form DGSEM - dispBlock.block(0, nParNode[parType], nParNode[parType], nParNode[parType] + 2) = (parPolyDerM[parType] - parInvMM[offsetMetric[parType] + (cellIdx - 1)] * B) * gBlock; - + dispBlock.block(0, nParNode[parType], nParNode[parType], nParNode[parType] + 2) = + (parPolyDerM[parType] - parInvMM[offsetMetric[parType] + (cellIdx - 1)] * B) * gBlock; + dispBlock += parInvMM[offsetMetric[parType] + (cellIdx - 1)] * B * gStarDC; dispBlock *= 2.0 / static_cast(deltaR[offsetMetric[parType] + (cellIdx - 1)]); } else { - // inexact integration is not maintained due to inferior performance. Code is in calcParticleCollocationDGSEMJacobian + // inexact integration is not maintained due to inferior performance. Code is in + // calcParticleCollocationDGSEMJacobian } return -dispBlock; // *-1 for residual @@ -543,32 +638,34 @@ class GeneralRateModelDG : public UnitOperationBase UserDefined }; - Discretization _disc; //!< Discretization info + Discretization _disc; //!< Discretization info std::vector _hasSurfaceDiffusion; //!< Determines whether surface diffusion is present in each particle type -// IExternalFunction* _extFun; //!< External function (owned by library user) + // IExternalFunction* _extFun; //!< External function (owned by library user) - parts::AxialConvectionDispersionOperatorBaseDG _convDispOp; //!< Convection dispersion operator base for interstitial volume transport + parts::AxialConvectionDispersionOperatorBaseDG + _convDispOp; //!< Convection dispersion operator base for interstitial volume transport IDynamicReactionModel* _dynReactionBulk; //!< Dynamic reactions in the bulk volume cadet::linalg::EigenSolverBase* _linearSolver; //!< Linear solver - Eigen::SparseMatrix _globalJac; //!< static part of global Jacobian + Eigen::SparseMatrix _globalJac; //!< static part of global Jacobian Eigen::SparseMatrix _globalJacDisc; //!< global Jacobian with time derivative from BDF method - //MatrixXd FDJac; // test purpose FD Jacobian + // MatrixXd FDJac; // test purpose FD Jacobian Eigen::MatrixXd _jacInlet; //!< Jacobian inlet DOF block matrix connects inlet DOFs to first bulk cells - active _colPorosity; //!< Column porosity (external porosity) \f$ \varepsilon_c \f$ + active _colPorosity; //!< Column porosity (external porosity) \f$ \varepsilon_c \f$ std::vector _parRadius; //!< Particle radius \f$ r_p \f$ bool _singleParRadius; std::vector _parCoreRadius; //!< Particle core radius \f$ r_c \f$ bool _singleParCoreRadius; std::vector _parPorosity; //!< Particle porosity (internal porosity) \f$ \varepsilon_p \f$ bool _singleParPorosity; - std::vector _parTypeVolFrac; //!< Volume fraction of each particle type + std::vector _parTypeVolFrac; //!< Volume fraction of each particle type std::vector _parDiscType; //!< Particle discretization mode - std::vector _parDiscVector; //!< Particle discretization shell edges - std::vector _parGeomSurfToVol; //!< Particle surface to volume ratio factor (i.e., 3.0 for spherical, 2.0 for cylindrical, 1.0 for hexahedral) + std::vector _parDiscVector; //!< Particle discretization shell edges + std::vector _parGeomSurfToVol; //!< Particle surface to volume ratio factor (i.e., 3.0 for spherical, 2.0 + //!< for cylindrical, 1.0 for hexahedral) // Vectorial parameters std::vector _filmDiffusion; //!< Film diffusion coefficient \f$ k_f \f$ @@ -579,26 +676,29 @@ class GeneralRateModelDG : public UnitOperationBase MultiplexMode _parSurfDiffusionMode; std::vector _poreAccessFactor; //!< Pore accessibility factor \f$ F_{\text{acc}} \f$ MultiplexMode _poreAccessFactorMode; - std::vector _parDepSurfDiffusion; //!< Parameter dependencies for particle surface diffusion - bool _singleParDepSurfDiffusion; //!< Determines whether a single parameter dependence for particle surface diffusion is used + std::vector + _parDepSurfDiffusion; //!< Parameter dependencies for particle surface diffusion + bool _singleParDepSurfDiffusion; //!< Determines whether a single parameter dependence for particle surface + //!< diffusion is used bool _hasParDepSurfDiffusion; //!< Determines whether particle surface diffusion parameter dependencies are present - bool _axiallyConstantParTypeVolFrac; //!< Determines whether particle type volume fraction is homogeneous across axial coordinate - bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used - unsigned int _jacobianAdDirs; //!< Number of AD seed vectors required for Jacobian computation + bool _axiallyConstantParTypeVolFrac; //!< Determines whether particle type volume fraction is homogeneous across + //!< axial coordinate + bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used + unsigned int _jacobianAdDirs; //!< Number of AD seed vectors required for Jacobian computation - std::vector _parCellSize; //!< Particle shell size - std::vector _parCenterRadius; //!< Particle node-centered position for each particle node + std::vector _parCellSize; //!< Particle shell size + std::vector _parCenterRadius; //!< Particle node-centered position for each particle node std::vector _parOuterSurfAreaPerVolume; //!< Particle shell outer sphere surface to volume ratio std::vector _parInnerSurfAreaPerVolume; //!< Particle shell inner sphere surface to volume ratio bool _factorizeJacobian; //!< Determines whether the Jacobian needs to be factorized double* _tempState; //!< Temporary storage with the size of the state vector or larger if binding models require it - std::vector _initC; //!< Liquid bulk phase initial conditions - std::vector _initCp; //!< Liquid particle phase initial conditions - std::vector _initQ; //!< Solid phase initial conditions - std::vector _initState; //!< Initial conditions for state vector if given + std::vector _initC; //!< Liquid bulk phase initial conditions + std::vector _initCp; //!< Liquid particle phase initial conditions + std::vector _initQ; //!< Solid phase initial conditions + std::vector _initState; //!< Initial conditions for state vector if given std::vector _initStateDot; //!< Initial conditions for time derivative BENCH_TIMER(_timerResidual) @@ -617,40 +717,109 @@ class GeneralRateModelDG : public UnitOperationBase class Indexer { public: - Indexer(const Discretization& disc) : _disc(disc) { } + Indexer(const Discretization& disc) : _disc(disc) + { + } // Strides - inline int strideColNode() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideColCell() const CADET_NOEXCEPT { return static_cast(_disc.nNodes * strideColNode()); } - inline int strideColComp() const CADET_NOEXCEPT { return 1; } + inline int strideColNode() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideColCell() const CADET_NOEXCEPT + { + return static_cast(_disc.nNodes * strideColNode()); + } + inline int strideColComp() const CADET_NOEXCEPT + { + return 1; + } - inline int strideParComp() const CADET_NOEXCEPT { return 1; } - inline int strideParLiquid() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideParBound(int parType) const CADET_NOEXCEPT { return static_cast(_disc.strideBound[parType]); } - inline int strideParNode(int parType) const CADET_NOEXCEPT { return strideParLiquid() + strideParBound(parType); } - inline int strideParShell(int parType) const CADET_NOEXCEPT { return strideParNode(parType) * _disc.nParNode[parType]; } - inline int strideParBlock(int parType) const CADET_NOEXCEPT { return static_cast(_disc.nParPoints[parType]) * strideParNode(parType); } + inline int strideParComp() const CADET_NOEXCEPT + { + return 1; + } + inline int strideParLiquid() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideParBound(int parType) const CADET_NOEXCEPT + { + return static_cast(_disc.strideBound[parType]); + } + inline int strideParNode(int parType) const CADET_NOEXCEPT + { + return strideParLiquid() + strideParBound(parType); + } + inline int strideParShell(int parType) const CADET_NOEXCEPT + { + return strideParNode(parType) * _disc.nParNode[parType]; + } + inline int strideParBlock(int parType) const CADET_NOEXCEPT + { + return static_cast(_disc.nParPoints[parType]) * strideParNode(parType); + } // Offsets - inline int offsetC() const CADET_NOEXCEPT { return _disc.nComp; } - inline int offsetCp() const CADET_NOEXCEPT { return _disc.nComp * _disc.nPoints + offsetC(); } - inline int offsetCp(ParticleTypeIndex pti) const CADET_NOEXCEPT { return offsetCp() + _disc.parTypeOffset[pti.value]; } - inline int offsetCp(ParticleTypeIndex pti, ParticleIndex pi) const CADET_NOEXCEPT { return offsetCp() + _disc.parTypeOffset[pti.value] + strideParBlock(pti.value) * pi.value; } - inline int offsetBoundComp(ParticleTypeIndex pti, ComponentIndex comp) const CADET_NOEXCEPT { return _disc.boundOffset[pti.value * _disc.nComp + comp.value]; } + inline int offsetC() const CADET_NOEXCEPT + { + return _disc.nComp; + } + inline int offsetCp() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nPoints + offsetC(); + } + inline int offsetCp(ParticleTypeIndex pti) const CADET_NOEXCEPT + { + return offsetCp() + _disc.parTypeOffset[pti.value]; + } + inline int offsetCp(ParticleTypeIndex pti, ParticleIndex pi) const CADET_NOEXCEPT + { + return offsetCp() + _disc.parTypeOffset[pti.value] + strideParBlock(pti.value) * pi.value; + } + inline int offsetBoundComp(ParticleTypeIndex pti, ComponentIndex comp) const CADET_NOEXCEPT + { + return _disc.boundOffset[pti.value * _disc.nComp + comp.value]; + } // Return pointer to first element of state variable in state vector - template inline real_t* c(real_t* const data) const { return data + offsetC(); } - template inline real_t const* c(real_t const* const data) const { return data + offsetC(); } + template inline real_t* c(real_t* const data) const + { + return data + offsetC(); + } + template inline real_t const* c(real_t const* const data) const + { + return data + offsetC(); + } - template inline real_t* cp(real_t* const data) const { return data + offsetCp(); } - template inline real_t const* cp(real_t const* const data) const { return data + offsetCp(); } + template inline real_t* cp(real_t* const data) const + { + return data + offsetCp(); + } + template inline real_t const* cp(real_t const* const data) const + { + return data + offsetCp(); + } - template inline real_t* q(real_t* const data) const { return data + offsetCp() + strideParLiquid(); } - template inline real_t const* q(real_t const* const data) const { return data + offsetCp() + strideParLiquid(); } + template inline real_t* q(real_t* const data) const + { + return data + offsetCp() + strideParLiquid(); + } + template inline real_t const* q(real_t const* const data) const + { + return data + offsetCp() + strideParLiquid(); + } // Return specific variable in state vector - template inline real_t& c(real_t* const data, unsigned int point, unsigned int comp) const { return data[offsetC() + comp + point * strideColNode()]; } - template inline const real_t& c(real_t const* const data, unsigned int point, unsigned int comp) const { return data[offsetC() + comp + point * strideColNode()]; } + template inline real_t& c(real_t* const data, unsigned int point, unsigned int comp) const + { + return data[offsetC() + comp + point * strideColNode()]; + } + template + inline const real_t& c(real_t const* const data, unsigned int point, unsigned int comp) const + { + return data[offsetC() + comp + point * strideColNode()]; + } protected: const Discretization& _disc; @@ -659,26 +828,73 @@ class GeneralRateModelDG : public UnitOperationBase class Exporter : public ISolutionExporter { public: - - Exporter(const Discretization& disc, const GeneralRateModelDG& model, double const* data) : _disc(disc), _idx(disc), _model(model), _data(data) { } + Exporter(const Discretization& disc, const GeneralRateModelDG& model, double const* data) + : _disc(disc), _idx(disc), _model(model), _data(data) + { + } Exporter(const Discretization&& disc, const GeneralRateModelDG& model, double const* data) = delete; - virtual bool hasParticleFlux() const CADET_NOEXCEPT { return false; } - virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT { return true; } - virtual bool hasSolidPhase() const CADET_NOEXCEPT { return _disc.strideBound[_disc.nParType] > 0; } - virtual bool hasVolume() const CADET_NOEXCEPT { return false; } - virtual bool isParticleLumped() const CADET_NOEXCEPT { return false; } - virtual bool hasPrimaryExtent() const CADET_NOEXCEPT { return true; } - - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } - virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT { return _disc.nPoints; } - virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numParticleTypes() const CADET_NOEXCEPT { return _disc.nParType; } - virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT { return _disc.nParPoints[parType]; } - virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT { return _disc.strideBound[parType]; } - virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT { return _disc.nComp * _disc.nPoints; } + virtual bool hasParticleFlux() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasSolidPhase() const CADET_NOEXCEPT + { + return _disc.strideBound[_disc.nParType] > 0; + } + virtual bool hasVolume() const CADET_NOEXCEPT + { + return false; + } + virtual bool isParticleLumped() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasPrimaryExtent() const CADET_NOEXCEPT + { + return true; + } + + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } + virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT + { + return _disc.nPoints; + } + virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numParticleTypes() const CADET_NOEXCEPT + { + return _disc.nParType; + } + virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.nParPoints[parType]; + } + virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.strideBound[parType]; + } + virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nPoints; + } virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT { unsigned int nDofPerParType = 0; @@ -686,7 +902,10 @@ class GeneralRateModelDG : public UnitOperationBase nDofPerParType += _disc.nParPoints[i]; return _disc.nPoints * nDofPerParType * _disc.nComp; } - virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return _disc.nPoints * _disc.nParPoints[parType] * _disc.nComp; } + virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.nPoints * _disc.nParPoints[parType] * _disc.nComp; + } virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT { unsigned int nDofPerParType = 0; @@ -694,9 +913,18 @@ class GeneralRateModelDG : public UnitOperationBase nDofPerParType += _disc.nParPoints[i] * _disc.strideBound[i]; return _disc.nPoints * nDofPerParType; } - virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return _disc.nPoints * _disc.nParPoints[parType] * _disc.strideBound[parType]; } - virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT { return 0; } + virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.nPoints * _disc.nParPoints[parType] * _disc.strideBound[parType]; + } + virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT + { + return 0; + } virtual int writeMobilePhase(double* buffer) const; virtual int writeSolidPhase(double* buffer) const; @@ -705,39 +933,56 @@ class GeneralRateModelDG : public UnitOperationBase virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const; virtual int writeParticleFlux(double* buffer) const; virtual int writeParticleFlux(unsigned int parType, double* buffer) const; - virtual int writeVolume(double* buffer) const { return 0; } + virtual int writeVolume(double* buffer) const + { + return 0; + } virtual int writeInlet(unsigned int port, double* buffer) const; virtual int writeInlet(double* buffer) const; virtual int writeOutlet(unsigned int port, double* buffer) const; virtual int writeOutlet(double* buffer) const; /** - * @brief calculates, writes the physical axial/column coordinates of the DG discretization with double! interface nodes - */ + * @brief calculates, writes the physical axial/column coordinates of the DG discretization with double! + * interface nodes + */ virtual int writePrimaryCoordinates(double* coords) const { - for (unsigned int i = 0; i < _disc.nCol; i++) { - for (unsigned int j = 0; j < _disc.nNodes; j++) { - // mapping - coords[i * _disc.nNodes + j] = _model._convDispOp.cellLeftBound(i) + 0.5 * (static_cast(_model._convDispOp.columnLength()) / static_cast(_disc.nCol)) * (1.0 + _model._convDispOp.LGLnodes()[j]); + for (unsigned int i = 0; i < _disc.nCol; i++) + { + for (unsigned int j = 0; j < _disc.nNodes; j++) + { + // mapping + coords[i * _disc.nNodes + j] = + _model._convDispOp.cellLeftBound(i) + + 0.5 * + (static_cast(_model._convDispOp.columnLength()) / static_cast(_disc.nCol)) * + (1.0 + _model._convDispOp.LGLnodes()[j]); } } return _disc.nPoints; } - virtual int writeSecondaryCoordinates(double* coords) const { return 0; } + virtual int writeSecondaryCoordinates(double* coords) const + { + return 0; + } /** - * @brief calculates, writes the physical radial/particle coordinates of the DG discretization with double! interface nodes - */ + * @brief calculates, writes the physical radial/particle coordinates of the DG discretization with double! + * interface nodes + */ virtual int writeParticleCoordinates(unsigned int parType, double* coords) const { active const* const pcr = _model._parCenterRadius.data() + _disc.offsetMetric[parType]; // Note that the DG particle shells are oppositely ordered compared to the FV particle shells - for (unsigned int par = 0; par < _disc.nParPoints[parType]; par++) { + for (unsigned int par = 0; par < _disc.nParPoints[parType]; par++) + { unsigned int cell = std::floor(par / _disc.nParNode[parType]); - double r_L = static_cast(pcr[cell]) - 0.5 * static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell]); - coords[par] = r_L + 0.5 * static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell]) * (1.0 + _disc.parNodes[parType][par % _disc.nParNode[parType]]); + double r_L = static_cast(pcr[cell]) - + 0.5 * static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell]); + coords[par] = r_L + 0.5 * static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell]) * + (1.0 + _disc.parNodes[parType][par % _disc.nParNode[parType]]); } return _disc.nParPoints[parType]; @@ -750,15 +995,19 @@ class GeneralRateModelDG : public UnitOperationBase double const* const _data; }; -// =========================================================================================================================================================== // -// ======================================== DG functions to compute particle discretization ================================================== // -// =========================================================================================================================================================== // + // =========================================================================================================================================================== + // // + // ======================================== DG functions to compute particle discretization + // ================================================== // + // =========================================================================================================================================================== + // // /** * @brief promotes doubles to actives * @detail promotes consecutive doubles to consecutive actives (with zero gradients) based on input double pointer */ - void vectorPromoter(double* state, const unsigned int nVals) { + void vectorPromoter(double* state, const unsigned int nVals) + { const int nDirs = ad::getDirections(); const int stride = (1 + nDirs); @@ -773,80 +1022,117 @@ class GeneralRateModelDG : public UnitOperationBase } } - template - void applyParInvMap(Eigen::Map, 0, InnerStride<>>& state, unsigned int parType) { - for (int cell = 0; cell < _disc.nParCell[parType]; cell++) { - state.segment(cell * _disc.nParNode[parType], _disc.nParNode[parType]) *= 2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell]) * 2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell]); + template + void applyParInvMap(Eigen::Map, 0, InnerStride<>>& state, unsigned int parType) + { + for (int cell = 0; cell < _disc.nParCell[parType]; cell++) + { + state.segment(cell * _disc.nParNode[parType], _disc.nParNode[parType]) *= + 2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell]) * 2.0 / + static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell]); } } - template - void parGSMVolumeIntegral(const int parType, Eigen::Map, 0, InnerStride>& state, Eigen::Map, 0, InnerStride>& stateDer) { + template + void parGSMVolumeIntegral(const int parType, + Eigen::Map, 0, InnerStride>& state, + Eigen::Map, 0, InnerStride>& stateDer) + { int nNodes = _disc.nParNode[parType]; - stateDer.segment(0, nNodes) - -= (_disc.minus_parInvMM_Ar[parType].template cast() * state.segment(0, nNodes)).template cast(); + stateDer.segment(0, nNodes) -= + (_disc.minus_parInvMM_Ar[parType].template cast() * state.segment(0, nNodes)) + .template cast(); } - template - void parVolumeIntegral(const int parType, const bool aux, Eigen::Map, 0, InnerStride>& state, Eigen::Map, 0, InnerStride>& stateDer) { + template + void parVolumeIntegral(const int parType, const bool aux, + Eigen::Map, 0, InnerStride>& state, + Eigen::Map, 0, InnerStride>& stateDer) + { int nNodes = _disc.nParNode[parType]; /* no additional metric term for auxiliary equation or particle equation with exact integration scheme -> res = - D * (d_p * c^p + invBeta_p sum_mi d_s c^s) */ - if (aux || (_disc.parExactInt[parType] && _parGeomSurfToVol[parType] == _disc.SurfVolRatioSlab)) { + if (aux || (_disc.parExactInt[parType] && _parGeomSurfToVol[parType] == _disc.SurfVolRatioSlab)) + { // comp-cell-node state vector: use of Eigen lib performance - for (unsigned int Cell = 0; Cell < _disc.nParCell[parType]; Cell++) { - stateDer.segment(Cell * nNodes, nNodes) - -= (_disc.parPolyDerM[parType].template cast() * state.segment(Cell * nNodes, nNodes)).template cast(); + for (unsigned int Cell = 0; Cell < _disc.nParCell[parType]; Cell++) + { + stateDer.segment(Cell * nNodes, nNodes) -= + (_disc.parPolyDerM[parType].template cast() * state.segment(Cell * nNodes, nNodes)) + .template cast(); } } - else if (_disc.parExactInt[parType] && _parGeomSurfToVol[parType] != _disc.SurfVolRatioSlab) { + else if (_disc.parExactInt[parType] && _parGeomSurfToVol[parType] != _disc.SurfVolRatioSlab) + { // comp-cell-node state vector: use of Eigen lib performance - for (unsigned int Cell = 0; Cell < _disc.nParCell[parType]; Cell++) { - stateDer.segment(Cell * nNodes, nNodes) - -= (_disc.minus_InvMM_ST[_disc.offsetMetric[parType] + Cell].template cast() * state.segment(Cell * nNodes, nNodes)).template cast(); + for (unsigned int Cell = 0; Cell < _disc.nParCell[parType]; Cell++) + { + stateDer.segment(Cell * nNodes, nNodes) -= + (_disc.minus_InvMM_ST[_disc.offsetMetric[parType] + Cell].template cast() * + state.segment(Cell * nNodes, nNodes)) + .template cast(); } } /* include metrics for main particle equation -> res = - D * (d_p * c^p + invBeta_p sum_mi d_s c^s) */ - else { // inexact integration, main equation + else + { // inexact integration, main equation int Cell0 = 0; // auxiliary variable to distinguish special case // special case for non slab-shaped particles without core => r(xi_0) = 0 - if (_parGeomSurfToVol[parType] != _disc.SurfVolRatioSlab && _parCoreRadius[parType] == 0.0) { + if (_parGeomSurfToVol[parType] != _disc.SurfVolRatioSlab && _parCoreRadius[parType] == 0.0) + { Cell0 = 1; // compute volume integral except for boundary node - stateDer.segment(1, nNodes - 1) -= (_disc.Dr[_disc.offsetMetric[parType]].block(1, 1, nNodes - 1, nNodes - 1).template cast() * state.segment(1, nNodes - 1)).template cast(); + stateDer.segment(1, nNodes - 1) -= (_disc.Dr[_disc.offsetMetric[parType]] + .block(1, 1, nNodes - 1, nNodes - 1) + .template cast() * + state.segment(1, nNodes - 1)) + .template cast(); // estimate volume integral for boundary node: sum_{j=1}^N state_j * w_j * D_{j,0} * r_j stateDer[0] += static_cast( - (state.segment(1, nNodes - 1).array() - * _disc.parInvWeights[parType].segment(1, nNodes - 1).array().cwiseInverse().template cast() - * _disc.parPolyDerM[parType].block(1, 0, nNodes - 1, 1).array().template cast() - * _disc.Ir[_disc.offsetMetric[parType]].segment(1, nNodes - 1).array().template cast() - ).sum() - ); + (state.segment(1, nNodes - 1).array() * + _disc.parInvWeights[parType] + .segment(1, nNodes - 1) + .array() + .cwiseInverse() + .template cast() * + _disc.parPolyDerM[parType].block(1, 0, nNodes - 1, 1).array().template cast() * + _disc.Ir[_disc.offsetMetric[parType]].segment(1, nNodes - 1).array().template cast()) + .sum()); } // "standard" computation for remaining cells - for (int cell = Cell0; cell < _disc.nParCell[parType]; cell++) { - stateDer.segment(cell * nNodes, nNodes) -= (_disc.Dr[_disc.offsetMetric[parType] + cell].template cast() * state.segment(cell * nNodes, nNodes)).template cast(); + for (int cell = Cell0; cell < _disc.nParCell[parType]; cell++) + { + stateDer.segment(cell * nNodes, nNodes) -= + (_disc.Dr[_disc.offsetMetric[parType] + cell].template cast() * + state.segment(cell * nNodes, nNodes)) + .template cast(); } } } /* - * @brief calculates the interface fluxes g* of particle mass balance equation and implements the respective boundary conditions + * @brief calculates the interface fluxes g* of particle mass balance equation and implements the respective + * boundary conditions * @param [in] aux bool if interface flux for auxiliary equation * @param [in] addParDisc bool if interface flux for additional particle DG-discretized equation - */ - template - void InterfaceFluxParticle(int parType, Eigen::Map, 0, InnerStride>& state, - const unsigned int strideCell, const unsigned int strideNode, const bool aux, const int comp, const bool addParDisc = false) { + */ + template + void InterfaceFluxParticle(int parType, + Eigen::Map, 0, InnerStride>& state, + const unsigned int strideCell, const unsigned int strideNode, const bool aux, + const int comp, const bool addParDisc = false) + { - Eigen::Map, 0, InnerStride<>> _surfFluxPar(reinterpret_cast(&_disc.surfaceFluxParticle[parType][0]), _disc.nParCell[parType] + 1, InnerStride<>(1)); + Eigen::Map, 0, InnerStride<>> _surfFluxPar( + reinterpret_cast(&_disc.surfaceFluxParticle[parType][0]), _disc.nParCell[parType] + 1, + InnerStride<>(1)); // reset surface flux storage as it is used multiple times _surfFluxPar.setZero(); @@ -854,33 +1140,40 @@ class GeneralRateModelDG : public UnitOperationBase // numerical flux: state* = 0.5 (state^+ + state^-) // calculate inner interface fluxes - for (unsigned int Cell = 1u; Cell < _disc.nParCell[parType]; Cell++) { - _surfFluxPar[Cell] // left interfaces + for (unsigned int Cell = 1u; Cell < _disc.nParCell[parType]; Cell++) + { + _surfFluxPar[Cell] // left interfaces = 0.5 * (state[Cell * strideCell - strideNode] + // outer/left node - state[Cell * strideCell]); // inner/right node + state[Cell * strideCell]); // inner/right node } // calculate boundary interface fluxes. - if (aux) { // ghost nodes given by state^- := state^+ for auxiliary equation + if (aux) + { // ghost nodes given by state^- := state^+ for auxiliary equation _surfFluxPar[0] = state[0]; _surfFluxPar[_disc.nParCell[parType]] = state[_disc.nParCell[parType] * strideCell - strideNode]; } - else if (addParDisc) { + else if (addParDisc) + { _surfFluxPar[0] = 0.0; _surfFluxPar[_disc.nParCell[parType]] = 0.0; } - else { - + else + { + // film diffusion BC - _surfFluxPar[_disc.nParCell[parType]] = static_cast(_disc.localFlux[comp]) - / (static_cast(_parPorosity[parType]) * static_cast(_poreAccessFactor[parType * _disc.nComp + comp])) - * (2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType]])); // inverse squared mapping was also applied, so we apply Map * invMap^2 = invMap + _surfFluxPar[_disc.nParCell[parType]] = + static_cast(_disc.localFlux[comp]) / + (static_cast(_parPorosity[parType]) * + static_cast(_poreAccessFactor[parType * _disc.nComp + comp])) * + (2.0 / static_cast( + _disc.deltaR[_disc.offsetMetric[parType]])); // inverse squared mapping was also applied, so + // we apply Map * invMap^2 = invMap // inner particle BC _surfFluxPar[0] = 0.0; - } } /** @@ -892,68 +1185,95 @@ class GeneralRateModelDG : public UnitOperationBase * @param [in] strideCell component-wise cell stride * @param [in] strideNodecomponent-wise node stride * @param [in] comp current component - */ - template + */ + template void parSurfaceIntegral(int parType, Eigen::Map, 0, InnerStride>& state, - Eigen::Map, 0, InnerStride>& stateDer, unsigned const int strideCell, unsigned const int strideNode, - const bool aux, const int comp = 0, const bool addParDisc = false) { + Eigen::Map, 0, InnerStride>& stateDer, + unsigned const int strideCell, unsigned const int strideNode, const bool aux, + const int comp = 0, const bool addParDisc = false) + { - Eigen::Map, 0, InnerStride<>> _surfFluxPar(reinterpret_cast(&_disc.surfaceFluxParticle[parType][0]), _disc.nParCell[parType] + 1, InnerStride<>(1)); + Eigen::Map, 0, InnerStride<>> _surfFluxPar( + reinterpret_cast(&_disc.surfaceFluxParticle[parType][0]), _disc.nParCell[parType] + 1, + InnerStride<>(1)); // calc numerical flux values InterfaceFluxParticle(parType, state, strideCell, strideNode, aux, comp, addParDisc); // strong surface integral -> M^-1 B [state - state*] - if (!_disc.parExactInt[parType]) { // inexact integration approach -> diagonal mass matrix + if (!_disc.parExactInt[parType]) + { // inexact integration approach -> diagonal mass matrix int Cell0 = 0; // auxiliary variable to distinguish special case // special case for sphere and cylinder if particle core = 0.0 -> leave out inner particle boundary flux - if (_parGeomSurfToVol[parType] != _disc.SurfVolRatioSlab && _parCoreRadius[parType] == 0.0) { + if (_parGeomSurfToVol[parType] != _disc.SurfVolRatioSlab && _parCoreRadius[parType] == 0.0) + { Cell0 = 1; stateDer[_disc.parPolyDeg[parType] * strideNode] // last cell node - += _disc.parInvWeights[parType][_disc.parPolyDeg[parType]] * (state[_disc.parPolyDeg[parType] * strideNode] - _surfFluxPar[1]); + += _disc.parInvWeights[parType][_disc.parPolyDeg[parType]] * + (state[_disc.parPolyDeg[parType] * strideNode] - _surfFluxPar[1]); } - for (unsigned int Cell = Cell0; Cell < _disc.nParCell[parType]; Cell++) { + for (unsigned int Cell = Cell0; Cell < _disc.nParCell[parType]; Cell++) + { stateDer[Cell * strideCell] // first cell node -= _disc.parInvWeights[parType][0] * (state[Cell * strideCell] - _surfFluxPar[Cell]); stateDer[Cell * strideCell + _disc.parPolyDeg[parType] * strideNode] // last cell node - += _disc.parInvWeights[parType][_disc.parPolyDeg[parType]] * (state[Cell * strideCell + _disc.parPolyDeg[parType] * strideNode] - _surfFluxPar[Cell + 1u]); + += _disc.parInvWeights[parType][_disc.parPolyDeg[parType]] * + (state[Cell * strideCell + _disc.parPolyDeg[parType] * strideNode] - _surfFluxPar[Cell + 1u]); } } - else { // exact integration approach -> dense mass matrix - for (unsigned int Cell = 0; Cell < _disc.nParCell[parType]; Cell++) { + else + { // exact integration approach -> dense mass matrix + for (unsigned int Cell = 0; Cell < _disc.nParCell[parType]; Cell++) + { - for (unsigned int Node = 0; Node < _disc.nParNode[parType]; Node++) { - if (aux) { // strong surface integral -> M^-1 B [state - state*] and B has two non-zero entries, -1 and 1 - stateDer[Cell * strideCell + Node * strideNode] - -= _disc.parInvMM_Leg[parType](Node, 0) * (state[Cell * strideCell]- _surfFluxPar[Cell]) - - _disc.parInvMM_Leg[parType](Node, _disc.parPolyDeg[parType]) * (state[Cell * strideCell + _disc.parPolyDeg[parType] * strideNode] - _surfFluxPar[Cell + 1u]); + for (unsigned int Node = 0; Node < _disc.nParNode[parType]; Node++) + { + if (aux) + { // strong surface integral -> M^-1 B [state - state*] and B has two non-zero entries, -1 and 1 + stateDer[Cell * strideCell + Node * strideNode] -= + _disc.parInvMM_Leg[parType](Node, 0) * (state[Cell * strideCell] - _surfFluxPar[Cell]) - + _disc.parInvMM_Leg[parType](Node, _disc.parPolyDeg[parType]) * + (state[Cell * strideCell + _disc.parPolyDeg[parType] * strideNode] - + _surfFluxPar[Cell + 1u]); } - else { - if (_parGeomSurfToVol[parType] == _disc.SurfVolRatioSlab) { // strong surface integral -> M^-1 B [state - state*] and B has two non-zero entries, -1 and 1 - stateDer[Cell * strideCell + Node * strideNode] - -= static_cast( - _disc.parInvMM[parType](Node, 0) * (state[Cell * strideCell] - _surfFluxPar[Cell]) - - _disc.parInvMM[parType](Node, _disc.parPolyDeg[parType]) * (state[Cell * strideCell + _disc.parPolyDeg[parType] * strideNode] - _surfFluxPar[Cell + 1u]) - ); + else + { + if (_parGeomSurfToVol[parType] == _disc.SurfVolRatioSlab) + { // strong surface integral -> M^-1 B [state - state*] and B has two non-zero entries, -1 and 1 + stateDer[Cell * strideCell + Node * strideNode] -= static_cast( + _disc.parInvMM[parType](Node, 0) * (state[Cell * strideCell] - _surfFluxPar[Cell]) - + _disc.parInvMM[parType](Node, _disc.parPolyDeg[parType]) * + (state[Cell * strideCell + _disc.parPolyDeg[parType] * strideNode] - + _surfFluxPar[Cell + 1u])); } - else if (_parGeomSurfToVol[parType] == _disc.SurfVolRatioCylinder) { // weak surface integral -> M^-1 B [- state*] and B has two non-zero entries, which depend on metrics - stateDer[Cell * strideCell + Node * strideNode] - -= static_cast( - _disc.Ir[_disc.offsetMetric[parType] + Cell][0] * _disc.parInvMM[_disc.offsetMetric[parType] + Cell](Node, 0) * (-_surfFluxPar[Cell]) - + _disc.Ir[_disc.offsetMetric[parType] + Cell][_disc.nParNode[parType] - 1] * _disc.parInvMM[_disc.offsetMetric[parType] + Cell](Node, _disc.parPolyDeg[parType]) * _surfFluxPar[Cell + 1u] - ); + else if (_parGeomSurfToVol[parType] == _disc.SurfVolRatioCylinder) + { // weak surface integral -> M^-1 B [- state*] and B has two non-zero entries, which depend on + // metrics + stateDer[Cell * strideCell + Node * strideNode] -= static_cast( + _disc.Ir[_disc.offsetMetric[parType] + Cell][0] * + _disc.parInvMM[_disc.offsetMetric[parType] + Cell](Node, 0) * + (-_surfFluxPar[Cell]) + + _disc.Ir[_disc.offsetMetric[parType] + Cell][_disc.nParNode[parType] - 1] * + _disc.parInvMM[_disc.offsetMetric[parType] + Cell](Node, + _disc.parPolyDeg[parType]) * + _surfFluxPar[Cell + 1u]); } - else if (_parGeomSurfToVol[parType] == _disc.SurfVolRatioSphere) { // weak surface integral -> M^-1 B [- state*] and B has two non-zero entries, which depend on metrics - stateDer[Cell * strideCell + Node * strideNode] - -= static_cast( - _disc.Ir[_disc.offsetMetric[parType] + Cell][0] * _disc.parInvMM[_disc.offsetMetric[parType] + Cell](Node, 0) * (-_surfFluxPar[Cell]) - + _disc.Ir[_disc.offsetMetric[parType] + Cell][_disc.nParNode[parType] - 1] * _disc.parInvMM[_disc.offsetMetric[parType] + Cell](Node, _disc.parPolyDeg[parType]) * _surfFluxPar[Cell + 1u] - ); + else if (_parGeomSurfToVol[parType] == _disc.SurfVolRatioSphere) + { // weak surface integral -> M^-1 B [- state*] and B has two non-zero entries, which depend on + // metrics + stateDer[Cell * strideCell + Node * strideNode] -= static_cast( + _disc.Ir[_disc.offsetMetric[parType] + Cell][0] * + _disc.parInvMM[_disc.offsetMetric[parType] + Cell](Node, 0) * + (-_surfFluxPar[Cell]) + + _disc.Ir[_disc.offsetMetric[parType] + Cell][_disc.nParNode[parType] - 1] * + _disc.parInvMM[_disc.offsetMetric[parType] + Cell](Node, + _disc.parPolyDeg[parType]) * + _surfFluxPar[Cell + 1u]); } } } @@ -963,39 +1283,53 @@ class GeneralRateModelDG : public UnitOperationBase /** * @brief solves the auxiliary system g = d c / d xi * @detail computes g = Dc - M^-1 B [c - c^*] and stores this in _disc.g_p - */ - template - void solve_auxiliary_DG(int parType, Eigen::Map, 0, InnerStride<>>& conc, unsigned int strideCell, unsigned int strideNode, int comp) { + */ + template + void solve_auxiliary_DG(int parType, Eigen::Map, 0, InnerStride<>>& conc, + unsigned int strideCell, unsigned int strideNode, int comp) + { - Eigen::Map, 0, InnerStride<>> g_p(reinterpret_cast(&_disc.g_p[parType][0]), _disc.nParPoints[parType], InnerStride<>(1)); - Eigen::Map, 0, InnerStride<>> _surfFluxPar(reinterpret_cast(&_disc.surfaceFluxParticle[parType][0]), _disc.nParCell[parType] + 1, InnerStride<>(1)); + Eigen::Map, 0, InnerStride<>> g_p( + reinterpret_cast(&_disc.g_p[parType][0]), _disc.nParPoints[parType], InnerStride<>(1)); + Eigen::Map, 0, InnerStride<>> _surfFluxPar( + reinterpret_cast(&_disc.surfaceFluxParticle[parType][0]), _disc.nParCell[parType] + 1, + InnerStride<>(1)); _surfFluxPar.setZero(); // reset surface flux storage as it is used multiple times - g_p.setZero(); // reset auxiliary variable g + g_p.setZero(); // reset auxiliary variable g // ========================================================================================// // solve auxiliary systems g = d c / d xi => g_p = Dc - M^-1 B [c - c^*] // // ========================================================================================// - + parVolumeIntegral(parType, true, conc, g_p); // volumne integral in strong DG form: - D c - - parSurfaceIntegral(parType, conc, g_p, strideCell, strideNode, true, comp); // surface integral in strong DG form: M^-1 B [c - c^*] - + + parSurfaceIntegral(parType, conc, g_p, strideCell, strideNode, true, + comp); // surface integral in strong DG form: M^-1 B [c - c^*] + g_p *= -1.0; // auxiliary factor -1 } - // ========================================================================================================================================================== // - // ======================================== DG particle Jacobian ============================================= // - // ========================================================================================================================================================== // + // ========================================================================================================================================================== + // // + // ======================================== DG particle Jacobian + // ============================================= // + // ========================================================================================================================================================== + // // typedef Eigen::Triplet T; /** - * @brief calculates the particle dispersion jacobian Pattern of the exact/inexact integration DG scheme for the given particle type and bead - */ - void calcParticleJacobianPattern(std::vector& tripletList, unsigned int parType, unsigned int colNode, unsigned int secIdx) { + * @brief calculates the particle dispersion jacobian Pattern of the exact/inexact integration DG scheme for the + * given particle type and bead + */ + void calcParticleJacobianPattern(std::vector& tripletList, unsigned int parType, unsigned int colNode, + unsigned int secIdx) + { // Ordering of particle surface diffusion: // bnd0comp0, bnd0comp1, bnd0comp2, bnd1comp0, bnd1comp1, bnd1comp2 - active const* const _parSurfDiff = getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + _disc.nBoundBeforeType[parType]; + active const* const _parSurfDiff = + getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + + _disc.nBoundBeforeType[parType]; Indexer idxr(_disc); @@ -1003,41 +1337,64 @@ class GeneralRateModelDG : public UnitOperationBase unsigned int sCell = _disc.nParNode[parType] * idxr.strideParNode(parType); unsigned int sNode = idxr.strideParNode(parType); unsigned int sComp = 1u; - unsigned int offset = idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }); + unsigned int offset = idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}); unsigned int nNodes = _disc.nParNode[parType]; // case: one cell -> diffBlock \in R^(nParNodes x nParNodes), GBlock = parPolyDerM - if (_disc.nParCell[parType] == 1) { + if (_disc.nParCell[parType] == 1) + { - // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the respective coefficients - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int i = 0; i < nNodes; i++) { - for (unsigned int j = 0; j < nNodes; j++) { + // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the respective + // coefficients + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int i = 0; i < nNodes; i++) + { + for (unsigned int j = 0; j < nNodes; j++) + { // handle liquid state // row: add component offset and go node strides from there for each dispersion block entry // col: add component offset and go node strides from there for each dispersion block entry - tripletList.push_back(T(offset + comp * sComp + i * sNode, - offset + comp * sComp + j * sNode, 0.0)); + tripletList.push_back( + T(offset + comp * sComp + i * sNode, offset + comp * sComp + j * sNode, 0.0)); // handle surface diffusion of bound states. - if (_hasSurfaceDiffusion[parType]) { + if (_hasSurfaceDiffusion[parType]) + { int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) { - // row: add current component offset and go node strides from there for each dispersion block entry - // col: jump oover liquid states, add current bound state offset and go node strides from there for each dispersion block entry - tripletList.push_back(T(offset + comp * sComp + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode, 0.0)); + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) + { + if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) + { + // row: add current component offset and go node strides from there for each + // dispersion block entry col: jump oover liquid states, add current bound state + // offset and go node strides from there for each dispersion block entry + tripletList.push_back( + T(offset + comp * sComp + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + bnd + j * sNode, + 0.0)); /* add surface diffusion dispersion block to solid */ - if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd]) { - // row: jump over liquid states, add current bound state offset and go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and go node strides from there for each dispersion block entry - tripletList.push_back(T(offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode, 0.0)); - + if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd]) + { + // row: jump over liquid states, add current bound state offset and go node + // strides from there for each dispersion block entry col: jump over liquid + // states, add current bound state offset and go node strides from there for + // each dispersion block entry + tripletList.push_back(T( + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + bnd + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + bnd + j * sNode, + 0.0)); } } } @@ -1046,48 +1403,73 @@ class GeneralRateModelDG : public UnitOperationBase } } } - else { + else + { - if (!_disc.parExactInt[parType]) { + if (!_disc.parExactInt[parType]) + { /* left boundary cell */ - // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the respective coefficients - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int i = 0; i < nNodes; i++) { - for (unsigned int j = nNodes; j < 3 * nNodes; j++) { + // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the + // respective coefficients + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int i = 0; i < nNodes; i++) + { + for (unsigned int j = nNodes; j < 3 * nNodes; j++) + { // pattern is more sparse than a nNodes x 2*nNodes block. - if ((j >= nNodes - 1 && j <= 2 * nNodes) || - (i == 0 && j <= 2 * nNodes) || - (i == nNodes - 1 && j >= nNodes - 1)) { + if ((j >= nNodes - 1 && j <= 2 * nNodes) || (i == 0 && j <= 2 * nNodes) || + (i == nNodes - 1 && j >= nNodes - 1)) + { // handle liquid state - // row: add component offset and go node strides from there for each dispersion block entry - // col: add component offset and go node strides from there for each dispersion block entry + // row: add component offset and go node strides from there for each dispersion block + // entry col: add component offset and go node strides from there for each dispersion + // block entry tripletList.push_back(T(offset + comp * sComp + i * sNode, - offset + comp * sComp + (j - nNodes) * sNode, - 0.0)); + offset + comp * sComp + (j - nNodes) * sNode, 0.0)); // handle surface diffusion of bound states. binding is handled in residualKernel(). - if (_hasSurfaceDiffusion[parType]) { + if (_hasSurfaceDiffusion[parType]) + { int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) { - // row: add current component offset and go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and go node strides from there for each dispersion block entry + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) + { + if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) + { + // row: add current component offset and go node strides from there for each + // dispersion block entry col: jump over liquid states, add current bound + // state offset and go node strides from there for each dispersion block + // entry tripletList.push_back(T(offset + comp * sComp + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + (j - nNodes) * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + (j - nNodes) * sNode, 0.0)); /* add surface diffusion dispersion block to solid */ - if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd]) { - // row: jump over liquid states, add current bound state offset and go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and go node strides from there for each dispersion block entry - tripletList.push_back(T(offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + (j - nNodes) * sNode, - 0.0)); - + if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd]) + { + // row: jump over liquid states, add current bound state offset and go + // node strides from there for each dispersion block entry col: jump + // over liquid states, add current bound state offset and go node + // strides from there for each dispersion block entry + tripletList.push_back( + T(offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + (j - nNodes) * sNode, + 0.0)); } } } @@ -1099,41 +1481,71 @@ class GeneralRateModelDG : public UnitOperationBase /* right boundary cell */ - // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the respective coefficients - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int i = 0; i < nNodes; i++) { - for (unsigned int j = 0; j < 2 * nNodes; j++) { + // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the + // respective coefficients + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int i = 0; i < nNodes; i++) + { + for (unsigned int j = 0; j < 2 * nNodes; j++) + { // pattern is more sparse than a nNodes x 2*nNodes block. - if ((j >= nNodes - 1 && j <= 2 * nNodes) || - (i == 0 && j <= 2 * nNodes) || - (i == nNodes - 1 && j >= nNodes - 1)) { + if ((j >= nNodes - 1 && j <= 2 * nNodes) || (i == 0 && j <= 2 * nNodes) || + (i == nNodes - 1 && j >= nNodes - 1)) + { // handle liquid state - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: add component offset and jump over previous cells. Go back one cell and go node strides from there for each dispersion block entry - tripletList.push_back(T(offset + comp * sComp + (_disc.nParCell[parType] - 1) * sCell + i * sNode, - offset + comp * sComp + (_disc.nParCell[parType] - 2) * sCell + j * sNode, - 0.0)); + // row: add component offset and jump over previous cells. Go node strides from there + // for each dispersion block entry col: add component offset and jump over previous + // cells. Go back one cell and go node strides from there for each dispersion block + // entry + tripletList.push_back( + T(offset + comp * sComp + (_disc.nParCell[parType] - 1) * sCell + i * sNode, + offset + comp * sComp + (_disc.nParCell[parType] - 2) * sCell + j * sNode, 0.0)); // handle surface diffusion of bound states. binding is handled in residualKernel(). - if (_hasSurfaceDiffusion[parType]) { + if (_hasSurfaceDiffusion[parType]) + { int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) { - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and jump over previous cells. Go back one cell and go node strides from there for each dispersion block entry - tripletList.push_back(T(offset + comp * sComp + (_disc.nParCell[parType] - 1) * sCell + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + (_disc.nParCell[parType] - 2) * sCell + j * sNode, - 0.0)); + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) + { + if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) + { + // row: add component offset and jump over previous cells. Go node strides + // from there for each dispersion block entry col: jump over liquid states, + // add current bound state offset and jump over previous cells. Go back one + // cell and go node strides from there for each dispersion block entry + tripletList.push_back( + T(offset + comp * sComp + (_disc.nParCell[parType] - 1) * sCell + + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + (_disc.nParCell[parType] - 2) * sCell + j * sNode, + 0.0)); /* add surface diffusion dispersion block to solid */ - if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd]) { - // row: jump over previous cells and liquid states, add current bound state offset and go node strides from there for each dispersion block entry - // col: jump over previous cells and liquid states, go back one cell, add current bound state offset and go node strides from there for each dispersion block entry - tripletList.push_back(T(offset + (_disc.nParCell[parType] - 1) * sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + i * sNode, - offset + (_disc.nParCell[parType] - 2) * sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode, - 0.0)); - + if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd]) + { + // row: jump over previous cells and liquid states, add current bound + // state offset and go node strides from there for each dispersion block + // entry col: jump over previous cells and liquid states, go back one + // cell, add current bound state offset and go node strides from there + // for each dispersion block entry + tripletList.push_back( + T(offset + (_disc.nParCell[parType] - 1) * sCell + + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + i * sNode, + offset + (_disc.nParCell[parType] - 2) * sCell + + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + j * sNode, + 0.0)); } } } @@ -1145,42 +1557,73 @@ class GeneralRateModelDG : public UnitOperationBase /* inner cells */ - for (int cell = 1; cell < _disc.nParCell[parType] - 1; cell++) { + for (int cell = 1; cell < _disc.nParCell[parType] - 1; cell++) + { - // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the respective coefficients - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int i = 0; i < nNodes; i++) { - for (unsigned int j = 0; j < 3 * nNodes; j++) { + // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the + // respective coefficients + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int i = 0; i < nNodes; i++) + { + for (unsigned int j = 0; j < 3 * nNodes; j++) + { // pattern is more sparse than a nNodes x 3*nNodes block. - if ((j >= nNodes - 1 && j <= 2 * nNodes) || - (i == 0 && j <= 2 * nNodes) || - (i == nNodes - 1 && j >= nNodes - 1)) { + if ((j >= nNodes - 1 && j <= 2 * nNodes) || (i == 0 && j <= 2 * nNodes) || + (i == nNodes - 1 && j >= nNodes - 1)) + { // handle liquid state - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: add component offset and jump over previous cells. Go back one cell and go node strides from there for each dispersion block entry + // row: add component offset and jump over previous cells. Go node strides from + // there for each dispersion block entry col: add component offset and jump over + // previous cells. Go back one cell and go node strides from there for each + // dispersion block entry tripletList.push_back(T(offset + comp * sComp + cell * sCell + i * sNode, - offset + comp * sComp + (cell - 1) * sCell + j * sNode, 0.0)); + offset + comp * sComp + (cell - 1) * sCell + j * sNode, + 0.0)); // handle surface diffusion of bound states. binding is handled in residualKernel(). - if (_hasSurfaceDiffusion[parType]) { + if (_hasSurfaceDiffusion[parType]) + { int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) { - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and jump over previous cells. Go back one cell and go node strides from there for each dispersion block entry - tripletList.push_back(T(offset + comp * sComp + cell * sCell + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + (cell - 1) * sCell + j * sNode, - 0.0)); + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; + bnd++) + { + if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) + { + // row: add component offset and jump over previous cells. Go node + // strides from there for each dispersion block entry col: jump over + // liquid states, add current bound state offset and jump over previous + // cells. Go back one cell and go node strides from there for each + // dispersion block entry + tripletList.push_back( + T(offset + comp * sComp + cell * sCell + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + (cell - 1) * sCell + j * sNode, + 0.0)); /* add surface diffusion dispersion block to solid */ - if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd]) { - // row: jump over previous cells and liquid states, add current bound state offset and go node strides from there for each dispersion block entry - // col: jump over previous cells and liquid states, go back one cell, add current bound state offset and go node strides from there for each dispersion block entry - tripletList.push_back(T(offset + cell * sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + i * sNode, - offset + (cell - 1) * sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode, - 0.0)); - + if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd]) + { + // row: jump over previous cells and liquid states, add current + // bound state offset and go node strides from there for each + // dispersion block entry col: jump over previous cells and liquid + // states, go back one cell, add current bound state offset and go + // node strides from there for each dispersion block entry + tripletList.push_back( + T(offset + cell * sCell + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + i * sNode, + offset + (cell - 1) * sCell + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + j * sNode, + 0.0)); } } } @@ -1191,45 +1634,70 @@ class GeneralRateModelDG : public UnitOperationBase } } } - else { //exact integration + else + { // exact integration - /* boundary cells */ + /* boundary cells */ - /* left boundary cell */ + /* left boundary cell */ - unsigned int special = 0u; if (_disc.nParCell[parType] < 3u) special = 1u; // limits the iterator for special case nCells = 3 (dependence on additional entry) - // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the respective coefficients - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int i = 0; i < nNodes; i++) { - for (unsigned int j = nNodes + 1; j < 3 * nNodes + 2 - special; j++) { + unsigned int special = 0u; + if (_disc.nParCell[parType] < 3u) + special = 1u; // limits the iterator for special case nCells = 3 (dependence on additional entry) + // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the + // respective coefficients + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int i = 0; i < nNodes; i++) + { + for (unsigned int j = nNodes + 1; j < 3 * nNodes + 2 - special; j++) + { // handle liquid state // row: add component offset and go node strides from there for each dispersion block entry - // col: add component offset and go node strides from there for each dispersion block entry. adjust for j start + // col: add component offset and go node strides from there for each dispersion block entry. + // adjust for j start tripletList.push_back(T(offset + comp * sComp + i * sNode, - offset + comp * sComp + j * sNode - (nNodes + 1) * sNode, - 0.0)); + offset + comp * sComp + j * sNode - (nNodes + 1) * sNode, 0.0)); // handle surface diffusion of bound states. binding is handled in residualKernel(). - if (_hasSurfaceDiffusion[parType]) { + if (_hasSurfaceDiffusion[parType]) + { int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) { - // row: add current component offset and go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and go node strides from there for each dispersion block entry. adjust for j start - tripletList.push_back(T(offset + comp * sComp + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode - (nNodes + 1) * sNode, - 0.0)); + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) + { + if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) + { + // row: add current component offset and go node strides from there for each + // dispersion block entry col: jump over liquid states, add current bound state + // offset and go node strides from there for each dispersion block entry. adjust + // for j start + tripletList.push_back(T( + offset + comp * sComp + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + bnd + j * sNode - (nNodes + 1) * sNode, + 0.0)); /* add surface diffusion dispersion block to solid */ - if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd]) { - // row: jump over liquid states, add current bound state offset and go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and go node strides from there for each dispersion block entry. adjust for j start - tripletList.push_back(T(offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode - (nNodes + 1) * sNode, + if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd]) + { + // row: jump over liquid states, add current bound state offset and go node + // strides from there for each dispersion block entry col: jump over liquid + // states, add current bound state offset and go node strides from there for + // each dispersion block entry. adjust for j start + tripletList.push_back(T(offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + j * sNode - (nNodes + 1) * sNode, 0.0)); - } } } @@ -1240,37 +1708,68 @@ class GeneralRateModelDG : public UnitOperationBase /* right boundary cell */ - // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the respective coefficients - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int i = 0; i < nNodes; i++) { + // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the + // respective coefficients + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int i = 0; i < nNodes; i++) + { - for (unsigned int j = special; j < 2 * nNodes + 1; j++) { + for (unsigned int j = special; j < 2 * nNodes + 1; j++) + { // handle liquid state - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: add component offset and jump over previous cells. Go back one cell (and node or adjust for start) and go node strides from there for each dispersion block entry. - tripletList.push_back(T(offset + comp * sComp + (_disc.nParCell[parType] - 1) * sCell + i * sNode, - offset + comp * sComp + (_disc.nParCell[parType] - 1) * sCell - sCell - sNode + j * sNode, - 0.0)); + // row: add component offset and jump over previous cells. Go node strides from there for + // each dispersion block entry col: add component offset and jump over previous cells. Go + // back one cell (and node or adjust for start) and go node strides from there for each + // dispersion block entry. + tripletList.push_back( + T(offset + comp * sComp + (_disc.nParCell[parType] - 1) * sCell + i * sNode, + offset + comp * sComp + (_disc.nParCell[parType] - 1) * sCell - sCell - sNode + + j * sNode, + 0.0)); // handle surface diffusion of bound states. binding is handled in residualKernel(). - if (_hasSurfaceDiffusion[parType]) { + if (_hasSurfaceDiffusion[parType]) + { int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) { - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and jump over previous cells. Go back one cell (and node or adjust for start) and go node strides from there for each dispersion block entry. - tripletList.push_back(T(offset + comp * sComp + (_disc.nParCell[parType] - 1) * sCell + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + (_disc.nParCell[parType] - 2) * sCell - sNode + j * sNode, - 0.0)); + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) + { + if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) + { + // row: add component offset and jump over previous cells. Go node strides from + // there for each dispersion block entry col: jump over liquid states, add + // current bound state offset and jump over previous cells. Go back one cell + // (and node or adjust for start) and go node strides from there for each + // dispersion block entry. + tripletList.push_back(T( + offset + comp * sComp + (_disc.nParCell[parType] - 1) * sCell + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + bnd + (_disc.nParCell[parType] - 2) * sCell - sNode + j * sNode, + 0.0)); /* add surface diffusion dispersion block to solid */ - if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd]) { - // row: jump over previous cells and over liquid states, add current bound state offset. go node strides from there for each dispersion block entry - // col: jump over previous cells and over liquid states, add current bound state offset. go back one cell (and node or adjust for start) and go node strides from there for each dispersion block entry. - tripletList.push_back(T(offset + (_disc.nParCell[parType] - 1) * sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + i * sNode, - offset + (_disc.nParCell[parType] - 2) * sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) - sNode + bnd + j * sNode, + if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd]) + { + // row: jump over previous cells and over liquid states, add current bound + // state offset. go node strides from there for each dispersion block entry + // col: jump over previous cells and over liquid states, add current bound + // state offset. go back one cell (and node or adjust for start) and go node + // strides from there for each dispersion block entry. + tripletList.push_back(T(offset + (_disc.nParCell[parType] - 1) * sCell + + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + i * sNode, + offset + (_disc.nParCell[parType] - 2) * sCell + + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) - + sNode + bnd + j * sNode, 0.0)); } } @@ -1279,121 +1778,202 @@ class GeneralRateModelDG : public UnitOperationBase } } } - if (_disc.nParCell[parType] == 3) { - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int i = 0; i < nNodes; i++) { - for (unsigned int j = 1; j < 3 * nNodes + 2 - 1; j++) { + if (_disc.nParCell[parType] == 3) + { + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int i = 0; i < nNodes; i++) + { + for (unsigned int j = 1; j < 3 * nNodes + 2 - 1; j++) + { // handle liquid state - // row: add component offset and jump over previous cell. Go node strides from there for each dispersion block entry - // col: add component offset. Go node strides from there for each dispersion block entry. adjust for j start + // row: add component offset and jump over previous cell. Go node strides from there for + // each dispersion block entry col: add component offset. Go node strides from there for + // each dispersion block entry. adjust for j start tripletList.push_back(T(offset + comp * sComp + sCell + i * sNode, - offset + comp * sComp + j * sNode - sNode, - 0.0)); + offset + comp * sComp + j * sNode - sNode, 0.0)); // handle surface diffusion of bound states. binding is handled in residualKernel(). - if (_hasSurfaceDiffusion[parType]) { + if (_hasSurfaceDiffusion[parType]) + { int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) { - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and jump over previous cell. go back one cell and go node strides from there for each dispersion block entry. adjust for j start + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) + { + if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) + { + // row: add component offset and jump over previous cells. Go node strides + // from there for each dispersion block entry col: jump over liquid states, + // add current bound state offset and jump over previous cell. go back one + // cell and go node strides from there for each dispersion block entry. + // adjust for j start tripletList.push_back(T(offset + comp * sComp + sCell + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode - sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + j * sNode - sNode, 0.0)); /* add surface diffusion dispersion block to solid */ - if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd]) { - // row: jump over previous cells and over liquid states, add current bound state offset. go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and jump over previous cell. go node strides from there for each dispersion block entry. adjust for j start - tripletList.push_back(T(offset + sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode - sNode, - 0.0)); - + if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd]) + { + // row: jump over previous cells and over liquid states, add current + // bound state offset. go node strides from there for each dispersion + // block entry col: jump over liquid states, add current bound state + // offset and jump over previous cell. go node strides from there for + // each dispersion block entry. adjust for j start + tripletList.push_back( + T(offset + sCell + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + j * sNode - sNode, + 0.0)); } } } } - } } } - }// special case nCells == 3 + } // special case nCells == 3 /* boundary cell neighbours (exist only if nCells >= 4) */ - if (_disc.nParCell[parType] >= 4) { + if (_disc.nParCell[parType] >= 4) + { - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int i = 0; i < nNodes; i++) { - for (unsigned int j = 1; j < 3 * nNodes + 2; j++) { + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int i = 0; i < nNodes; i++) + { + for (unsigned int j = 1; j < 3 * nNodes + 2; j++) + { // handle liquid state - // row: add component offset and jump over previous cell. Go node strides from there for each dispersion block entry - // col: add component offset. Go node strides from there for each dispersion block entry. adjust for j start + // row: add component offset and jump over previous cell. Go node strides from there for + // each dispersion block entry col: add component offset. Go node strides from there for + // each dispersion block entry. adjust for j start tripletList.push_back(T(offset + comp * sComp + sCell + i * sNode, - offset + comp * sComp + j * sNode - sNode, - 0.0)); + offset + comp * sComp + j * sNode - sNode, 0.0)); // handle surface diffusion of bound states. binding is handled in residualKernel(). - if (_hasSurfaceDiffusion[parType]) { + if (_hasSurfaceDiffusion[parType]) + { int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) { - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and jump over previous cells. Go back one cell and go node strides from there for each dispersion block entry. adjust for j start + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) + { + if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) + { + // row: add component offset and jump over previous cells. Go node strides + // from there for each dispersion block entry col: jump over liquid states, + // add current bound state offset and jump over previous cells. Go back one + // cell and go node strides from there for each dispersion block entry. + // adjust for j start tripletList.push_back(T(offset + comp * sComp + sCell + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode - sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + j * sNode - sNode, 0.0)); /* add surface diffusion dispersion block to solid */ - if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd]) { - // row: jump over previous cells and over liquid states, add current bound state offset. go node strides from there for each dispersion block entry - // col: jump over previous cells and over liquid states, add current bound state offset. go back one cell and go node strides from there for each dispersion block entry. adjust for j start - tripletList.push_back(T(offset + sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode - sNode, - 0.0)); - + if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd]) + { + // row: jump over previous cells and over liquid states, add current + // bound state offset. go node strides from there for each dispersion + // block entry col: jump over previous cells and over liquid states, add + // current bound state offset. go back one cell and go node strides from + // there for each dispersion block entry. adjust for j start + tripletList.push_back( + T(offset + sCell + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + j * sNode - sNode, + 0.0)); } } } } - } } } - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int i = 0; i < nNodes; i++) { - for (unsigned int j = 0; j < 3 * nNodes + 2 - 1; j++) { + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int i = 0; i < nNodes; i++) + { + for (unsigned int j = 0; j < 3 * nNodes + 2 - 1; j++) + { // handle liquid state - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: add component offset and jump over previous cells. Go back one cell and node. Go node strides from there for each dispersion block entry. - tripletList.push_back(T(offset + comp * sComp + (_disc.nParCell[parType] - 2) * sCell + i * sNode, - offset + comp * sComp + (_disc.nParCell[parType] - 2) * sCell - sCell - sNode + j * sNode, - 0.0)); + // row: add component offset and jump over previous cells. Go node strides from there + // for each dispersion block entry col: add component offset and jump over previous + // cells. Go back one cell and node. Go node strides from there for each dispersion + // block entry. + tripletList.push_back( + T(offset + comp * sComp + (_disc.nParCell[parType] - 2) * sCell + i * sNode, + offset + comp * sComp + (_disc.nParCell[parType] - 2) * sCell - sCell - sNode + + j * sNode, + 0.0)); // handle surface diffusion of bound states. binding is handled in residualKernel(). - if (_hasSurfaceDiffusion[parType]) { + if (_hasSurfaceDiffusion[parType]) + { int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) { - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and jump over previous cells. Go back one cell and node and go node strides from there for each dispersion block entry - tripletList.push_back(T(offset + comp * sComp + (_disc.nParCell[parType] - 2) * sCell + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + (_disc.nParCell[parType] - 2) * sCell - sCell - sNode + j * sNode, + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) + { + if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) + { + // row: add component offset and jump over previous cells. Go node strides + // from there for each dispersion block entry col: jump over liquid states, + // add current bound state offset and jump over previous cells. Go back one + // cell and node and go node strides from there for each dispersion block + // entry + tripletList.push_back(T(offset + comp * sComp + + (_disc.nParCell[parType] - 2) * sCell + + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + (_disc.nParCell[parType] - 2) * sCell - + sCell - sNode + j * sNode, 0.0)); /* add surface diffusion dispersion block to solid */ - if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd]) { - // row: jump over previous cells and over liquid states, add current bound state offset. go node strides from there for each dispersion block entry - // col: jump over previous cells and over liquid states, add current bound state offset. go back one cell and node and go node strides from there for each dispersion block entry - tripletList.push_back(T(offset + (_disc.nParCell[parType] - 2) * sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + i * sNode, - offset + (_disc.nParCell[parType] - 2) * sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) - sCell - sNode + bnd + j * sNode, - 0.0)); - + if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd]) + { + // row: jump over previous cells and over liquid states, add current + // bound state offset. go node strides from there for each dispersion + // block entry col: jump over previous cells and over liquid states, add + // current bound state offset. go back one cell and node and go node + // strides from there for each dispersion block entry + tripletList.push_back( + T(offset + (_disc.nParCell[parType] - 2) * sCell + + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + i * sNode, + offset + (_disc.nParCell[parType] - 2) * sCell + + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) - + sCell - sNode + bnd + j * sNode, + 0.0)); } } } @@ -1405,41 +1985,72 @@ class GeneralRateModelDG : public UnitOperationBase /* Inner cells (exist only if nCells >= 5) */ - if (_disc.nParCell[parType] >= 5) { + if (_disc.nParCell[parType] >= 5) + { - for (unsigned int cell = 2; cell < _disc.nParCell[parType] - 2; cell++) { + for (unsigned int cell = 2; cell < _disc.nParCell[parType] - 2; cell++) + { - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int i = 0; i < nNodes; i++) { - for (unsigned int j = 0; j < 3 * nNodes + 2; j++) { + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int i = 0; i < nNodes; i++) + { + for (unsigned int j = 0; j < 3 * nNodes + 2; j++) + { // handle liquid state - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: add component offset and jump over previous cells. Go back one cell and node. Go node strides from there for each dispersion block entry. - tripletList.push_back(T(offset + comp * sComp + cell * sCell + i * sNode, - offset + comp * sComp + cell * sCell - sCell - sNode + j * sNode, - 0.0)); + // row: add component offset and jump over previous cells. Go node strides from + // there for each dispersion block entry col: add component offset and jump over + // previous cells. Go back one cell and node. Go node strides from there for each + // dispersion block entry. + tripletList.push_back( + T(offset + comp * sComp + cell * sCell + i * sNode, + offset + comp * sComp + cell * sCell - sCell - sNode + j * sNode, 0.0)); // handle surface diffusion of bound states. binding is handled in residualKernel(). - if (_hasSurfaceDiffusion[parType]) { + if (_hasSurfaceDiffusion[parType]) + { int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) { - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and jump over previous cells. Go back one cell and node and go node strides from there for each dispersion block entry - tripletList.push_back(T(offset + comp * sComp + cell * sCell + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + cell * sCell - sCell - sNode + j * sNode, - 0.0)); + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; + bnd++) + { + if (_parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)] != 0.0) + { + // row: add component offset and jump over previous cells. Go node + // strides from there for each dispersion block entry col: jump over + // liquid states, add current bound state offset and jump over previous + // cells. Go back one cell and node and go node strides from there for + // each dispersion block entry + tripletList.push_back( + T(offset + comp * sComp + cell * sCell + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + cell * sCell - sCell - sNode + j * sNode, + 0.0)); /* add surface diffusion dispersion block to solid */ - if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd]) { - // row: jump over previous cells and over liquid states, add current bound state offset. go node strides from there for each dispersion block entry - // col: jump over previous cells and over liquid states, add current bound state offset. go back one cell and node and go node strides from there for each dispersion block entry - tripletList.push_back(T(offset + cell * sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + i * sNode, - offset + cell * sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd - sCell - sNode + j * sNode, - 0.0)); - + if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd]) + { + // row: jump over previous cells and over liquid states, add current + // bound state offset. go node strides from there for each + // dispersion block entry col: jump over previous cells and over + // liquid states, add current bound state offset. go back one cell + // and node and go node strides from there for each dispersion block + // entry + tripletList.push_back( + T(offset + cell * sCell + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + i * sNode, + offset + cell * sCell + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd - sCell - sNode + j * sNode, + 0.0)); } } } @@ -1448,66 +2059,87 @@ class GeneralRateModelDG : public UnitOperationBase } } } - } } // parExactInt } // if nCells > 1 } - unsigned int calcParDispNNZ(int parType) { + unsigned int calcParDispNNZ(int parType) + { - if (_disc.parExactInt[parType]) { - return _disc.nComp * ((3u * _disc.nParCell[parType] - 2u) * _disc.nParNode[parType] * _disc.nParNode[parType] + (2u * _disc.nParCell[parType] - 3u) * _disc.nParNode[parType]); + if (_disc.parExactInt[parType]) + { + return _disc.nComp * + ((3u * _disc.nParCell[parType] - 2u) * _disc.nParNode[parType] * _disc.nParNode[parType] + + (2u * _disc.nParCell[parType] - 3u) * _disc.nParNode[parType]); } - else { - return _disc.nComp * (_disc.nParCell[parType] * _disc.nParNode[parType] * _disc.nParNode[parType] + 8u * _disc.nParNode[parType]); + else + { + return _disc.nComp * (_disc.nParCell[parType] * _disc.nParNode[parType] * _disc.nParNode[parType] + + 8u * _disc.nParNode[parType]); } } /** * @brief sets the sparsity pattern of the binding Jacobian */ - void parBindingPattern_GRM(std::vector& tripletList, unsigned int parType, unsigned int colNode) { + void parBindingPattern_GRM(std::vector& tripletList, unsigned int parType, unsigned int colNode) + { Indexer idxr(_disc); - int offset = idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }); + int offset = idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}); // every bound state might depend on every bound and liquid state - for (int parNode = 0; parNode < _disc.nParPoints[parType]; parNode++) { - for (int bnd = 0; bnd < _disc.strideBound[parType]; bnd++) { - for (int conc = 0; conc < idxr.strideParNode(parType); conc++) { + for (int parNode = 0; parNode < _disc.nParPoints[parType]; parNode++) + { + for (int bnd = 0; bnd < _disc.strideBound[parType]; bnd++) + { + for (int conc = 0; conc < idxr.strideParNode(parType); conc++) + { // row: jump over previous nodes and liquid states and add current bound state offset // col: jump over previous nodes and add current concentration offset (liquid and bound) - tripletList.push_back(T(offset + parNode * idxr.strideParNode(parType) + idxr.strideParLiquid() + bnd, - offset + parNode * idxr.strideParNode(parType) + conc, 0.0)); + tripletList.push_back( + T(offset + parNode * idxr.strideParNode(parType) + idxr.strideParLiquid() + bnd, + offset + parNode * idxr.strideParNode(parType) + conc, 0.0)); } } } } /** *@brief adds the time derivative entries from particle equations - *@detail since the main diagonal entries are already set, we actually only set the solid phase time derivative entries for the discretized particle mass balance equations + *@detail since the main diagonal entries are already set, we actually only set the solid phase time derivative + *entries for the discretized particle mass balance equations */ - void parTimeDerJacPattern_GRM(std::vector& tripletList, unsigned int parType, unsigned int colNode, unsigned int secIdx) { + void parTimeDerJacPattern_GRM(std::vector& tripletList, unsigned int parType, unsigned int colNode, + unsigned int secIdx) + { Indexer idxr(_disc); - active const* const _parSurfDiff = getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + _disc.nBoundBeforeType[parType]; - unsigned int offset = idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }); + active const* const _parSurfDiff = + getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + + _disc.nBoundBeforeType[parType]; + unsigned int offset = idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}); - for (unsigned int parNode = 0; parNode < _disc.nParPoints[parType]; parNode++) { + for (unsigned int parNode = 0; parNode < _disc.nParPoints[parType]; parNode++) + { // discretization special case: we get an algebraic equation at inner particle boundary - if (!_disc.parExactInt[parType] && parNode == 0u && _parGeomSurfToVol[parType] != _disc.SurfVolRatioSlab && _parCoreRadius[parType] == 0.0) + if (!_disc.parExactInt[parType] && parNode == 0u && _parGeomSurfToVol[parType] != _disc.SurfVolRatioSlab && + _parCoreRadius[parType] == 0.0) continue; - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) + { // row: jump over previous nodes add current component offset // col: jump over previous nodes, liquid phase and previous bound states tripletList.push_back(T(offset + parNode * idxr.strideParNode(parType) + comp, - offset + parNode * idxr.strideParNode(parType) + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd, + offset + parNode * idxr.strideParNode(parType) + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + bnd, 0.0)); } } @@ -1516,7 +2148,8 @@ class GeneralRateModelDG : public UnitOperationBase /** * @brief sets the sparsity pattern of the global Jacobian */ - void setParJacPattern(std::vector& tripletList, unsigned int parType, unsigned int colNode, unsigned int secIdx) { + void setParJacPattern(std::vector& tripletList, unsigned int parType, unsigned int colNode, unsigned int secIdx) + { calcParticleJacobianPattern(tripletList, parType, colNode, secIdx); @@ -1525,43 +2158,56 @@ class GeneralRateModelDG : public UnitOperationBase parBindingPattern_GRM(tripletList, parType, colNode); } /** - * @brief returns the offset between state order and parameter storage (bnd0comp0, bnd0comp1, bnd0comp2, bnd1comp0, bnd1comp1, bnd1comp2, ...) for one components bound state for a certain particle type + * @brief returns the offset between state order and parameter storage (bnd0comp0, bnd0comp1, bnd0comp2, bnd1comp0, + * bnd1comp1, bnd1comp2, ...) for one components bound state for a certain particle type * @todo find a different more elegant/easy way? */ - unsigned int getOffsetSurfDiff(unsigned int parType, unsigned int comp, unsigned int bnd) { - + unsigned int getOffsetSurfDiff(unsigned int parType, unsigned int comp, unsigned int bnd) + { + unsigned int offNextBound = 0; - + // we need to estimate the offset to the next parameter of current components next bound state // Ordering of particle surface diffusion: bnd0comp0, bnd0comp1, bnd0comp2, bnd1comp0, bnd1comp1, bnd1comp2 - for (unsigned int _comp = 0; _comp < _disc.nComp; _comp++) { - if(_comp < comp) // if its a component that occurs before comp, add all bound states of that component up to bnd + 1 (note that bound index starts at 0 -> +1). - offNextBound += std::min(bnd + 1u, _disc.nBound[parType * _disc.nComp + _comp]); - else // Otherwise, only add all previous (i.e. up to bnd) bound states of that component. This includes the current component itself (comp == _comp). - offNextBound += std::min(bnd, _disc.nBound[parType * _disc.nComp + _comp]); - } + for (unsigned int _comp = 0; _comp < _disc.nComp; _comp++) + { + if (_comp < comp) // if its a component that occurs before comp, add all bound states of that component up + // to bnd + 1 (note that bound index starts at 0 -> +1). + offNextBound += std::min(bnd + 1u, _disc.nBound[parType * _disc.nComp + _comp]); + else // Otherwise, only add all previous (i.e. up to bnd) bound states of that component. This includes the + // current component itself (comp == _comp). + offNextBound += std::min(bnd, _disc.nBound[parType * _disc.nComp + _comp]); + } return offNextBound; } /** * @brief calculate offsets between surface diffusion parameter storage and state ordering */ - void orderSurfDiff() { + void orderSurfDiff() + { Indexer idxr(_disc); - for (unsigned int type = 0; type < _disc.nParType; type++) { - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int bnd = 0; bnd < _disc.nBound[type * _disc.nComp + comp]; bnd++) { - _disc.offsetSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{ type }, ComponentIndex{ comp }) + bnd] = getOffsetSurfDiff(type, comp, bnd); + for (unsigned int type = 0; type < _disc.nParType; type++) + { + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int bnd = 0; bnd < _disc.nBound[type * _disc.nComp + comp]; bnd++) + { + _disc.offsetSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{type}, ComponentIndex{comp}) + bnd] = + getOffsetSurfDiff(type, comp, bnd); } } } } /** - * @brief analytically calculates the particle dispersion jacobian of the DGSEM (exact integration) for a single particle type and bead + * @brief analytically calculates the particle dispersion jacobian of the DGSEM (exact integration) for a single + * particle type and bead */ - int calcParticleDGSEMJacobian(unsigned int parType, unsigned int colNode, const active* const parDiff, const active* const parSurfDiff, const double* const invBetaP) { + int calcParticleDGSEMJacobian(unsigned int parType, unsigned int colNode, const active* const parDiff, + const active* const parSurfDiff, const double* const invBetaP) + { Indexer idxr(_disc); @@ -1569,76 +2215,130 @@ class GeneralRateModelDG : public UnitOperationBase unsigned int sCell = _disc.nParNode[parType] * idxr.strideParNode(parType); unsigned int sNode = idxr.strideParNode(parType); unsigned int sComp = 1u; - unsigned int offset = idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }); + unsigned int offset = idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}); unsigned int nNodes = _disc.nParNode[parType]; /* Special case */ - if (_disc.nParCell[parType] == 1) { + if (_disc.nParCell[parType] == 1) + { - linalg::BandedEigenSparseRowIterator jacIt(_globalJac, idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode})); // row iterator starting at first cell, first component - - insertParJacBlock(_disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + 0].block(0, nNodes + 1, nNodes, nNodes), jacIt, idxr, parDiff, parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, 1u, 0); + linalg::BandedEigenSparseRowIterator jacIt( + _globalJac, + idxr.offsetCp(ParticleTypeIndex{parType}, + ParticleIndex{colNode})); // row iterator starting at first cell, first component + + insertParJacBlock( + _disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + 0].block(0, nNodes + 1, nNodes, nNodes), jacIt, + idxr, parDiff, parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, 1u, 0); return 1; } /* Special case */ - if (_disc.nParCell[parType] == 2) { + if (_disc.nParCell[parType] == 2) + { + + linalg::BandedEigenSparseRowIterator jacIt( + _globalJac, + idxr.offsetCp(ParticleTypeIndex{parType}, + ParticleIndex{colNode})); // row iterator starting at first cell, first component - linalg::BandedEigenSparseRowIterator jacIt(_globalJac, idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode })); // row iterator starting at first cell, first component - - insertParJacBlock(_disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + 0].block(0, nNodes + 1, nNodes, 2 * nNodes), jacIt, idxr, parDiff, parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, 1u, 0); + insertParJacBlock( + _disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + 0].block(0, nNodes + 1, nNodes, 2 * nNodes), + jacIt, idxr, parDiff, parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, + 1u, 0); // right Bacobian block, iterator is already moved to second cell - insertParJacBlock(_disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + 1].block(0, 1, nNodes, 2 * nNodes), jacIt, idxr, parDiff, parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, 1u, -idxr.strideParShell(parType)); + insertParJacBlock(_disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + 1].block(0, 1, nNodes, 2 * nNodes), + jacIt, idxr, parDiff, parSurfDiff, invBetaP, + _binding[parType]->reactionQuasiStationarity(), parType, 1u, + -idxr.strideParShell(parType)); return 1; } /* Special case */ - if (_disc.nParCell[parType] == 3) { + if (_disc.nParCell[parType] == 3) + { + + linalg::BandedEigenSparseRowIterator jacIt( + _globalJac, idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}) + + idxr.strideParShell(parType)); // row iterator starting at first cell, first component - linalg::BandedEigenSparseRowIterator jacIt(_globalJac, idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }) + idxr.strideParShell(parType)); // row iterator starting at first cell, first component - - insertParJacBlock(_disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + 1].block(0, 1, nNodes, 3 * nNodes), jacIt, idxr, parDiff, parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, 1u, -idxr.strideParShell(parType)); + insertParJacBlock(_disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + 1].block(0, 1, nNodes, 3 * nNodes), + jacIt, idxr, parDiff, parSurfDiff, invBetaP, + _binding[parType]->reactionQuasiStationarity(), parType, 1u, + -idxr.strideParShell(parType)); } /* Inner cells (exist only if nCells >= 5) */ - if (_disc.nParCell[parType] >= 5) { + if (_disc.nParCell[parType] >= 5) + { + + linalg::BandedEigenSparseRowIterator jacIt( + _globalJac, + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}) + + idxr.strideParShell(parType) * 2); // row iterator starting at third cell, first component - linalg::BandedEigenSparseRowIterator jacIt(_globalJac, idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }) + idxr.strideParShell(parType) * 2); // row iterator starting at third cell, first component - - // insert all (nCol - 4) inner cell blocks + // insert all (nCol - 4) inner cell blocks for (unsigned int cell = 2; cell < _disc.nParCell[parType] - 2; cell++) - insertParJacBlock(_disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + cell], jacIt, idxr, parDiff, parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, 1u, -(idxr.strideParShell(parType) + idxr.strideParNode(parType))); + insertParJacBlock(_disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + cell], jacIt, idxr, parDiff, + parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, 1u, + -(idxr.strideParShell(parType) + idxr.strideParNode(parType))); } /* boundary cell neighbours (exist only if nCells >= 4) */ - if (_disc.nParCell[parType] >= 4) { - - linalg::BandedEigenSparseRowIterator jacIt(_globalJac, idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }) + idxr.strideParShell(parType)); // row iterator starting at second cell, first component - - insertParJacBlock(_disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + 1].block(0, 1, nNodes, 3 * nNodes + 1), jacIt, idxr, parDiff, parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, 1u, -idxr.strideParShell(parType)); + if (_disc.nParCell[parType] >= 4) + { - jacIt += (_disc.nParCell[parType] - 4) * idxr.strideParShell(parType); // move iterator to preultimate cell (already at third cell) - insertParJacBlock(_disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + _disc.nParCell[parType] - 2u].block(0, 0, nNodes, 3 * nNodes + 1), jacIt, idxr, parDiff, parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, 1u, -(idxr.strideParShell(parType) + idxr.strideParNode(parType))); + linalg::BandedEigenSparseRowIterator jacIt( + _globalJac, idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}) + + idxr.strideParShell(parType)); // row iterator starting at second cell, first component + + insertParJacBlock( + _disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + 1].block(0, 1, nNodes, 3 * nNodes + 1), jacIt, + idxr, parDiff, parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, 1u, + -idxr.strideParShell(parType)); + + jacIt += (_disc.nParCell[parType] - 4) * + idxr.strideParShell(parType); // move iterator to preultimate cell (already at third cell) + insertParJacBlock( + _disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + _disc.nParCell[parType] - 2u].block( + 0, 0, nNodes, 3 * nNodes + 1), + jacIt, idxr, parDiff, parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, + 1u, -(idxr.strideParShell(parType) + idxr.strideParNode(parType))); } /* boundary cells (exist only if nCells >= 3) */ - if (_disc.nParCell[parType] >= 3) { - - linalg::BandedEigenSparseRowIterator jacIt(_globalJac, idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode })); // row iterator starting at first cell, first component - - insertParJacBlock(_disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + 0].block(0, nNodes + 1, nNodes, 2 * nNodes + 1), jacIt, idxr, parDiff, parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, 1u, 0); + if (_disc.nParCell[parType] >= 3) + { - jacIt += (_disc.nParCell[parType] - 2) * idxr.strideParShell(parType); // move iterator to last cell (already at second cell) - insertParJacBlock(_disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + _disc.nParCell[parType] - 1u].block(0, 0, nNodes, 2 * nNodes + 1), jacIt, idxr, parDiff, parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, 1u, -(idxr.strideParShell(parType) + idxr.strideParNode(parType))); + linalg::BandedEigenSparseRowIterator jacIt( + _globalJac, + idxr.offsetCp(ParticleTypeIndex{parType}, + ParticleIndex{colNode})); // row iterator starting at first cell, first component + + insertParJacBlock( + _disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + 0].block(0, nNodes + 1, nNodes, 2 * nNodes + 1), + jacIt, idxr, parDiff, parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, + 1u, 0); + + jacIt += (_disc.nParCell[parType] - 2) * + idxr.strideParShell(parType); // move iterator to last cell (already at second cell) + insertParJacBlock( + _disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + _disc.nParCell[parType] - 1u].block( + 0, 0, nNodes, 2 * nNodes + 1), + jacIt, idxr, parDiff, parSurfDiff, invBetaP, _binding[parType]->reactionQuasiStationarity(), parType, + 1u, -(idxr.strideParShell(parType) + idxr.strideParNode(parType))); } return 1; } /** - * @brief analytically calculates the particle dispersion jacobian of the collocation DGSEM (inexact integration) for one particle type and bead + * @brief analytically calculates the particle dispersion jacobian of the collocation DGSEM (inexact integration) + * for one particle type and bead * @note deprecated, not further development */ - int calcParticleCollocationDGSEMJacobian(unsigned int parType, unsigned int colNode, const active* const parDiff, const active* const parSurfDiff, const double* const invBetaP) { + int calcParticleCollocationDGSEMJacobian(unsigned int parType, unsigned int colNode, const active* const parDiff, + const active* const parSurfDiff, const double* const invBetaP) + { Indexer idxr(_disc); @@ -1646,75 +2346,106 @@ class GeneralRateModelDG : public UnitOperationBase unsigned int sCell = _disc.nParNode[parType] * idxr.strideParNode(parType); unsigned int sNode = idxr.strideParNode(parType); unsigned int sComp = 1u; - unsigned int offset = idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }); + unsigned int offset = idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}); unsigned int nNodes = _disc.nParNode[parType]; // blocks to compute jacobian Eigen::MatrixXd dispBlock; Eigen::MatrixXd B = MatrixXd::Zero(nNodes, nNodes); - B(0, 0) = -1.0; B(nNodes - 1, nNodes - 1) = 1.0; + B(0, 0) = -1.0; + B(nNodes - 1, nNodes - 1) = 1.0; // special case: one cell -> diffBlock \in R^(nParNodes x nParNodes), GBlock = parPolyDerM - if (_disc.nParCell[parType] == 1) { + if (_disc.nParCell[parType] == 1) + { double invMap = (2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType]])); if (_parGeomSurfToVol[parType] == _disc.SurfVolRatioSlab || _parCoreRadius[parType] != 0.0) - dispBlock = invMap * invMap * (_disc.Dr[parType] - _disc.parInvWeights[parType].asDiagonal() * B) * _disc.parPolyDerM[parType]; + dispBlock = invMap * invMap * (_disc.Dr[parType] - _disc.parInvWeights[parType].asDiagonal() * B) * + _disc.parPolyDerM[parType]; - else { // special treatment of inner boundary node for spherical and cylindrical particles without particle core + else + { // special treatment of inner boundary node for spherical and cylindrical particles without particle core dispBlock = MatrixXd::Zero(nNodes, nNodes); // reduced system - dispBlock.block(1, 0, nNodes - 1, nNodes) - = (_disc.Dr[parType].block(1, 1, nNodes - 1, nNodes - 1) - - _disc.parInvWeights[parType].segment(1, nNodes - 1).asDiagonal() * B.block(1, 1, nNodes - 1, nNodes - 1)) - * _disc.parPolyDerM[parType].block(1, 0, nNodes - 1, nNodes); + dispBlock.block(1, 0, nNodes - 1, nNodes) = + (_disc.Dr[parType].block(1, 1, nNodes - 1, nNodes - 1) - + _disc.parInvWeights[parType].segment(1, nNodes - 1).asDiagonal() * + B.block(1, 1, nNodes - 1, nNodes - 1)) * + _disc.parPolyDerM[parType].block(1, 0, nNodes - 1, nNodes); // inner boundary node - dispBlock.block(0, 0, 1, nNodes) - = -(_disc.Ir[parType].segment(1, nNodes - 1).template cast().cwiseProduct( - _disc.parInvWeights[parType].segment(1, nNodes - 1).cwiseInverse()).cwiseProduct( - _disc.parPolyDerM[parType].block(1, 0, nNodes - 1, 1))).transpose() - * _disc.parPolyDerM[parType].block(1, 0, nNodes - 1, nNodes); + dispBlock.block(0, 0, 1, nNodes) = + -(_disc.Ir[parType] + .segment(1, nNodes - 1) + .template cast() + .cwiseProduct(_disc.parInvWeights[parType].segment(1, nNodes - 1).cwiseInverse()) + .cwiseProduct(_disc.parPolyDerM[parType].block(1, 0, nNodes - 1, 1))) + .transpose() * + _disc.parPolyDerM[parType].block(1, 0, nNodes - 1, nNodes); dispBlock *= invMap * invMap; } - // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the respective coefficients - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int i = 0; i < dispBlock.rows(); i++) { - for (unsigned int j = 0; j < dispBlock.cols(); j++) { + // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the respective + // coefficients + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int i = 0; i < dispBlock.rows(); i++) + { + for (unsigned int j = 0; j < dispBlock.cols(); j++) + { // handle liquid state // row: add component offset and go node strides from there for each dispersion block entry // col: add component offset and go node strides from there for each dispersion block entry - _globalJac.coeffRef(offset + comp * sComp + i * sNode, - offset + comp * sComp + j * sNode) - = -(static_cast(parDiff[comp])) * dispBlock(i, j); // - D_p * (Delta r / 2)^2 * (D_r D - M^-1 B D) + _globalJac.coeffRef(offset + comp * sComp + i * sNode, offset + comp * sComp + j * sNode) = + -(static_cast(parDiff[comp])) * + dispBlock(i, j); // - D_p * (Delta r / 2)^2 * (D_r D - M^-1 B D) // handle surface diffusion of bound states. binding is handled in residualKernel(). - if (_hasSurfaceDiffusion[parType]) { + if (_hasSurfaceDiffusion[parType]) + { int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) != 0.0) { + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) + { + if (static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) != 0.0) + { /* add surface diffusion dispersion block to liquid */ - // row: add current component offset and go node strides from there for each dispersion block entry - // col: jump oover liquid states, add current bound state offset and go node strides from there for each dispersion block entry - _globalJac.coeffRef(offset + comp * sComp + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode) - = -(static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) * invBetaP[comp]) * dispBlock(i, j); // - D_s * (1 / Beta_p) * (Delta r / 2)^2 * (D_r D - M^-1 B D) + // row: add current component offset and go node strides from there for each + // dispersion block entry col: jump oover liquid states, add current bound state + // offset and go node strides from there for each dispersion block entry + _globalJac.coeffRef( + offset + comp * sComp + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + bnd + j * sNode) = + -(static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) * + invBetaP[comp]) * + dispBlock(i, j); // - D_s * (1 / Beta_p) * (Delta r / 2)^2 * (D_r D - M^-1 B D) /* add surface diffusion dispersion block to solid */ - if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd]) { - // row: jump oover liquid states, add current bound state offset and go node strides from there for each dispersion block entry - // col: jump oover liquid states, add current bound state offset and go node strides from there for each dispersion block entry - _globalJac.coeffRef(offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode) - = -(static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * dispBlock(i, j); // - D_s * (Delta r / 2)^2 * (D_r D - M^-1 B D) - + if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd]) + { + // row: jump oover liquid states, add current bound state offset and go node + // strides from there for each dispersion block entry col: jump oover liquid + // states, add current bound state offset and go node strides from there for + // each dispersion block entry + _globalJac.coeffRef( + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + bnd + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + bnd + j * sNode) = + -(static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * + dispBlock(i, j); // - D_s * (Delta r / 2)^2 * (D_r D - M^-1 B D) } } } @@ -1723,7 +2454,8 @@ class GeneralRateModelDG : public UnitOperationBase } } } - else { + else + { /* boundary cells */ @@ -1752,68 +2484,105 @@ class GeneralRateModelDG : public UnitOperationBase double invMap = (2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType]])); // "standard" computation for slab-shaped particles and spherical, cylindrical particles without core - if (_parGeomSurfToVol[parType] == _disc.SurfVolRatioSlab || _parCoreRadius[parType] != 0.0) { + if (_parGeomSurfToVol[parType] == _disc.SurfVolRatioSlab || _parCoreRadius[parType] != 0.0) + { // dispBlock <- invMap^2 * ( D * G_l - M^-1 * B * [G_l - g^*] ) - bnd_dispBlock.block(0, 0, nNodes, nNodes + 1) = (_disc.Dr[_disc.offsetMetric[parType]] - _disc.parInvWeights[parType].asDiagonal() * B) * GBlock_l; - bnd_dispBlock.block(0, 0, nNodes, 2 * nNodes) += _disc.parInvWeights[parType].asDiagonal() * B * bnd_gStarDC; + bnd_dispBlock.block(0, 0, nNodes, nNodes + 1) = + (_disc.Dr[_disc.offsetMetric[parType]] - _disc.parInvWeights[parType].asDiagonal() * B) * GBlock_l; + bnd_dispBlock.block(0, 0, nNodes, 2 * nNodes) += + _disc.parInvWeights[parType].asDiagonal() * B * bnd_gStarDC; bnd_dispBlock *= invMap * invMap; } - else { // special treatment of inner boundary node for spherical and cylindrical particles without particle core + else + { // special treatment of inner boundary node for spherical and cylindrical particles without particle core // inner boundary node - bnd_dispBlock.block(0, 0, 1, nNodes + 1) - = -(_disc.Ir[_disc.offsetMetric[parType]].template cast().segment(1, nNodes - 1).cwiseProduct( - _disc.parInvWeights[parType].segment(1, nNodes - 1).cwiseInverse()).cwiseProduct( - _disc.parPolyDerM[parType].block(1, 0, nNodes - 1, 1))).transpose() - * GBlock_l.block(1, 0, nNodes - 1, nNodes + 1); + bnd_dispBlock.block(0, 0, 1, nNodes + 1) = + -(_disc.Ir[_disc.offsetMetric[parType]] + .template cast() + .segment(1, nNodes - 1) + .cwiseProduct(_disc.parInvWeights[parType].segment(1, nNodes - 1).cwiseInverse()) + .cwiseProduct(_disc.parPolyDerM[parType].block(1, 0, nNodes - 1, 1))) + .transpose() * + GBlock_l.block(1, 0, nNodes - 1, nNodes + 1); // reduced system for remaining nodes - bnd_dispBlock.block(1, 0, nNodes - 1, nNodes + 1) - = (_disc.Dr[_disc.offsetMetric[parType]].block(1, 1, nNodes - 1, nNodes - 1) - - _disc.parInvWeights[parType].segment(1, nNodes - 1).asDiagonal() * B.block(1, 1, nNodes - 1, nNodes - 1) - ) * GBlock_l.block(1, 0, nNodes - 1, nNodes + 1); + bnd_dispBlock.block(1, 0, nNodes - 1, nNodes + 1) = + (_disc.Dr[_disc.offsetMetric[parType]].block(1, 1, nNodes - 1, nNodes - 1) - + _disc.parInvWeights[parType].segment(1, nNodes - 1).asDiagonal() * + B.block(1, 1, nNodes - 1, nNodes - 1)) * + GBlock_l.block(1, 0, nNodes - 1, nNodes + 1); - bnd_dispBlock.block(1, 0, nNodes - 1, 2 * nNodes) - += _disc.parInvWeights[parType].segment(1, nNodes - 1).asDiagonal() * B.block(1, 1, nNodes - 1, nNodes - 1) * bnd_gStarDC.block(1, 0, nNodes - 1, 2 * nNodes); + bnd_dispBlock.block(1, 0, nNodes - 1, 2 * nNodes) += + _disc.parInvWeights[parType].segment(1, nNodes - 1).asDiagonal() * + B.block(1, 1, nNodes - 1, nNodes - 1) * bnd_gStarDC.block(1, 0, nNodes - 1, 2 * nNodes); // mapping bnd_dispBlock *= invMap * invMap; } - - // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the respective coefficients - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int i = 0; i < bnd_dispBlock.rows(); i++) { - for (unsigned int j = 0; j < bnd_dispBlock.cols(); j++) { + + // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the respective + // coefficients + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int i = 0; i < bnd_dispBlock.rows(); i++) + { + for (unsigned int j = 0; j < bnd_dispBlock.cols(); j++) + { // handle liquid state // inexact integration pattern is more sparse than a nNodes x 2*nNodes block. - if ((j <= nNodes) || (i == nNodes - 1)) { + if ((j <= nNodes) || (i == nNodes - 1)) + { // row: add component offset and go node strides from there for each dispersion block entry // col: add component offset and go node strides from there for each dispersion block entry - _globalJac.coeffRef(offset + comp * sComp + i * sNode, - offset + comp * sComp + j * sNode) - = -static_cast(parDiff[comp]) * bnd_dispBlock(i, j); // dispBlock <- D_p * [ M^-1 * M_r * G_l + invMap * ( D * G_l - M^-1 * B * [G_l - g^*] ) ] + _globalJac.coeffRef(offset + comp * sComp + i * sNode, offset + comp * sComp + j * sNode) = + -static_cast(parDiff[comp]) * + bnd_dispBlock(i, j); // dispBlock <- D_p * [ M^-1 * M_r * G_l + invMap * ( D * G_l - + // M^-1 * B * [G_l - g^*] ) ] // handle surface diffusion of bound states. binding is handled in residualKernel(). - if (_hasSurfaceDiffusion[parType]) { + if (_hasSurfaceDiffusion[parType]) + { int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) != 0.0) { - // row: add current component offset and go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and go node strides from there for each dispersion block entry - _globalJac.coeffRef(offset + comp * sComp + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode) - = -static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) * invBetaP[comp] * bnd_dispBlock(i, j); // dispBlock <- D_s * invBeta * [ M^-1 * M_r * G_l + invMap * ( D * G_l - M^-1 * B * [G_l - g^*] ) ] + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) + { + if (static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) != 0.0) + { + // row: add current component offset and go node strides from there for each + // dispersion block entry col: jump over liquid states, add current bound state + // offset and go node strides from there for each dispersion block entry + _globalJac.coeffRef( + offset + comp * sComp + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + bnd + j * sNode) = + -static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) * + invBetaP[comp] * + bnd_dispBlock(i, j); // dispBlock <- D_s * invBeta * [ M^-1 * M_r * G_l + + // invMap * ( D * G_l - M^-1 * B * [G_l - g^*] ) ] /* add surface diffusion dispersion block to solid */ - if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd]) { - // row: jump over liquid states, add current bound state offset and go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and go node strides from there for each dispersion block entry - _globalJac.coeffRef(offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode) - = -(static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * bnd_dispBlock(i, j); // - D_s * (Delta r / 2)^2 * (D_r D - M^-1 B D) - + if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd]) + { + // row: jump over liquid states, add current bound state offset and go node + // strides from there for each dispersion block entry col: jump over liquid + // states, add current bound state offset and go node strides from there for + // each dispersion block entry + _globalJac.coeffRef(offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + j * sNode) = + -(static_cast( + parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * + bnd_dispBlock(i, j); // - D_s * (Delta r / 2)^2 * (D_r D - M^-1 B D) } } } @@ -1824,7 +2593,8 @@ class GeneralRateModelDG : public UnitOperationBase } /* right boundary cell */ - invMap = (2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType] + _disc.nParCell[parType] - 1])); + invMap = + (2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType] + _disc.nParCell[parType] - 1])); // numerical flux contribution for left interface of right boundary cell -> d f^*_0 / d cp bnd_gStarDC.setZero(); @@ -1833,44 +2603,83 @@ class GeneralRateModelDG : public UnitOperationBase bnd_gStarDC *= 0.5; // dispBlock <- invMap * ( D_r * G_r - M^-1 * B * [G_r - g^*] ) bnd_dispBlock.setZero(); - bnd_dispBlock.block(0, nNodes - 1, nNodes, nNodes + 1) = (_disc.Dr[_disc.offsetMetric[parType] + _disc.nParCell[parType] - 1] - _disc.parInvWeights[parType].asDiagonal() * B) * GBlock_r; - bnd_dispBlock.block(0, 0, nNodes, 2 * nNodes) += _disc.parInvWeights[parType].asDiagonal() * B * bnd_gStarDC; + bnd_dispBlock.block(0, nNodes - 1, nNodes, nNodes + 1) = + (_disc.Dr[_disc.offsetMetric[parType] + _disc.nParCell[parType] - 1] - + _disc.parInvWeights[parType].asDiagonal() * B) * + GBlock_r; + bnd_dispBlock.block(0, 0, nNodes, 2 * nNodes) += + _disc.parInvWeights[parType].asDiagonal() * B * bnd_gStarDC; bnd_dispBlock *= invMap * invMap; - // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the respective coefficients - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int i = 0; i < bnd_dispBlock.rows(); i++) { - for (unsigned int j = 0; j < bnd_dispBlock.cols(); j++) { + // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the respective + // coefficients + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int i = 0; i < bnd_dispBlock.rows(); i++) + { + for (unsigned int j = 0; j < bnd_dispBlock.cols(); j++) + { // handle liquid state // inexact integration pattern is more sparse than a nNodes x 2*nNodes block. - if ((j <= nNodes) || (i == nNodes - 1)) { - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: add component offset and jump over previous cells. Go back one cell and go node strides from there for each dispersion block entry - _globalJac.coeffRef(offset + comp * sComp + (_disc.nParCell[parType] - 1) * sCell + i * sNode, - offset + comp * sComp + (_disc.nParCell[parType] - 2) * sCell + j * sNode) - = -static_cast(parDiff[comp]) * bnd_dispBlock(i, j); // dispBlock <- D_p * [ M^-1 * M_r * G_l + invMap * ( D * G_l - M^-1 * B * [G_l - g^*] ) ] + if ((j <= nNodes) || (i == nNodes - 1)) + { + // row: add component offset and jump over previous cells. Go node strides from there for + // each dispersion block entry col: add component offset and jump over previous cells. Go + // back one cell and go node strides from there for each dispersion block entry + _globalJac.coeffRef( + offset + comp * sComp + (_disc.nParCell[parType] - 1) * sCell + i * sNode, + offset + comp * sComp + (_disc.nParCell[parType] - 2) * sCell + j * sNode) = + -static_cast(parDiff[comp]) * + bnd_dispBlock(i, j); // dispBlock <- D_p * [ M^-1 * M_r * G_l + invMap * ( D * G_l - + // M^-1 * B * [G_l - g^*] ) ] // handle surface diffusion of bound states. binding is handled in residualKernel(). - if (_hasSurfaceDiffusion[parType]) { + if (_hasSurfaceDiffusion[parType]) + { int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) != 0.0) { - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and jump over previous cells. Go back one cell and go node strides from there for each dispersion block entry - _globalJac.coeffRef(offset + comp * sComp + (_disc.nParCell[parType] - 1) * sCell + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + (_disc.nParCell[parType] - 2) * sCell + j * sNode) - = -static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) * invBetaP[comp] * bnd_dispBlock(i, j); // dispBlock <- D_s * invBeta * [ M^-1 * M_r * G_l + invMap * ( D * G_l - M^-1 * B * [G_l - g^*] ) ] + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) + { + if (static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) != 0.0) + { + // row: add component offset and jump over previous cells. Go node strides from + // there for each dispersion block entry col: jump over liquid states, add + // current bound state offset and jump over previous cells. Go back one cell and + // go node strides from there for each dispersion block entry + _globalJac.coeffRef( + offset + comp * sComp + (_disc.nParCell[parType] - 1) * sCell + i * sNode, + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{comp}) + + bnd + (_disc.nParCell[parType] - 2) * sCell + j * sNode) = + -static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) * + invBetaP[comp] * + bnd_dispBlock(i, j); // dispBlock <- D_s * invBeta * [ M^-1 * M_r * G_l + + // invMap * ( D * G_l - M^-1 * B * [G_l - g^*] ) ] /* add surface diffusion dispersion block to solid */ - if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd]) { - // row: jump over previous cells and over liquid states, add current bound state offset. go node strides from there for each dispersion block entry - // col: jump over previous cells and over liquid states, add current bound state offset. go back one cell and go node strides from there for each dispersion block entry - _globalJac.coeffRef(offset + (_disc.nParCell[parType] - 1) * sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + i * sNode, - offset + (_disc.nParCell[parType] - 2) * sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode) - = -(static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * bnd_dispBlock(i, j); // - D_s * (Delta r / 2)^2 * (D_r D - M^-1 B D) - + if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd]) + { + // row: jump over previous cells and over liquid states, add current bound + // state offset. go node strides from there for each dispersion block entry + // col: jump over previous cells and over liquid states, add current bound + // state offset. go back one cell and go node strides from there for each + // dispersion block entry + _globalJac.coeffRef(offset + (_disc.nParCell[parType] - 1) * sCell + + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + i * sNode, + offset + (_disc.nParCell[parType] - 2) * sCell + + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + j * sNode) = + -(static_cast( + parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * + bnd_dispBlock(i, j); // - D_s * (Delta r / 2)^2 * (D_r D - M^-1 B D) } } } @@ -1900,52 +2709,90 @@ class GeneralRateModelDG : public UnitOperationBase dispBlock.setZero(); // dispersion block part without metrics - dispBlock.block(0, nNodes - 1, nNodes, nNodes + 2) = -1.0 * _disc.parInvWeights[parType].asDiagonal() * B * GBlock; + dispBlock.block(0, nNodes - 1, nNodes, nNodes + 2) = + -1.0 * _disc.parInvWeights[parType].asDiagonal() * B * GBlock; dispBlock.block(0, 0, nNodes, 3 * nNodes) += _disc.parInvWeights[parType].asDiagonal() * B * gStarDC; dispBlock *= invMap * invMap; - for (int cell = 1; cell < _disc.nParCell[parType] - 1; cell++) { + for (int cell = 1; cell < _disc.nParCell[parType] - 1; cell++) + { double invMap = (2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell])); // add metric part, dependent on current cell - dispBlock.block(0, nNodes - 1, nNodes, nNodes + 2) += _disc.Dr[_disc.offsetMetric[parType] + cell] * GBlock * invMap * invMap; + dispBlock.block(0, nNodes - 1, nNodes, nNodes + 2) += + _disc.Dr[_disc.offsetMetric[parType] + cell] * GBlock * invMap * invMap; - // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the respective coefficients - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int i = 0; i < dispBlock.rows(); i++) { - for (unsigned int j = 0; j < dispBlock.cols(); j++) { + // fill the jacobian: add dispersion block for each unbound and bound component, adjusted for the + // respective coefficients + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int i = 0; i < dispBlock.rows(); i++) + { + for (unsigned int j = 0; j < dispBlock.cols(); j++) + { // handle liquid state // pattern is more sparse than a nNodes x 3*nNodes block. - if ((j >= nNodes - 1 && j <= 2 * nNodes) || - (i == 0 && j <= 2 * nNodes) || - (i == nNodes - 1 && j >= nNodes - 1)) { - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: add component offset and jump over previous cells. Go back one cell and go node strides from there for each dispersion block entry + if ((j >= nNodes - 1 && j <= 2 * nNodes) || (i == 0 && j <= 2 * nNodes) || + (i == nNodes - 1 && j >= nNodes - 1)) + { + // row: add component offset and jump over previous cells. Go node strides from there + // for each dispersion block entry col: add component offset and jump over previous + // cells. Go back one cell and go node strides from there for each dispersion block + // entry _globalJac.coeffRef(offset + comp * sComp + cell * sCell + i * sNode, - offset + comp * sComp + (cell - 1) * sCell + j * sNode) - = -static_cast(parDiff[comp]) * dispBlock(i, j); // dispBlock <- D_p * [ M^-1 * M_r * G_l + invMap * ( D * G_l - M^-1 * B * [G_l - g^*] ) ] + offset + comp * sComp + (cell - 1) * sCell + j * sNode) = + -static_cast(parDiff[comp]) * + dispBlock(i, j); // dispBlock <- D_p * [ M^-1 * M_r * G_l + invMap * ( D * G_l - + // M^-1 * B * [G_l - g^*] ) ] // handle surface diffusion of bound states. binding is handled in residualKernel(). - if (_hasSurfaceDiffusion[parType]) { + if (_hasSurfaceDiffusion[parType]) + { int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) { - if (static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) != 0.0) { - // row: add component offset and jump over previous cells. Go node strides from there for each dispersion block entry - // col: jump over liquid states, add current bound state offset and jump over previous cells. Go back one cell and go node strides from there for each dispersion block entry + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++) + { + if (static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) != + 0.0) + { + // row: add component offset and jump over previous cells. Go node strides + // from there for each dispersion block entry col: jump over liquid states, + // add current bound state offset and jump over previous cells. Go back one + // cell and go node strides from there for each dispersion block entry _globalJac.coeffRef(offset + comp * sComp + cell * sCell + i * sNode, - offset + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + (cell - 1) * sCell + j * sNode) - = -static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) * invBetaP[comp] * dispBlock(i, j); // dispBlock <- D_s * invBeta * [ M^-1 * M_r * G_l + invMap * ( D * G_l - M^-1 * B * [G_l - g^*] ) ] + offset + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + (cell - 1) * sCell + j * sNode) = + -static_cast( + parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) * + invBetaP[comp] * + dispBlock(i, j); // dispBlock <- D_s * invBeta * [ M^-1 * M_r * G_l + + // invMap * ( D * G_l - M^-1 * B * [G_l - g^*] ) ] /* add surface diffusion dispersion block to solid */ - if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd]) { - // row: jump over previous cells and liquid states, add current bound state offset and go node strides from there for each dispersion block entry - // col: jump over previous cells and liquid states, go back one cell and add current bound state offset and go node strides from there for each dispersion block entry - _globalJac.coeffRef(offset + cell * sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + i * sNode, - offset + (cell - 1) * sCell + idxr.strideParLiquid() + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ comp }) + bnd + j * sNode) - = -(static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * dispBlock(i, j); // - D_s * (Delta r / 2)^2 * (D_r D - M^-1 B D) - + if (!qsReaction[idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd]) + { + // row: jump over previous cells and liquid states, add current bound + // state offset and go node strides from there for each dispersion block + // entry col: jump over previous cells and liquid states, go back one + // cell and add current bound state offset and go node strides from + // there for each dispersion block entry + _globalJac.coeffRef(offset + cell * sCell + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + i * sNode, + offset + (cell - 1) * sCell + + idxr.strideParLiquid() + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, + ComponentIndex{comp}) + + bnd + j * sNode) = + -(static_cast( + parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * + dispBlock(i, j); // - D_s * (Delta r / 2)^2 * (D_r D - M^-1 B D) } } } @@ -1955,7 +2802,8 @@ class GeneralRateModelDG : public UnitOperationBase } } // substract metric part in preparation of next iteration - dispBlock.block(0, nNodes - 1, nNodes, nNodes + 2) -= _disc.Dr[_disc.offsetMetric[parType] + cell] * GBlock * invMap * invMap; + dispBlock.block(0, nNodes - 1, nNodes, nNodes + 2) -= + _disc.Dr[_disc.offsetMetric[parType] + cell] * GBlock * invMap * invMap; } } // if nCells > 1 @@ -1963,36 +2811,44 @@ class GeneralRateModelDG : public UnitOperationBase return 1; } /** - * @brief adds jacobian entries which have been overwritten by the binding kernel (only use for surface diffusion combined with kinetic binding) + * @brief adds jacobian entries which have been overwritten by the binding kernel (only use for surface diffusion + * combined with kinetic binding) * @detail only adds the entries d RHS_i / d c^s_i, which lie on the diagonal * @parType[in] current particle type * @parSurfDiff[in] pointer to particle surface diffusion at current section and particle type */ - int addSolidDGentries(unsigned int parType, const active* const parSurfDiff) { + int addSolidDGentries(unsigned int parType, const active* const parSurfDiff) + { if (!_disc.parExactInt[parType]) return addSolidDGentries_inexInt(parType, parSurfDiff); Indexer idxr(_disc); - for (unsigned int col = 0; col < _disc.nPoints; col++) { + for (unsigned int col = 0; col < _disc.nPoints; col++) + { // Get jacobian iterator at first solid entry of first particle of current type - linalg::BandedEigenSparseRowIterator jac(_globalJac, idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ col }) + idxr.strideParLiquid()); + linalg::BandedEigenSparseRowIterator jac( + _globalJac, idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{col}) + idxr.strideParLiquid()); for (unsigned int cell = 0; cell < _disc.nParCell[parType]; cell++) - addDiagonalSolidJacobianEntries(_disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + cell].block(0, _disc.nParNode[parType] + 1, _disc.nParNode[parType], _disc.nParNode[parType]), + addDiagonalSolidJacobianEntries( + _disc.DGjacParDispBlocks[_disc.offsetMetric[parType] + cell].block( + 0, _disc.nParNode[parType] + 1, _disc.nParNode[parType], _disc.nParNode[parType]), jac, idxr, parSurfDiff, _binding[parType]->reactionQuasiStationarity(), parType); } return 1; } /** - * @brief adds jacobian entries which have been overwritten by the binding kernel (only use for surface diffusion combined with kinetic binding) + * @brief adds jacobian entries which have been overwritten by the binding kernel (only use for surface diffusion + * combined with kinetic binding) * @detail only adds the entries d RHS_i / d c^s_i, which lie on the diagonal * @parType[in] current particle type * @parSurfDiff[in] pointer to particle surface diffusion at current section and particle type */ - int addSolidDGentries_inexInt(unsigned int parType, const active* const parSurfDiff) { + int addSolidDGentries_inexInt(unsigned int parType, const active* const parSurfDiff) + { Indexer idxr(_disc); @@ -2005,39 +2861,47 @@ class GeneralRateModelDG : public UnitOperationBase // blocks to compute jacobian Eigen::MatrixXd dispBlock; Eigen::MatrixXd B = MatrixXd::Zero(nNodes, nNodes); - B(0, 0) = -1.0; B(nNodes - 1, nNodes - 1) = 1.0; + B(0, 0) = -1.0; + B(nNodes - 1, nNodes - 1) = 1.0; // special case: one cell -> diffBlock \in R^(nParNodes x nParNodes), GBlock = parPolyDerM - if (_disc.nParCell[parType] == 1) { + if (_disc.nParCell[parType] == 1) + { double invMap = (2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType]])); if (_parGeomSurfToVol[parType] == _disc.SurfVolRatioSlab || _parCoreRadius[parType] != 0.0) - dispBlock = invMap * invMap * (_disc.Dr[parType] - _disc.parInvWeights[parType].asDiagonal() * B) * _disc.parPolyDerM[parType]; + dispBlock = invMap * invMap * (_disc.Dr[parType] - _disc.parInvWeights[parType].asDiagonal() * B) * + _disc.parPolyDerM[parType]; - else { // special treatment of inner boundary node for spherical and cylindrical particles without particle core + else + { // special treatment of inner boundary node for spherical and cylindrical particles without particle core dispBlock = MatrixXd::Zero(nNodes, nNodes); // reduced system - dispBlock.block(1, 0, nNodes - 1, nNodes) - = (_disc.Dr[parType].block(1, 1, nNodes - 1, nNodes - 1) - - _disc.parInvWeights[parType].segment(1, nNodes - 1).asDiagonal() * B.block(1, 1, nNodes - 1, nNodes - 1)) - * _disc.parPolyDerM[parType].block(1, 0, nNodes - 1, nNodes); + dispBlock.block(1, 0, nNodes - 1, nNodes) = + (_disc.Dr[parType].block(1, 1, nNodes - 1, nNodes - 1) - + _disc.parInvWeights[parType].segment(1, nNodes - 1).asDiagonal() * + B.block(1, 1, nNodes - 1, nNodes - 1)) * + _disc.parPolyDerM[parType].block(1, 0, nNodes - 1, nNodes); // inner boundary node - dispBlock.block(0, 0, 1, nNodes) - = -(_disc.Ir[parType].segment(1, nNodes - 1).template cast().cwiseProduct( - _disc.parInvWeights[parType].segment(1, nNodes - 1).cwiseInverse()).cwiseProduct( - _disc.parPolyDerM[parType].block(1, 0, nNodes - 1, 1))).transpose() - * _disc.parPolyDerM[parType].block(1, 0, nNodes - 1, nNodes); + dispBlock.block(0, 0, 1, nNodes) = + -(_disc.Ir[parType] + .segment(1, nNodes - 1) + .template cast() + .cwiseProduct(_disc.parInvWeights[parType].segment(1, nNodes - 1).cwiseInverse()) + .cwiseProduct(_disc.parPolyDerM[parType].block(1, 0, nNodes - 1, 1))) + .transpose() * + _disc.parPolyDerM[parType].block(1, 0, nNodes - 1, nNodes); dispBlock *= invMap * invMap; } for (unsigned int colNode = 0; colNode < _disc.nPoints; colNode++) { - unsigned int offset = idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }); + unsigned int offset = idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}); // start at first solid entry linalg::BandedEigenSparseRowIterator jac(_globalJac, offset + idxr.strideParLiquid()); @@ -2049,14 +2913,16 @@ class GeneralRateModelDG : public UnitOperationBase { if (static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) != 0.0) { - jac[0] += -(static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * dispBlock(node, node); + jac[0] += -(static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * + dispBlock(node, node); } } } } } } - else { + else + { /* boundary cells */ // initialize dispersion and metric block matrices @@ -2085,45 +2951,61 @@ class GeneralRateModelDG : public UnitOperationBase bnd_gStarDC *= 0.5; // "standard" computation for slab-shaped particles and spherical, cylindrical particles without core - if (_parGeomSurfToVol[parType] == _disc.SurfVolRatioSlab || _parCoreRadius[parType] != 0.0) { + if (_parGeomSurfToVol[parType] == _disc.SurfVolRatioSlab || _parCoreRadius[parType] != 0.0) + { // dispBlock <- invMap^2 * ( D * G_l - M^-1 * B * [G_l - g^*] ) - bnd_dispBlock.block(0, 0, nNodes, nNodes + 1) = (_disc.Dr[_disc.offsetMetric[parType]] - _disc.parInvWeights[parType].asDiagonal() * B) * GBlock_l; - bnd_dispBlock.block(0, 0, nNodes, 2 * nNodes) += _disc.parInvWeights[parType].asDiagonal() * B * bnd_gStarDC; + bnd_dispBlock.block(0, 0, nNodes, nNodes + 1) = + (_disc.Dr[_disc.offsetMetric[parType]] - _disc.parInvWeights[parType].asDiagonal() * B) * GBlock_l; + bnd_dispBlock.block(0, 0, nNodes, 2 * nNodes) += + _disc.parInvWeights[parType].asDiagonal() * B * bnd_gStarDC; bnd_dispBlock *= invMap * invMap; } - else { // special treatment of inner boundary node for spherical and cylindrical particles without particle core + else + { // special treatment of inner boundary node for spherical and cylindrical particles without particle core // inner boundary node - bnd_dispBlock.block(0, 0, 1, nNodes + 1) - = -(_disc.Ir[_disc.offsetMetric[parType]].template cast().segment(1, nNodes - 1).cwiseProduct( - _disc.parInvWeights[parType].segment(1, nNodes - 1).cwiseInverse()).cwiseProduct( - _disc.parPolyDerM[parType].block(1, 0, nNodes - 1, 1))).transpose() - * GBlock_l.block(1, 0, nNodes - 1, nNodes + 1); + bnd_dispBlock.block(0, 0, 1, nNodes + 1) = + -(_disc.Ir[_disc.offsetMetric[parType]] + .template cast() + .segment(1, nNodes - 1) + .cwiseProduct(_disc.parInvWeights[parType].segment(1, nNodes - 1).cwiseInverse()) + .cwiseProduct(_disc.parPolyDerM[parType].block(1, 0, nNodes - 1, 1))) + .transpose() * + GBlock_l.block(1, 0, nNodes - 1, nNodes + 1); // reduced system for remaining nodes - bnd_dispBlock.block(1, 0, nNodes - 1, nNodes + 1) - = (_disc.Dr[_disc.offsetMetric[parType]].block(1, 1, nNodes - 1, nNodes - 1) - - _disc.parInvWeights[parType].segment(1, nNodes - 1).asDiagonal() * B.block(1, 1, nNodes - 1, nNodes - 1) - ) * GBlock_l.block(1, 0, nNodes - 1, nNodes + 1); + bnd_dispBlock.block(1, 0, nNodes - 1, nNodes + 1) = + (_disc.Dr[_disc.offsetMetric[parType]].block(1, 1, nNodes - 1, nNodes - 1) - + _disc.parInvWeights[parType].segment(1, nNodes - 1).asDiagonal() * + B.block(1, 1, nNodes - 1, nNodes - 1)) * + GBlock_l.block(1, 0, nNodes - 1, nNodes + 1); - bnd_dispBlock.block(1, 0, nNodes - 1, 2 * nNodes) - += _disc.parInvWeights[parType].segment(1, nNodes - 1).asDiagonal() * B.block(1, 1, nNodes - 1, nNodes - 1) * bnd_gStarDC.block(1, 0, nNodes - 1, 2 * nNodes); + bnd_dispBlock.block(1, 0, nNodes - 1, 2 * nNodes) += + _disc.parInvWeights[parType].segment(1, nNodes - 1).asDiagonal() * + B.block(1, 1, nNodes - 1, nNodes - 1) * bnd_gStarDC.block(1, 0, nNodes - 1, 2 * nNodes); // mapping bnd_dispBlock *= invMap * invMap; } - for (unsigned int colNode = 0; colNode < _disc.nPoints; colNode++) { + for (unsigned int colNode = 0; colNode < _disc.nPoints; colNode++) + { - unsigned int offset = idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }); + unsigned int offset = idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}); // start at first solid entry of first cell linalg::BandedEigenSparseRowIterator jac_left(_globalJac, offset + idxr.strideParLiquid()); - for (unsigned int node = 0; node < _disc.nParNode[parType]; node++, jac_left += idxr.strideParLiquid()) { - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++, ++jac_left) { - if (static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) != 0.0) { - jac_left[0] += -(static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * bnd_dispBlock(node, node); + for (unsigned int node = 0; node < _disc.nParNode[parType]; node++, jac_left += idxr.strideParLiquid()) + { + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++, ++jac_left) + { + if (static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) != 0.0) + { + jac_left[0] += + -(static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * + bnd_dispBlock(node, node); } } } @@ -2142,21 +3024,32 @@ class GeneralRateModelDG : public UnitOperationBase bnd_gStarDC *= 0.5; // dispBlock <- invMap * ( D_r * G_r - M^-1 * B * [G_r - g^*] ) bnd_dispBlock.setZero(); - bnd_dispBlock.block(0, nNodes - 1, nNodes, nNodes + 1) = (_disc.Dr[_disc.offsetMetric[parType] + _cell] - _disc.parInvWeights[parType].asDiagonal() * B) * GBlock_r; - bnd_dispBlock.block(0, 0, nNodes, 2 * nNodes) += _disc.parInvWeights[parType].asDiagonal() * B * bnd_gStarDC; + bnd_dispBlock.block(0, nNodes - 1, nNodes, nNodes + 1) = + (_disc.Dr[_disc.offsetMetric[parType] + _cell] - _disc.parInvWeights[parType].asDiagonal() * B) * + GBlock_r; + bnd_dispBlock.block(0, 0, nNodes, 2 * nNodes) += + _disc.parInvWeights[parType].asDiagonal() * B * bnd_gStarDC; bnd_dispBlock *= invMap * invMap; - for (unsigned int colNode = 0; colNode < _disc.nPoints; colNode++) { + for (unsigned int colNode = 0; colNode < _disc.nPoints; colNode++) + { - unsigned int offset = idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }); + unsigned int offset = idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}); // start at first solid entry of last cell - linalg::BandedEigenSparseRowIterator jac_right(_globalJac, offset + (_disc.nParCell[parType] - 1) * sCell + idxr.strideParLiquid()); + linalg::BandedEigenSparseRowIterator jac_right( + _globalJac, offset + (_disc.nParCell[parType] - 1) * sCell + idxr.strideParLiquid()); - for (unsigned int node = 0; node < _disc.nParNode[parType]; node++, jac_right += idxr.strideParLiquid()) { - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++, ++jac_right) { - if (static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) != 0.0) { - jac_right[0] += -(static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * bnd_dispBlock(node, _disc.nParNode[parType] + node); + for (unsigned int node = 0; node < _disc.nParNode[parType]; node++, jac_right += idxr.strideParLiquid()) + { + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++, ++jac_right) + { + if (static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) != 0.0) + { + jac_right[0] += + -(static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * + bnd_dispBlock(node, _disc.nParNode[parType] + node); } } } @@ -2165,7 +3058,7 @@ class GeneralRateModelDG : public UnitOperationBase /* inner cells */ - // auxiliary block [ d g(c) / d c ] for inner cells + // auxiliary block [ d g(c) / d c ] for inner cells MatrixXd GBlock = MatrixXd::Zero(nNodes, nNodes + 2); GBlock.block(0, 1, nNodes, nNodes) = _disc.parPolyDerM[parType]; GBlock(0, 0) -= 0.5 * _disc.parInvWeights[parType][0]; @@ -2183,31 +3076,40 @@ class GeneralRateModelDG : public UnitOperationBase dispBlock.setZero(); // dispersion block part without metrics - dispBlock.block(0, nNodes - 1, nNodes, nNodes + 2) = -1.0 * _disc.parInvWeights[parType].asDiagonal() * B * GBlock; + dispBlock.block(0, nNodes - 1, nNodes, nNodes + 2) = + -1.0 * _disc.parInvWeights[parType].asDiagonal() * B * GBlock; dispBlock.block(0, 0, nNodes, 3 * nNodes) += _disc.parInvWeights[parType].asDiagonal() * B * gStarDC; - for (int cell = 1; cell < _disc.nParCell[parType] - 1; cell++) { + for (int cell = 1; cell < _disc.nParCell[parType] - 1; cell++) + { // add metric part, dependent on current cell - dispBlock.block(0, nNodes - 1, nNodes, nNodes + 2) += _disc.Dr[_disc.offsetMetric[parType] + cell] * GBlock; + dispBlock.block(0, nNodes - 1, nNodes, nNodes + 2) += + _disc.Dr[_disc.offsetMetric[parType] + cell] * GBlock; invMap = (2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[parType] + cell])); dispBlock *= invMap * invMap; - for (unsigned int colNode = 0; colNode < _disc.nPoints; colNode++) { + for (unsigned int colNode = 0; colNode < _disc.nPoints; colNode++) + { - unsigned int offset = idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }); + unsigned int offset = idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}); // start at first solid entry of current inner cell - linalg::BandedEigenSparseRowIterator jac_inner(_globalJac, offset + cell * sCell + idxr.strideParLiquid()); + linalg::BandedEigenSparseRowIterator jac_inner(_globalJac, + offset + cell * sCell + idxr.strideParLiquid()); - for (unsigned int node = 0; node < _disc.nParNode[parType]; node++, jac_inner += idxr.strideParLiquid()) + for (unsigned int node = 0; node < _disc.nParNode[parType]; + node++, jac_inner += idxr.strideParLiquid()) { - for (unsigned int comp = 0; comp < _disc.nComp; comp++) + for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; bnd++, ++jac_inner) + for (unsigned int bnd = 0; bnd < _disc.nBound[parType * _disc.nComp + comp]; + bnd++, ++jac_inner) { if (static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)]) != 0.0) { - jac_inner[0] += -(static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * dispBlock(node, _disc.nParNode[parType] + node); + jac_inner[0] += + -(static_cast(parSurfDiff[getOffsetSurfDiff(parType, comp, bnd)])) * + dispBlock(node, _disc.nParNode[parType] + node); } } } @@ -2216,7 +3118,8 @@ class GeneralRateModelDG : public UnitOperationBase // substract metric part in preparation of next iteration dispBlock /= invMap * invMap; - dispBlock.block(0, nNodes - 1, nNodes, nNodes + 2) -= _disc.Dr[_disc.offsetMetric[parType] + cell] * GBlock; + dispBlock.block(0, nNodes - 1, nNodes, nNodes + 2) -= + _disc.Dr[_disc.offsetMetric[parType] + cell] * GBlock; } } // if nCells > 1 @@ -2233,12 +3136,18 @@ class GeneralRateModelDG : public UnitOperationBase * @param [in] strideDead how many (dead) states to be jumped over after each state block * @param [in] nStates how many states are concerned, defaults to nComp */ - void addJacBlock(Eigen::MatrixXd block, linalg::BandedEigenSparseRowIterator& jac, int offRowToCol, Indexer& idxr, unsigned int nCells, unsigned int strideNode, unsigned int nStates, unsigned int strideDead = 0) { + void addJacBlock(Eigen::MatrixXd block, linalg::BandedEigenSparseRowIterator& jac, int offRowToCol, Indexer& idxr, + unsigned int nCells, unsigned int strideNode, unsigned int nStates, unsigned int strideDead = 0) + { - for (unsigned int cell = 0; cell < nCells; cell++) { - for (unsigned int i = 0; i < block.rows(); i++, jac += strideDead) { - for (unsigned int state = 0; state < nStates; state++, ++jac) { - for (unsigned int j = 0; j < block.cols(); j++) { + for (unsigned int cell = 0; cell < nCells; cell++) + { + for (unsigned int i = 0; i < block.rows(); i++, jac += strideDead) + { + for (unsigned int state = 0; state < nStates; state++, ++jac) + { + for (unsigned int j = 0; j < block.cols(); j++) + { // row: at current node component // col: jump to node j jac[(j - i) * strideNode + offRowToCol] += block(i, j); @@ -2256,17 +3165,28 @@ class GeneralRateModelDG : public UnitOperationBase * @param [in] nonKinetic pointer to binding kinetics * @param [in] type particle type */ - template - void addDiagonalSolidJacobianEntries(Eigen::MatrixXd block, linalg::BandedEigenSparseRowIterator& jac, Indexer& idxr, ParamType* surfDiff, const int* nonKinetic, unsigned int type) { - - for (unsigned int i = 0; i < block.rows(); i++, jac += idxr.strideParLiquid()) { - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int bnd = 0; bnd < _disc.nBound[type * _disc.nComp + comp]; bnd++, ++jac) { - if (static_cast(surfDiff[_disc.offsetSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{ type }, ComponentIndex{ comp }) + bnd]]) != 0.0 - && !nonKinetic[idxr.offsetBoundComp(ParticleTypeIndex{ type }, ComponentIndex{ comp }) + bnd]) { + template + void addDiagonalSolidJacobianEntries(Eigen::MatrixXd block, linalg::BandedEigenSparseRowIterator& jac, + Indexer& idxr, ParamType* surfDiff, const int* nonKinetic, unsigned int type) + { + + for (unsigned int i = 0; i < block.rows(); i++, jac += idxr.strideParLiquid()) + { + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int bnd = 0; bnd < _disc.nBound[type * _disc.nComp + comp]; bnd++, ++jac) + { + if (static_cast(surfDiff[_disc.offsetSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{type}, + ComponentIndex{comp}) + + bnd]]) != 0.0 && + !nonKinetic[idxr.offsetBoundComp(ParticleTypeIndex{type}, ComponentIndex{comp}) + bnd]) + { // row, col: at current node and bound state - jac[0] += block(i, i) - * static_cast(surfDiff[_disc.offsetSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{ type }, ComponentIndex{ comp }) + bnd]]); + jac[0] += block(i, i) * + static_cast( + surfDiff[_disc.offsetSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{type}, + ComponentIndex{comp}) + + bnd]]); } } } @@ -2285,40 +3205,66 @@ class GeneralRateModelDG : public UnitOperationBase * @param [in] nBlocks number of blocks, i.e. cells/elements, to be inserted * @param [in] offRowToCol column to row offset (i.e. start at upper left corner of block) */ - void insertParJacBlock(Eigen::MatrixXd block, linalg::BandedEigenSparseRowIterator& jac, Indexer& idxr, const active* const parDiff, const active* const surfDiff, const double* const beta_p, const int* nonKinetic, unsigned int type, unsigned int nBlocks, int offRowToCol) { + void insertParJacBlock(Eigen::MatrixXd block, linalg::BandedEigenSparseRowIterator& jac, Indexer& idxr, + const active* const parDiff, const active* const surfDiff, const double* const beta_p, + const int* nonKinetic, unsigned int type, unsigned int nBlocks, int offRowToCol) + { - for (unsigned int cell = 0; cell < nBlocks; cell++) { - for (unsigned int i = 0; i < block.rows(); i++) { - for (unsigned int comp = 0; comp < _disc.nComp; comp++, ++jac) { - for (unsigned int j = 0; j < block.cols(); j++) { + for (unsigned int cell = 0; cell < nBlocks; cell++) + { + for (unsigned int i = 0; i < block.rows(); i++) + { + for (unsigned int comp = 0; comp < _disc.nComp; comp++, ++jac) + { + for (unsigned int j = 0; j < block.cols(); j++) + { /* liquid on liquid blocks */ // row: at current node and component; col: jump to node j - jac[(j - i) * idxr.strideParNode(type) + offRowToCol] = block(i, j) * static_cast(parDiff[comp]); + jac[(j - i) * idxr.strideParNode(type) + offRowToCol] = + block(i, j) * static_cast(parDiff[comp]); } /* liquid on solid blocks */ - for (unsigned int bnd = 0; bnd < _disc.nBound[type * _disc.nComp + comp]; bnd++) { - if (static_cast(surfDiff[_disc.offsetSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{ type }, ComponentIndex{ comp }) + bnd]]) != 0.0) { - for (unsigned int j = 0; j < block.cols(); j++) { + for (unsigned int bnd = 0; bnd < _disc.nBound[type * _disc.nComp + comp]; bnd++) + { + if (static_cast( + surfDiff[_disc.offsetSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{type}, + ComponentIndex{comp}) + + bnd]]) != 0.0) + { + for (unsigned int j = 0; j < block.cols(); j++) + { // row: at current node and component; col: jump to node j and to current bound state - jac[(j - i) * idxr.strideParNode(type) + offRowToCol + idxr.strideParLiquid() - comp - + idxr.offsetBoundComp(ParticleTypeIndex{ type }, ComponentIndex{ comp }) + bnd - ] - = block(i, j) * static_cast(beta_p[comp]) - * static_cast(surfDiff[_disc.offsetSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{ type }, ComponentIndex{ comp }) + bnd]]); + jac[(j - i) * idxr.strideParNode(type) + offRowToCol + idxr.strideParLiquid() - comp + + idxr.offsetBoundComp(ParticleTypeIndex{type}, ComponentIndex{comp}) + bnd] = + block(i, j) * static_cast(beta_p[comp]) * + static_cast( + surfDiff[_disc.offsetSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{type}, + ComponentIndex{comp}) + + bnd]]); } } } } /* solid on solid blocks */ - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int bnd = 0; bnd < _disc.nBound[type * _disc.nComp + comp]; bnd++, ++jac) { - if (static_cast(surfDiff[_disc.offsetSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{ type }, ComponentIndex{ comp }) + bnd]]) != 0.0 - && !nonKinetic[idxr.offsetBoundComp(ParticleTypeIndex{ type }, ComponentIndex{ comp }) + bnd]) { - for (unsigned int j = 0; j < block.cols(); j++) { + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int bnd = 0; bnd < _disc.nBound[type * _disc.nComp + comp]; bnd++, ++jac) + { + if (static_cast( + surfDiff[_disc.offsetSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{type}, + ComponentIndex{comp}) + + bnd]]) != 0.0 && + !nonKinetic[idxr.offsetBoundComp(ParticleTypeIndex{type}, ComponentIndex{comp}) + bnd]) + { + for (unsigned int j = 0; j < block.cols(); j++) + { // row: at current node and bound state; col: jump to node j - jac[(j - i) * idxr.strideParNode(type) + offRowToCol + bnd] - = block(i, j) - * static_cast(surfDiff[_disc.offsetSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{ type }, ComponentIndex{ comp }) + bnd]]); + jac[(j - i) * idxr.strideParNode(type) + offRowToCol + bnd] = + block(i, j) * + static_cast( + surfDiff[_disc.offsetSurfDiff[idxr.offsetBoundComp(ParticleTypeIndex{type}, + ComponentIndex{comp}) + + bnd]]); } } } @@ -2330,19 +3276,22 @@ class GeneralRateModelDG : public UnitOperationBase * @brief analytically calculates the static (per section) particle jacobian * @return 1 if jacobain calculation fits the predefined pattern of the jacobian, 0 if not. */ - int calcStaticAnaParticleDispJacobian(unsigned int parType, unsigned int colNode, const active* const parDiff, const active* const parSurfDiff, const double* const invBetaP) { + int calcStaticAnaParticleDispJacobian(unsigned int parType, unsigned int colNode, const active* const parDiff, + const active* const parSurfDiff, const double* const invBetaP) + { // DG particle dispersion Jacobian - if(_disc.parExactInt[parType]) + if (_disc.parExactInt[parType]) calcParticleDGSEMJacobian(parType, colNode, parDiff, parSurfDiff, invBetaP); else // deprecated calcParticleCollocationDGSEMJacobian(parType, colNode, parDiff, parSurfDiff, invBetaP); - return _globalJac.isCompressed(); // if matrix lost its compressed storage, the calculation did not fit the pre-defined pattern. + return _globalJac.isCompressed(); // if matrix lost its compressed storage, the calculation did not fit the + // pre-defined pattern. } - - void setJacobianPattern_GRM(SparseMatrix& globalJ, unsigned int secIdx, bool hasBulkReaction) { + void setJacobianPattern_GRM(SparseMatrix& globalJ, unsigned int secIdx, bool hasBulkReaction) + { Indexer idxr(_disc); @@ -2350,14 +3299,17 @@ class GeneralRateModelDG : public UnitOperationBase // reserve space for all entries int bulkEntries = _convDispOp.nConvDispEntries(false); if (hasBulkReaction) - bulkEntries += _disc.nPoints * _disc.nComp * _disc.nComp; // add nComp entries for every component at each discrete bulk point + bulkEntries += _disc.nPoints * _disc.nComp * + _disc.nComp; // add nComp entries for every component at each discrete bulk point // particle int addTimeDer = 0; // additional time derivative entries: bound states in particle dispersion equation int isothermNNZ = 0; int particleEntries = 0; - for (int type = 0; type < _disc.nParType; type++) { - isothermNNZ = (idxr.strideParNode(type)) * _disc.nParPoints[type] * _disc.strideBound[type]; // every bound satte might depend on every bound and liquid state + for (int type = 0; type < _disc.nParType; type++) + { + isothermNNZ = (idxr.strideParNode(type)) * _disc.nParPoints[type] * + _disc.strideBound[type]; // every bound satte might depend on every bound and liquid state addTimeDer = _disc.nParPoints[type] * _disc.strideBound[type]; particleEntries += calcParDispNNZ(type) + addTimeDer + isothermNNZ; } @@ -2372,49 +3324,67 @@ class GeneralRateModelDG : public UnitOperationBase _convDispOp.convDispJacPattern(tripletList, bulkOffset); // bulk reaction jacobian - if (hasBulkReaction) { - for (unsigned int colNode = 0; colNode < _disc.nPoints; colNode++) { - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int toComp = 0; toComp < _disc.nComp; toComp++) { - tripletList.push_back(T(idxr.offsetC() + colNode * idxr.strideColNode() + comp * idxr.strideColComp(), - idxr.offsetC() + colNode * idxr.strideColNode() + toComp * idxr.strideColComp(), - 0.0)); + if (hasBulkReaction) + { + for (unsigned int colNode = 0; colNode < _disc.nPoints; colNode++) + { + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int toComp = 0; toComp < _disc.nComp; toComp++) + { + tripletList.push_back( + T(idxr.offsetC() + colNode * idxr.strideColNode() + comp * idxr.strideColComp(), + idxr.offsetC() + colNode * idxr.strideColNode() + toComp * idxr.strideColComp(), 0.0)); } } } } // particle jacobian (including isotherm and time derivative) - for (int colNode = 0; colNode < _disc.nPoints; colNode++) { - for (int type = 0; type < _disc.nParType; type++) { + for (int colNode = 0; colNode < _disc.nPoints; colNode++) + { + for (int type = 0; type < _disc.nParType; type++) + { setParJacPattern(tripletList, type, colNode, secIdx); } } // flux jacobians - for (unsigned int type = 0; type < _disc.nParType; type++) { - for (unsigned int colNode = 0; colNode < _disc.nPoints; colNode++) { - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { + for (unsigned int type = 0; type < _disc.nParType; type++) + { + for (unsigned int colNode = 0; colNode < _disc.nPoints; colNode++) + { + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { // add Cl on Cp entries // row: add bulk offset, jump over previous nodes and components // col: add flux offset to current parType, jump over previous nodes and components - tripletList.push_back(T(idxr.offsetC() + colNode * idxr.strideColNode() + comp * idxr.strideColComp(), - idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ colNode }) + (_disc.nParPoints[type] - 1) * idxr.strideParNode(type) + comp * idxr.strideParComp(), 0.0)); + tripletList.push_back( + T(idxr.offsetC() + colNode * idxr.strideColNode() + comp * idxr.strideColComp(), + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{colNode}) + + (_disc.nParPoints[type] - 1) * idxr.strideParNode(type) + comp * idxr.strideParComp(), + 0.0)); // add Cp on Cl entries - if(!_disc.parExactInt[type]) - // row: add particle offset to current parType and particle, go to last node and add component offset - // col: add flux offset to current component, jump over previous nodes and components - tripletList.push_back(T(idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ colNode }) - + (_disc.nParPoints[type] - 1) * idxr.strideParNode(type) + comp * idxr.strideParComp(), + if (!_disc.parExactInt[type]) + // row: add particle offset to current parType and particle, go to last node and add component + // offset col: add flux offset to current component, jump over previous nodes and components + tripletList.push_back(T(idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{colNode}) + + (_disc.nParPoints[type] - 1) * idxr.strideParNode(type) + + comp * idxr.strideParComp(), idxr.offsetC() + colNode * idxr.strideColNode() + comp, 0.0)); - else { - for (unsigned int node = 0; node < _disc.nParNode[type]; node++) { - // row: add particle offset to current parType and particle, go to last cell and current node and add component offset - // col: add flux offset to current component, jump over previous nodes and components - tripletList.push_back(T(idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ colNode }) + (_disc.nParCell[type] - 1) * _disc.nParNode[type] * idxr.strideParNode(type) - + node * idxr.strideParNode(type) + comp * idxr.strideParComp(), - idxr.offsetC() + colNode * idxr.strideColNode() + comp, 0.0)); + else + { + for (unsigned int node = 0; node < _disc.nParNode[type]; node++) + { + // row: add particle offset to current parType and particle, go to last cell and current + // node and add component offset col: add flux offset to current component, jump over + // previous nodes and components + tripletList.push_back( + T(idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{colNode}) + + (_disc.nParCell[type] - 1) * _disc.nParNode[type] * idxr.strideParNode(type) + + node * idxr.strideParNode(type) + comp * idxr.strideParComp(), + idxr.offsetC() + colNode * idxr.strideColNode() + comp, 0.0)); } } } @@ -2424,70 +3394,101 @@ class GeneralRateModelDG : public UnitOperationBase globalJ.setFromTriplets(tripletList.begin(), tripletList.end()); } - int calcFluxJacobians(unsigned int secIdx, bool outliersOnly = false) { + int calcFluxJacobians(unsigned int secIdx, bool outliersOnly = false) + { Indexer idxr(_disc); - for (unsigned int type = 0; type < _disc.nParType; type++) { + for (unsigned int type = 0; type < _disc.nParType; type++) + { // lifting matrix entry for exact integration scheme depends on metrics for sphere and cylinder - double exIntLiftContribution = static_cast(_disc.Ir[_disc.offsetMetric[type] + _disc.nParCell[type] - 1][_disc.nParNode[type] - 1]); + double exIntLiftContribution = static_cast( + _disc.Ir[_disc.offsetMetric[type] + _disc.nParCell[type] - 1][_disc.nParNode[type] - 1]); if (_parGeomSurfToVol[type] == _disc.SurfVolRatioSlab) exIntLiftContribution = 1.0; // Ordering of diffusion: // sec0type0comp0, sec0type0comp1, sec0type0comp2, sec0type1comp0, sec0type1comp1, sec0type1comp2, // sec1type0comp0, sec1type0comp1, sec1type0comp2, sec1type1comp0, sec1type1comp1, sec1type1comp2, ... - active const* const filmDiff = getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const filmDiff = + getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; linalg::BandedEigenSparseRowIterator jacCl(_globalJac, idxr.offsetC()); - linalg::BandedEigenSparseRowIterator jacCp(_globalJac, idxr.offsetCp(ParticleTypeIndex{ type }) + (_disc.nParPoints[type] - 1) * idxr.strideParNode(type)); + linalg::BandedEigenSparseRowIterator jacCp(_globalJac, + idxr.offsetCp(ParticleTypeIndex{type}) + + (_disc.nParPoints[type] - 1) * idxr.strideParNode(type)); for (unsigned int colNode = 0; colNode < _disc.nPoints; colNode++) { - for (unsigned int comp = 0; comp < _disc.nComp; comp++, ++jacCp, ++jacCl) { + for (unsigned int comp = 0; comp < _disc.nComp; comp++, ++jacCp, ++jacCl) + { // add Cl on Cl entries (added since these entries are also touched by bulk jacobian) // row: already at bulk phase. already at current node and component. // col: already at bulk phase. already at current node and component. - if(!outliersOnly) - jacCl[0] += static_cast(filmDiff[comp]) * (1.0 - static_cast(_colPorosity)) / static_cast(_colPorosity) - * _parGeomSurfToVol[type] / static_cast(_parRadius[type]) - * _parTypeVolFrac[type + colNode * _disc.nParType].getValue(); + if (!outliersOnly) + jacCl[0] += static_cast(filmDiff[comp]) * (1.0 - static_cast(_colPorosity)) / + static_cast(_colPorosity) * _parGeomSurfToVol[type] / + static_cast(_parRadius[type]) * + _parTypeVolFrac[type + colNode * _disc.nParType].getValue(); // add Cl on Cp entries (added since these entries are also touched by bulk jacobian) // row: already at bulk phase. already at current node and component. // col: go to current particle phase entry. - jacCl[jacCp.row() - jacCl.row()] = -static_cast(filmDiff[comp]) * (1.0 - static_cast(_colPorosity)) / static_cast(_colPorosity) - * _parGeomSurfToVol[type] / static_cast(_parRadius[type]) - * _parTypeVolFrac[type + colNode * _disc.nParType].getValue(); + jacCl[jacCp.row() - jacCl.row()] = -static_cast(filmDiff[comp]) * + (1.0 - static_cast(_colPorosity)) / + static_cast(_colPorosity) * _parGeomSurfToVol[type] / + static_cast(_parRadius[type]) * + _parTypeVolFrac[type + colNode * _disc.nParType].getValue(); // add Cp on Flux entries - if (!_disc.parExactInt[type]) { + if (!_disc.parExactInt[type]) + { // row: already at particle. already at current node and liquid state. // col: already at particle. already at current node and liquid state. if (!outliersOnly) // Cp on Cb - jacCp[0] += static_cast(filmDiff[comp]) * 2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[type]]) * _disc.parInvWeights[type][0] / static_cast(_parPorosity[type]) / static_cast(_poreAccessFactor[type * _disc.nComp + comp]); + jacCp[0] += static_cast(filmDiff[comp]) * 2.0 / + static_cast(_disc.deltaR[_disc.offsetMetric[type]]) * + _disc.parInvWeights[type][0] / static_cast(_parPorosity[type]) / + static_cast(_poreAccessFactor[type * _disc.nComp + comp]); // row: already at particle. already at current node and liquid state. // col: go to current bulk phase. - jacCp[jacCl.row() - jacCp.row()] = -static_cast(filmDiff[comp]) * 2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[type]]) * _disc.parInvWeights[type][0] / static_cast(_parPorosity[type]) / static_cast(_poreAccessFactor[type * _disc.nComp + comp]); + jacCp[jacCl.row() - jacCp.row()] = + -static_cast(filmDiff[comp]) * 2.0 / + static_cast(_disc.deltaR[_disc.offsetMetric[type]]) * _disc.parInvWeights[type][0] / + static_cast(_parPorosity[type]) / + static_cast(_poreAccessFactor[type * _disc.nComp + comp]); } - else { + else + { unsigned int entry = jacCp.row(); - for (int node = _disc.parPolyDeg[type]; node >= 0; node--, jacCp -= idxr.strideParNode(type)) { + for (int node = _disc.parPolyDeg[type]; node >= 0; node--, jacCp -= idxr.strideParNode(type)) + { // row: already at particle. Already at current node and liquid state. // col: original entry at outer node. if (!outliersOnly) // Cp on Cb - jacCp[entry - jacCp.row()] - += static_cast(filmDiff[comp]) * 2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[type]]) * _disc.parInvMM[_disc.offsetMetric[type] + _disc.nParCell[type] - 1](node, _disc.nParNode[type] - 1) * exIntLiftContribution / static_cast(_parPorosity[type]) / static_cast(_poreAccessFactor[type * _disc.nComp + comp]); + jacCp[entry - jacCp.row()] += + static_cast(filmDiff[comp]) * 2.0 / + static_cast(_disc.deltaR[_disc.offsetMetric[type]]) * + _disc.parInvMM[_disc.offsetMetric[type] + _disc.nParCell[type] - 1]( + node, _disc.nParNode[type] - 1) * + exIntLiftContribution / static_cast(_parPorosity[type]) / + static_cast(_poreAccessFactor[type * _disc.nComp + comp]); // row: already at particle. Already at current node and liquid state. // col: go to current bulk phase. - jacCp[jacCl.row() - jacCp.row()] - = -static_cast(filmDiff[comp]) * 2.0 / static_cast(_disc.deltaR[_disc.offsetMetric[type]]) * _disc.parInvMM[_disc.offsetMetric[type] + _disc.nParCell[type] - 1](node, _disc.nParNode[type] - 1) * exIntLiftContribution / static_cast(_parPorosity[type]) / static_cast(_poreAccessFactor[type * _disc.nComp + comp]); + jacCp[jacCl.row() - jacCp.row()] = + -static_cast(filmDiff[comp]) * 2.0 / + static_cast(_disc.deltaR[_disc.offsetMetric[type]]) * + _disc.parInvMM[_disc.offsetMetric[type] + _disc.nParCell[type] - 1]( + node, _disc.nParNode[type] - 1) * + exIntLiftContribution / static_cast(_parPorosity[type]) / + static_cast(_poreAccessFactor[type * _disc.nComp + comp]); } // set back iterator to first node as required by component loop jacCp += _disc.nParNode[type] * idxr.strideParNode(type); } } - if (colNode < _disc.nPoints - 1) // execute iteration statement only when condition is true in next loop. + if (colNode < + _disc.nPoints - 1) // execute iteration statement only when condition is true in next loop. jacCp += _disc.strideBound[type] + (_disc.nParPoints[type] - 1) * idxr.strideParNode(type); } } @@ -2495,26 +3496,35 @@ class GeneralRateModelDG : public UnitOperationBase return 1; } - int calcStaticAnaJacobian_GRM(unsigned int secIdx) { + int calcStaticAnaJacobian_GRM(unsigned int secIdx) + { Indexer idxr(_disc); // inlet and bulk jacobian _convDispOp.calcStaticAnaJacobian(_globalJac, _jacInlet, idxr.offsetC()); // particle jacobian (without isotherm, which is handled in residualKernel) - for (int colNode = 0; colNode < _disc.nPoints; colNode++) { - for (int type = 0; type < _disc.nParType; type++) { + for (int colNode = 0; colNode < _disc.nPoints; colNode++) + { + for (int type = 0; type < _disc.nParType; type++) + { // Prepare parameters - const active* const parDiff = getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + const active* const parDiff = + getSectionDependentSlice(_parDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; // Ordering of particle surface diffusion: // bnd0comp0, bnd0comp1, bnd0comp2, bnd1comp0, bnd1comp1, bnd1comp2 - const active* const parSurfDiff = getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + _disc.nBoundBeforeType[type]; + const active* const parSurfDiff = + getSectionDependentSlice(_parSurfDiffusion, _disc.strideBound[_disc.nParType], secIdx) + + _disc.nBoundBeforeType[type]; double* invBetaP = new double[_disc.nComp]; - for (int comp = 0; comp < _disc.nComp; comp++) { - invBetaP[comp] = (1.0 - static_cast(_parPorosity[type])) / (static_cast(_poreAccessFactor[_disc.nComp * type + comp]) * static_cast(_parPorosity[type])); + for (int comp = 0; comp < _disc.nComp; comp++) + { + invBetaP[comp] = (1.0 - static_cast(_parPorosity[type])) / + (static_cast(_poreAccessFactor[_disc.nComp * type + comp]) * + static_cast(_parPorosity[type])); } calcStaticAnaParticleDispJacobian(type, colNode, parDiff, parSurfDiff, invBetaP); @@ -2529,17 +3539,21 @@ class GeneralRateModelDG : public UnitOperationBase /** * @brief computes the jacobian via finite differences (testing purpose) */ - MatrixXd calcFDJacobian(const double* y_, const double* yDot_, const SimulationTime simTime, util::ThreadLocalStorage& threadLocalMem, double alpha) { + MatrixXd calcFDJacobian(const double* y_, const double* yDot_, const SimulationTime simTime, + util::ThreadLocalStorage& threadLocalMem, double alpha) + { // create solution vectors Eigen::Map hmpf(y_, numDofs()); VectorXd y = hmpf; VectorXd yDot; - if (yDot_) { + if (yDot_) + { Eigen::Map hmpf2(yDot_, numDofs()); yDot = hmpf2; } - else { + else + { return MatrixXd::Zero(numDofs(), numDofs()); } VectorXd res = VectorXd::Zero(numDofs()); @@ -2553,40 +3567,44 @@ class GeneralRateModelDG : public UnitOperationBase residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, threadLocalMem); - for (int col = 0; col < Jacobian.cols(); col++) { + for (int col = 0; col < Jacobian.cols(); col++) + { Jacobian.col(col) = -(1.0 + alpha) * res; } /* Residual(y+h) */ // state DOFs - for (int dof = 0; dof < Jacobian.cols(); dof++) { + for (int dof = 0; dof < Jacobian.cols(); dof++) + { y[dof] += epsilon; - residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, threadLocalMem); + residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, + threadLocalMem); y[dof] -= epsilon; Jacobian.col(dof) += res; } // state derivative Jacobian - for (int dof = 0; dof < Jacobian.cols(); dof++) { + for (int dof = 0; dof < Jacobian.cols(); dof++) + { yDot[dof] += epsilon; - residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, threadLocalMem); + residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, + threadLocalMem); yDot[dof] -= epsilon; Jacobian.col(dof) += alpha * res; } ///* exterminate numerical noise */ - //for (int i = 0; i < Jacobian.rows(); i++) { + // for (int i = 0; i < Jacobian.rows(); i++) { // for (int j = 0; j < Jacobian.cols(); j++) { // if (std::abs(Jacobian(i, j)) < 1e-10) Jacobian(i, j) = 0.0; // } - //} + // } Jacobian /= epsilon; return Jacobian; } - }; } // namespace model } // namespace cadet -#endif // LIBCADET_GENERALRATEMODELDG_HPP_ +#endif // LIBCADET_GENERALRATEMODELDG_HPP_ diff --git a/src/libcadet/model/InletModel.cpp b/src/libcadet/model/InletModel.cpp index 5b1e5a336..6eb621b65 100644 --- a/src/libcadet/model/InletModel.cpp +++ b/src/libcadet/model/InletModel.cpp @@ -33,8 +33,9 @@ namespace cadet namespace model { -InletModel::InletModel(UnitOpIdx unitOpIdx) : _unitOpIdx(unitOpIdx), _inlet(nullptr), - _inletConcentrationsRaw(nullptr), _inletDerivatives(nullptr), _inletConcentrations(nullptr) +InletModel::InletModel(UnitOpIdx unitOpIdx) + : _unitOpIdx(unitOpIdx), _inlet(nullptr), _inletConcentrationsRaw(nullptr), _inletDerivatives(nullptr), + _inletConcentrations(nullptr) { } @@ -112,7 +113,7 @@ bool InletModel::hasParameter(const ParameterId& pId) const // Check inlet profile if (!_inlet || ((pId.unitOperation != _unitOpIdx) && (pId.unitOperation != UnitOpIndep))) return false; - + const std::vector inletParams = _inlet->availableParameters(_unitOpIdx); // Search for parameter @@ -166,7 +167,8 @@ bool InletModel::setParameter(const ParameterId& pId, bool value) void InletModel::setSensitiveParameterValue(const ParameterId& pId, double value) { // Check inlet and filter parameters - if (_inlet && ((pId.unitOperation == _unitOpIdx) || (pId.unitOperation == UnitOpIndep)) && (_sensParamsInlet.find(pId) != _sensParamsInlet.end())) + if (_inlet && ((pId.unitOperation == _unitOpIdx) || (pId.unitOperation == UnitOpIndep)) && + (_sensParamsInlet.find(pId) != _sensParamsInlet.end())) _inlet->setParameterValue(pId, value); } @@ -194,7 +196,7 @@ bool InletModel::setSensitiveParameter(const ParameterId& pId, unsigned int adDi { LOG(Debug) << "Found parameter " << pId << " in Inlet: Dir " << adDirection << " is set to " << adValue; } - + // Register parameter and reserve AD direction _sensParamsInlet[pId] = std::make_tuple(adDirection, adValue); return true; @@ -214,21 +216,23 @@ unsigned int InletModel::numSensParams() const return _sensParamsInlet.size(); } -template<> double const* InletModel::moveInletValues(double const* const rawValues, double t, unsigned int secIdx) const +template <> +double const* InletModel::moveInletValues(double const* const rawValues, double t, unsigned int secIdx) const { // No parameter derivatives required, return raw values return rawValues; } -template<> active const* InletModel::moveInletValues(double const* const rawValues, double t, unsigned int secIdx) const +template <> +active const* InletModel::moveInletValues(double const* const rawValues, double t, unsigned int secIdx) const { // Convert to active for (unsigned int i = 0; i < _nComp; ++i) _inletConcentrations[i] = rawValues[i]; // Get time derivative -// _inlet->timeDerivative(t, secIdx, _inletDerivatives + _nComp); -// LOG(Debug) << "dInlet / dt = " << cadet::log::VectorPtr(_inletDerivatives + _nComp, _nComp); + // _inlet->timeDerivative(t, secIdx, _inletDerivatives + _nComp); + // LOG(Debug) << "dInlet / dt = " << cadet::log::VectorPtr(_inletDerivatives + _nComp, _nComp); // Retrieve parameter derivatives for (auto sp : _sensParamsInlet) @@ -237,30 +241,38 @@ template<> active const* InletModel::moveInletValues(double const* const rawValu const double adValue = std::get<1>(sp.second); _inlet->parameterDerivative(t, secIdx, sp.first, _inletDerivatives); -// LOG(Debug) << "dInlet / dp = " << cadet::log::VectorPtr(_inletDerivatives, _nComp); + // LOG(Debug) << "dInlet / dp = " << cadet::log::VectorPtr(_inletDerivatives, _nComp); // Copy derivatives into AD datatypes for (unsigned int i = 0; i < _nComp; ++i) - _inletConcentrations[i].setADValue(adDir, _inletConcentrations[i].getADValue(adDir) + _inletDerivatives[i] * adValue); + _inletConcentrations[i].setADValue(adDir, _inletConcentrations[i].getADValue(adDir) + + _inletDerivatives[i] * adValue); -// LOG(Debug) << "totalInlet " << adDir << " " << cadet::log::VectorPtr(_inletConcentrations, _nComp); + // LOG(Debug) << "totalInlet " << adDir << " " << + // cadet::log::VectorPtr(_inletConcentrations, _nComp); } return _inletConcentrations; } -template<> double const* InletModel::getData() const +template <> double const* InletModel::getData() const { return _inletConcentrationsRaw; } -template<> active const* InletModel::getData() const +template <> active const* InletModel::getData() const { return _inletConcentrations; } -void InletModel::useAnalyticJacobian(const bool analyticJac) { } -void InletModel::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) { } +void InletModel::useAnalyticJacobian(const bool analyticJac) +{ +} +void InletModel::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac) +{ +} void InletModel::reportSolution(ISolutionRecorder& recorder, double const* const solution) const { @@ -280,47 +292,63 @@ unsigned int InletModel::requiredADdirs() const CADET_NOEXCEPT return 0; } -void InletModel::prepareADvectors(const AdJacobianParams& adJac) const { } +void InletModel::prepareADvectors(const AdJacobianParams& adJac) const +{ +} -void InletModel::applyInitialCondition(const SimulationState& simState) const { } +void InletModel::applyInitialCondition(const SimulationState& simState) const +{ +} -void InletModel::readInitialCondition(IParameterProvider& paramProvider) { } +void InletModel::readInitialCondition(IParameterProvider& paramProvider) +{ +} -void InletModel::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) -{ +void InletModel::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) +{ _inlet->inletConcentration(simTime.t, simTime.secIdx, _inletConcentrationsRaw); std::copy_n(_inletConcentrationsRaw, _nComp, vecStateY); } -void InletModel::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) +void InletModel::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) { consistentInitialState(simTime, vecStateY, adJac, errorTol, threadLocalMem); _inlet->timeDerivative(simTime.t, simTime.secIdx, _inletDerivatives); } -void InletModel::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem) -{ +void InletModel::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem) +{ _inlet->timeDerivative(simTime.t, simTime.secIdx, _inletDerivatives); std::copy_n(_inletDerivatives, _nComp, vecStateYdot); } -void InletModel::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem) +void InletModel::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem) { std::copy_n(_inletDerivatives, _nComp, vecStateYdot); } -int InletModel::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int InletModel::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) { return residualImpl(simTime.t, simTime.secIdx, simState, res, threadLocalMem); } -int InletModel::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem) +int InletModel::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem) { return residualImpl(simTime.t, simTime.secIdx, simState, res, threadLocalMem); } template -int InletModel::residualImpl(double t, unsigned int secIdx, const ConstSimulationState& simState, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem) +int InletModel::residualImpl(double t, unsigned int secIdx, const ConstSimulationState& simState, + ResidualType* const res, util::ThreadLocalStorage& threadLocalMem) { // Evaluate the user-specified function for the inlet concentration _inlet->inletConcentration(t, secIdx, _inletConcentrationsRaw); @@ -337,20 +365,24 @@ int InletModel::residualImpl(double t, unsigned int secIdx, const ConstSimulatio return 0; } -int InletModel::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int InletModel::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { return residualImpl(simTime.t, simTime.secIdx, simState, res, threadLocalMem); } -int InletModel::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem) +int InletModel::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem) { // Evaluate residual for all parameters using AD in vector mode return residualImpl(simTime.t, simTime.secIdx, simState, adRes, threadLocalMem); } -int InletModel::residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3) +int InletModel::residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3) { for (std::size_t param = 0; param < yS.size(); ++param) { @@ -364,7 +396,8 @@ int InletModel::residualSensFwdCombine(const SimulationTime& simTime, const Cons return 0; } -int InletModel::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int InletModel::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) { // Evaluate residual for all parameters using AD in vector mode, Jacobian is always analytic (identity matrix) return residualImpl(simTime.t, simTime.secIdx, simState, adJac.adRes, threadLocalMem); @@ -376,7 +409,8 @@ void InletModel::initializeSensitivityStates(const std::vector& vecSens } void InletModel::consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem) { for (std::size_t param = 0; param < vecSensY.size(); ++param) { @@ -403,12 +437,14 @@ void InletModel::consistentInitialSensitivity(const SimulationTime& simTime, con } void InletModel::leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem) { consistentInitialSensitivity(simTime, simState, vecSensY, vecSensYdot, adRes, threadLocalMem); } -void InletModel::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret) +void InletModel::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret) { for (unsigned int i = 0; i < numDofs(); ++i) { @@ -416,12 +452,12 @@ void InletModel::multiplyWithJacobian(const SimulationTime& simTime, const Const } } -void InletModel::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret) +void InletModel::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* sDot, double* ret) { std::fill_n(ret, numDofs(), 0.0); } - int InletModel::Exporter::writeInlet(unsigned int port, double* buffer) const { cadet_assert(port == 0); @@ -448,12 +484,12 @@ int InletModel::Exporter::writeOutlet(double* buffer) const return _nComp; } - -void registerInletModel(std::unordered_map>& models) +void registerInletModel( + std::unordered_map>& models) { models[InletModel::identifier()] = [](UnitOpIdx uoId, IParameterProvider&) { return new InletModel(uoId); }; } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/InletModel.hpp b/src/libcadet/model/InletModel.hpp index 3f8c62a4a..0a5e10ecb 100644 --- a/src/libcadet/model/InletModel.hpp +++ b/src/libcadet/model/InletModel.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the inlet model which encapsulates an inlet profile as unit operation. */ @@ -47,7 +47,6 @@ namespace model class InletModel : public IUnitOperation { public: - InletModel(UnitOpIdx unitOpIdx); virtual ~InletModel() CADET_NOEXCEPT; @@ -56,20 +55,45 @@ class InletModel : public IUnitOperation virtual bool usesAD() const CADET_NOEXCEPT; virtual unsigned int requiredADdirs() const CADET_NOEXCEPT; - virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT { return _unitOpIdx; } - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _nComp; } - virtual void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT { } - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual bool canAccumulate() const CADET_NOEXCEPT { return true; } + virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT + { + return _unitOpIdx; + } + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _nComp; + } + virtual void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT + { + } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual bool canAccumulate() const CADET_NOEXCEPT + { + return true; + } - static const char* identifier() { return "INLET"; } - virtual const char* unitOperationName() const CADET_NOEXCEPT { return identifier(); } + static const char* identifier() + { + return "INLET"; + } + virtual const char* unitOperationName() const CADET_NOEXCEPT + { + return identifier(); + } virtual bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper); virtual bool configure(IParameterProvider& paramProvider); - virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac); - + virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac); + virtual std::unordered_map getAllParameterValues() const; virtual bool hasParameter(const ParameterId& pId) const; virtual double getParameterDouble(const ParameterId& pId) const; @@ -89,128 +113,279 @@ class InletModel : public IUnitOperation virtual void reportSolution(ISolutionRecorder& recorder, double const* const solution) const; virtual void reportSolutionStructure(ISolutionRecorder& recorder) const; - virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem); - virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - - virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3); + virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem); + virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + + virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3); // linearSolve is a null operation (the result is I^-1 *rhs -> rhs) since the Jacobian is an identity matrix virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) { return 0; } + const ConstSimulationState& simState) + { + return 0; + } virtual void prepareADvectors(const AdJacobianParams& adJac) const; virtual void applyInitialCondition(const SimulationState& simState) const; virtual void readInitialCondition(IParameterProvider& paramProvider); - virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); virtual void consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); virtual void initializeSensitivityStates(const std::vector& vecSensY) const; - virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem); virtual void leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) { } + virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) + { + } - virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret); - virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret); + virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret); + virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* sDot, double* ret); - virtual bool hasInlet() const CADET_NOEXCEPT { return false; } - virtual bool hasOutlet() const CADET_NOEXCEPT { return true; } + virtual bool hasInlet() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasOutlet() const CADET_NOEXCEPT + { + return true; + } - virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT { return 0; } - virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT { return 1; } - virtual unsigned int localInletComponentIndex(unsigned int port) const CADET_NOEXCEPT { return 0; } - virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT { return 0; } + virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int localInletComponentIndex(unsigned int port) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT + { + return 0; + } virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections); - virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut) { } + virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut) + { + } - virtual unsigned int threadLocalMemorySize() const CADET_NOEXCEPT { return 0; } + virtual unsigned int threadLocalMemorySize() const CADET_NOEXCEPT + { + return 0; + } #ifdef CADET_BENCHMARK_MODE - virtual std::vector benchmarkTimings() const { return std::vector(0); } - virtual char const* const* benchmarkDescriptions() const { return nullptr; } + virtual std::vector benchmarkTimings() const + { + return std::vector(0); + } + virtual char const* const* benchmarkDescriptions() const + { + return nullptr; + } #endif protected: - template T const* moveInletValues(double const* const rawValues, double t, unsigned int secIdx) const; template T const* getData() const; template - int residualImpl(double t, unsigned int secIdx, const ConstSimulationState& simState, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); + int residualImpl(double t, unsigned int secIdx, const ConstSimulationState& simState, ResidualType* const res, + util::ThreadLocalStorage& threadLocalMem); - UnitOpIdx _unitOpIdx; //!< Unit operation index - unsigned int _nComp; //!< Number of components + UnitOpIdx _unitOpIdx; //!< Unit operation index + unsigned int _nComp; //!< Number of components std::vector _tempState; // Temporary storage for the state vector IInletProfile* _inlet; //!< Inlet profile (owned by library user) double* _inletConcentrationsRaw; //!< Provides memory for getting the inlet concentrations from the inlet profile - double* _inletDerivatives; //!< Points to memory used for retrieving the derivatives of the inlet profile (do not delete since the memory is owned by _inletConcentrationsRaw) + double* _inletDerivatives; //!< Points to memory used for retrieving the derivatives of the inlet profile (do not + //!< delete since the memory is owned by _inletConcentrationsRaw) active* _inletConcentrations; //!< Provides memory for the inlet concentrations - std::unordered_map> _sensParamsInlet; //!< Maps an inlet parameter to its AD direction and derivative value + std::unordered_map> + _sensParamsInlet; //!< Maps an inlet parameter to its AD direction and derivative value class Exporter : public ISolutionExporter { public: - - Exporter(unsigned int nComp, double const* data) : _data(data), _nComp(nComp) { } - - virtual bool hasParticleFlux() const CADET_NOEXCEPT { return false; } - virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT { return false; } - virtual bool hasSolidPhase() const CADET_NOEXCEPT { return false; } - virtual bool hasVolume() const CADET_NOEXCEPT { return false; } - virtual bool isParticleLumped() const CADET_NOEXCEPT { return false; } - virtual bool hasPrimaryExtent() const CADET_NOEXCEPT { return false; } - - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _nComp; } - virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numParticleTypes() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT { return 0; } - - virtual int writeMobilePhase(double* buffer) const { return 0; } - virtual int writeSolidPhase(double* buffer) const { return 0; } - virtual int writeParticleMobilePhase(double* buffer) const { return 0; } - virtual int writeSolidPhase(unsigned int parType, double* buffer) const { return 0; } - virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const { return 0; } - virtual int writeParticleFlux(double* buffer) const { return 0; } - virtual int writeParticleFlux(unsigned int parType, double* buffer) const { return 0; } - virtual int writeVolume(double* buffer) const { return 0; } + Exporter(unsigned int nComp, double const* data) : _data(data), _nComp(nComp) + { + } + + virtual bool hasParticleFlux() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasSolidPhase() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasVolume() const CADET_NOEXCEPT + { + return false; + } + virtual bool isParticleLumped() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasPrimaryExtent() const CADET_NOEXCEPT + { + return false; + } + + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _nComp; + } + virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numParticleTypes() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT + { + return 0; + } + + virtual int writeMobilePhase(double* buffer) const + { + return 0; + } + virtual int writeSolidPhase(double* buffer) const + { + return 0; + } + virtual int writeParticleMobilePhase(double* buffer) const + { + return 0; + } + virtual int writeSolidPhase(unsigned int parType, double* buffer) const + { + return 0; + } + virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const + { + return 0; + } + virtual int writeParticleFlux(double* buffer) const + { + return 0; + } + virtual int writeParticleFlux(unsigned int parType, double* buffer) const + { + return 0; + } + virtual int writeVolume(double* buffer) const + { + return 0; + } virtual int writeInlet(unsigned int port, double* buffer) const; virtual int writeInlet(double* buffer) const; virtual int writeOutlet(unsigned int port, double* buffer) const; virtual int writeOutlet(double* buffer) const; - virtual int writePrimaryCoordinates(double* coords) const { return 0; } - virtual int writeSecondaryCoordinates(double* coords) const { return 0; } - virtual int writeParticleCoordinates(unsigned int parType, double* coords) const { return 0; } + virtual int writePrimaryCoordinates(double* coords) const + { + return 0; + } + virtual int writeSecondaryCoordinates(double* coords) const + { + return 0; + } + virtual int writeParticleCoordinates(unsigned int parType, double* coords) const + { + return 0; + } protected: double const* const _data; @@ -221,4 +396,4 @@ class InletModel : public IUnitOperation } // namespace model } // namespace cadet -#endif // LIBCADET_INLETMODEL_HPP_ +#endif // LIBCADET_INLETMODEL_HPP_ diff --git a/src/libcadet/model/LumpedRateModelWithPores-InitialConditions.cpp b/src/libcadet/model/LumpedRateModelWithPores-InitialConditions.cpp index 92d2febac..7debdaf79 100644 --- a/src/libcadet/model/LumpedRateModelWithPores-InitialConditions.cpp +++ b/src/libcadet/model/LumpedRateModelWithPores-InitialConditions.cpp @@ -29,7 +29,7 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include +#include #endif namespace cadet @@ -39,11 +39,14 @@ namespace model { template -int LumpedRateModelWithPores::multiplexInitialConditions(const cadet::ParameterId& pId, unsigned int adDirection, double adValue) +int LumpedRateModelWithPores::multiplexInitialConditions(const cadet::ParameterId& pId, + unsigned int adDirection, double adValue) { if (_singleBinding) { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { _sensParams.insert(&_initCp[pId.component]); for (unsigned int t = 0; t < _disc.nParType; ++t) @@ -52,18 +55,23 @@ int LumpedRateModelWithPores::multiplexInitialConditions(const else if (pId.name == hashString("INIT_CP")) return -1; - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { _sensParams.insert(&_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState]); for (unsigned int t = 0; t < _disc.nParType; ++t) - _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState].setADValue(adDirection, adValue); + _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState] + .setADValue(adDirection, adValue); } else if (pId.name == hashString("INIT_Q")) return -1; } else { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { _sensParams.insert(&_initCp[pId.particleType * _disc.nComp + pId.component]); _initCp[pId.particleType * _disc.nComp + pId.component].setADValue(adDirection, adValue); @@ -71,10 +79,16 @@ int LumpedRateModelWithPores::multiplexInitialConditions(const else if (pId.name == hashString("INIT_CP")) return -1; - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { - _sensParams.insert(&_initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState]); - _initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState].setADValue(adDirection, adValue); + _sensParams.insert( + &_initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState]); + _initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState] + .setADValue(adDirection, adValue); } else if (pId.name == hashString("INIT_Q")) return -1; @@ -83,11 +97,14 @@ int LumpedRateModelWithPores::multiplexInitialConditions(const } template -int LumpedRateModelWithPores::multiplexInitialConditions(const cadet::ParameterId& pId, double val, bool checkSens) +int LumpedRateModelWithPores::multiplexInitialConditions(const cadet::ParameterId& pId, double val, + bool checkSens) { if (_singleBinding) { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { if (checkSens && !contains(_sensParams, &_initCp[pId.component])) return -1; @@ -98,20 +115,27 @@ int LumpedRateModelWithPores::multiplexInitialConditions(const else if (pId.name == hashString("INIT_CP")) return -1; - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { - if (checkSens && !contains(_sensParams, &_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState])) + if (checkSens && + !contains(_sensParams, + &_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState])) return -1; for (unsigned int t = 0; t < _disc.nParType; ++t) - _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState].setValue(val); + _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState] + .setValue(val); } else if (pId.name == hashString("INIT_Q")) return -1; } else { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { if (checkSens && !contains(_sensParams, &_initCp[pId.particleType * _disc.nComp + pId.component])) return -1; @@ -121,12 +145,19 @@ int LumpedRateModelWithPores::multiplexInitialConditions(const else if (pId.name == hashString("INIT_CP")) return -1; - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { - if (checkSens && !contains(_sensParams, &_initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState])) + if (checkSens && + !contains(_sensParams, + &_initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState])) return -1; - _initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState].setValue(val); + _initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState] + .setValue(val); } else if (pId.name == hashString("INIT_Q")) return -1; @@ -148,7 +179,8 @@ void LumpedRateModelWithPores::applyInitialCondition(const Sim if (!_initStateDot.empty()) { std::fill(simState.vecStateYdot, simState.vecStateYdot + idxr.offsetC(), 0.0); - std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), simState.vecStateYdot + idxr.offsetC()); + std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), + simState.vecStateYdot + idxr.offsetC()); } else std::fill(simState.vecStateYdot, simState.vecStateYdot + numDofs(), 0.0); @@ -199,7 +231,8 @@ void LumpedRateModelWithPores::readInitialCondition(IParameter // Check if INIT_STATE contains the full state and its time derivative if (initState.size() >= 2 * numPureDofs()) - _initStateDot = std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); + _initStateDot = + std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); return; } @@ -215,7 +248,8 @@ void LumpedRateModelWithPores::readInitialCondition(IParameter { const std::vector initCp = paramProvider.getDoubleArray("INIT_CP"); - if (((initCp.size() < _disc.nComp) && _singleBinding) || ((initCp.size() < _disc.nComp * _disc.nParType) && !_singleBinding)) + if (((initCp.size() < _disc.nComp) && _singleBinding) || + ((initCp.size() < _disc.nComp * _disc.nParType) && !_singleBinding)) throw InvalidParameterException("INIT_CP does not contain enough values for all components"); if (!_singleBinding) @@ -239,7 +273,9 @@ void LumpedRateModelWithPores::readInitialCondition(IParameter if (initQ.empty() || (_disc.strideBound[_disc.nParType] == 0)) return; - if ((_disc.strideBound[_disc.nParType] > 0) && (((initQ.size() < _disc.strideBound[_disc.nParType]) && !_singleBinding) || ((initQ.size() < _disc.strideBound[0]) && _singleBinding))) + if ((_disc.strideBound[_disc.nParType] > 0) && + (((initQ.size() < _disc.strideBound[_disc.nParType]) && !_singleBinding) || + ((initQ.size() < _disc.strideBound[0]) && _singleBinding))) throw InvalidParameterException("INIT_Q does not contain enough values for all bound states"); if (!_singleBinding) @@ -301,7 +337,10 @@ void LumpedRateModelWithPores::readInitialCondition(IParameter * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ template -void LumpedRateModelWithPores::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) +void LumpedRateModelWithPores::consistentInitialState(const SimulationTime& simTime, + double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -341,7 +380,7 @@ void LumpedRateModelWithPores::consistentInitialState(const Si const linalg::ConstMaskArray mask{qsMask.data(), static_cast(_disc.nComp + _disc.strideBound[type])}; const int probSize = linalg::numMaskActive(mask); - //Problem capturing variables here + // Problem capturing variables here #ifdef CADET_PARALLELIZE BENCH_SCOPE(_timerConsistentInitPar); tbb::parallel_for(std::size_t(0), static_cast(_disc.nCol), [&](std::size_t pblk) @@ -352,7 +391,8 @@ void LumpedRateModelWithPores::consistentInitialState(const Si LinearBufferAllocator tlmAlloc = threadLocalMem.get(); // Reuse memory of band matrix for dense matrix - linalg::DenseMatrixView fullJacobianMatrix(_jacPdisc[type].data() + pblk * mask.len * mask.len, nullptr, mask.len, mask.len); + linalg::DenseMatrixView fullJacobianMatrix(_jacPdisc[type].data() + pblk * mask.len * mask.len, nullptr, + mask.len, mask.len); // Midpoint of current column cell (z coordinate) - needed in externally dependent adsorption kinetic const double z = (0.5 + static_cast(pblk)) / static_cast(_disc.nCol); @@ -371,25 +411,26 @@ void LumpedRateModelWithPores::consistentInitialState(const Si double* const fullX = static_cast(fullXBuffer); BufferedArray jacobianMemBuffer = tlmAlloc.array(probSize * probSize); - linalg::DenseMatrixView jacobianMatrix(static_cast(jacobianMemBuffer), _jacPdisc[type].pivot() + pblk * probSize, probSize, probSize); + linalg::DenseMatrixView jacobianMatrix(static_cast(jacobianMemBuffer), + _jacPdisc[type].pivot() + pblk * probSize, probSize, probSize); BufferedArray conservedQuantsBuffer = tlmAlloc.array(numActiveComp); double* const conservedQuants = static_cast(conservedQuantsBuffer); - const parts::cell::CellParameters cellResParams - { - _disc.nComp, - _disc.nBound + _disc.nComp * type, - _disc.boundOffset + _disc.nComp * type, - _disc.strideBound[type], - _binding[type]->reactionQuasiStationarity(), - _parPorosity[type], - _poreAccessFactor.data() + _disc.nComp * type, - _binding[type], - (_dynReaction[type] && (_dynReaction[type]->numReactionsCombined() > 0)) ? _dynReaction[type] : nullptr - }; - - const int localOffsetToParticle = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); + const parts::cell::CellParameters cellResParams{ + _disc.nComp, + _disc.nBound + _disc.nComp * type, + _disc.boundOffset + _disc.nComp * type, + _disc.strideBound[type], + _binding[type]->reactionQuasiStationarity(), + _parPorosity[type], + _poreAccessFactor.data() + _disc.nComp * type, + _binding[type], + (_dynReaction[type] && (_dynReaction[type]->numReactionsCombined() > 0)) ? _dynReaction[type] + : nullptr}; + + const int localOffsetToParticle = + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); const int localOffsetInParticle = idxr.strideParLiquid(); // Get pointer to q variables in a shell of particle pblk @@ -400,7 +441,8 @@ void LumpedRateModelWithPores::consistentInitialState(const Si const ColumnPosition colPos{z, 0.0, static_cast(_parRadius[type]) * 0.5}; // Determine whether nonlinear solver is required - if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - idxr.strideParLiquid(), tlmAlloc)) + if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - idxr.strideParLiquid(), tlmAlloc)) CADET_PAR_CONTINUE; // Extract initial values from current state @@ -408,15 +450,16 @@ void LumpedRateModelWithPores::consistentInitialState(const Si // Save values of conserved moieties const double epsQ = 1.0 - static_cast(_parPorosity[type]); - linalg::conservedMoietiesFromPartitionedMask(mask, _disc.nBound + type * _disc.nComp, _disc.nComp, qShell - _disc.nComp, conservedQuants, static_cast(_parPorosity[type]), epsQ); + linalg::conservedMoietiesFromPartitionedMask(mask, _disc.nBound + type * _disc.nComp, _disc.nComp, + qShell - _disc.nComp, conservedQuants, + static_cast(_parPorosity[type]), epsQ); std::function jacFunc; if (localAdY && localAdRes) { - jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) - { - // Copy over state vector to AD state vector (without changing directional values to keep seed vectors) - // and initialize residuals with zero (also resetting directional values) + jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) { + // Copy over state vector to AD state vector (without changing directional values to keep seed + // vectors) and initialize residuals with zero (also resetting directional values) ad::copyToAd(qShell - _disc.nComp, localAdY, mask.len); // @todo Check if this is necessary ad::resetAd(localAdRes, mask.len); @@ -425,32 +468,34 @@ void LumpedRateModelWithPores::consistentInitialState(const Si linalg::applyVectorSubset(x, mask, localAdY); // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, localAdY, nullptr, localAdRes, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, localAdY, nullptr, localAdRes, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); #ifdef CADET_CHECK_ANALYTIC_JACOBIAN std::copy_n(qShell - _disc.nComp, mask.len, fullX); linalg::applyVectorSubset(x, mask, fullX); // Compute analytic Jacobian - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); // Compare const double diff = ad::compareDenseJacobianWithBandedAd( - adJac.adRes + idxr.offsetCp(ParticleTypeIndex{type}), pblk * idxr.strideParBlock(type), adJac.adDirOffset, _jacP[type].lowerBandwidth(), - _jacP[type].lowerBandwidth(), _jacP[type].upperBandwidth(), fullJacobianMatrix - ); + adJac.adRes + idxr.offsetCp(ParticleTypeIndex{type}), pblk * idxr.strideParBlock(type), + adJac.adDirOffset, _jacP[type].lowerBandwidth(), _jacP[type].lowerBandwidth(), + _jacP[type].upperBandwidth(), fullJacobianMatrix); LOG(Debug) << "MaxDiff: " << diff; #endif // Extract Jacobian from AD - ad::extractDenseJacobianFromBandedAd( - adJac.adRes + idxr.offsetCp(ParticleTypeIndex{type}), pblk * idxr.strideParBlock(type), adJac.adDirOffset, _jacP[type].lowerBandwidth(), - _jacP[type].lowerBandwidth(), _jacP[type].upperBandwidth(), fullJacobianMatrix - ); + ad::extractDenseJacobianFromBandedAd(adJac.adRes + idxr.offsetCp(ParticleTypeIndex{type}), + pblk * idxr.strideParBlock(type), adJac.adDirOffset, + _jacP[type].lowerBandwidth(), _jacP[type].lowerBandwidth(), + _jacP[type].upperBandwidth(), fullJacobianMatrix); // Extract Jacobian from full Jacobian mat.setAll(0.0); @@ -489,16 +534,16 @@ void LumpedRateModelWithPores::consistentInitialState(const Si } else { - jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) - { + jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) { // Prepare input vector by overwriting masked items std::copy_n(qShell - _disc.nComp, mask.len, fullX); linalg::applyVectorSubset(x, mask, fullX); // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); // Extract Jacobian from full Jacobian mat.setAll(0.0); @@ -538,16 +583,16 @@ void LumpedRateModelWithPores::consistentInitialState(const Si // Apply nonlinear solver _nonlinearSolver->solve( - [&](double const* const x, double* const r) - { + [&](double const* const x, double* const r) { // Prepare input vector by overwriting masked items std::copy_n(qShell - _disc.nComp, mask.len, fullX); linalg::applyVectorSubset(x, mask, fullX); // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); // Extract values from residual linalg::selectVectorSubset(fullResidual, mask, r); @@ -587,7 +632,8 @@ void LumpedRateModelWithPores::consistentInitialState(const Si linalg::applyVectorSubset(solution, mask, qShell - idxr.strideParLiquid()); // Refine / correct solution - _binding[type]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - idxr.strideParLiquid(), tlmAlloc); + _binding[type]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - idxr.strideParLiquid(), tlmAlloc); } CADET_PARFOR_END; } @@ -646,10 +692,13 @@ void LumpedRateModelWithPores::consistentInitialState(const Si * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] vecStateY Consistently initialized state vector - * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent state time derivatives. + * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent + * state time derivatives. */ template -void LumpedRateModelWithPores::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem) +void LumpedRateModelWithPores::consistentInitialTimeDerivative( + const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -693,18 +742,21 @@ void LumpedRateModelWithPores::consistentInitialTimeDerivative continue; // Get iterators to beginning of solid phase - linalg::BandMatrix::RowIterator jacSolidOrig = _jacP[type].row(idxr.strideParBlock(type) * pblk + static_cast(idxr.strideParLiquid())); + linalg::BandMatrix::RowIterator jacSolidOrig = + _jacP[type].row(idxr.strideParBlock(type) * pblk + static_cast(idxr.strideParLiquid())); linalg::FactorizableBandMatrix::RowIterator jacSolid = jac - idxr.strideParBound(type); int const* const mask = _binding[type]->reactionQuasiStationarity(); - double* const qShellDot = vecStateYdot + idxr.offsetCp(ParticleTypeIndex{static_cast(type)}, ParticleIndex{pblk}) + idxr.strideParLiquid(); + double* const qShellDot = + vecStateYdot + idxr.offsetCp(ParticleTypeIndex{static_cast(type)}, ParticleIndex{pblk}) + + idxr.strideParLiquid(); // Obtain derivative of fluxes wrt. time std::fill_n(dFluxDt, _disc.strideBound[type], 0.0); if (_binding[type]->dependsOnTime()) { - _binding[type]->timeDerivativeQuasiStationaryFluxes(simTime.t, simTime.secIdx, - ColumnPosition{z, 0.0, static_cast(_parRadius[type]) * 0.5}, + _binding[type]->timeDerivativeQuasiStationaryFluxes( + simTime.t, simTime.secIdx, ColumnPosition{z, 0.0, static_cast(_parRadius[type]) * 0.5}, qShellDot - _disc.nComp, qShellDot, dFluxDt, tlmAlloc); } @@ -731,7 +783,8 @@ void LumpedRateModelWithPores::consistentInitialTimeDerivative LOG(Error) << "Factorize() failed for par type block " << type; } - const bool result2 = _jacPdisc[type].solve(scaleFactors, vecStateYdot + idxr.offsetCp(ParticleTypeIndex{static_cast(type)})); + const bool result2 = _jacPdisc[type].solve( + scaleFactors, vecStateYdot + idxr.offsetCp(ParticleTypeIndex{static_cast(type)})); if (!result2) { LOG(Error) << "Solve() failed for par type block " << type; @@ -751,8 +804,6 @@ void LumpedRateModelWithPores::consistentInitialTimeDerivative solveForFluxes(vecStateYdot, idxr); } - - /** * @brief Computes approximately / partially consistent initial values (state variables without their time derivatives) * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have @@ -796,7 +847,11 @@ void LumpedRateModelWithPores::consistentInitialTimeDerivative * @param [in] errorTol Error tolerance for algebraic equations */ template -void LumpedRateModelWithPores::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) +void LumpedRateModelWithPores::leanConsistentInitialState(const SimulationTime& simTime, + double* const vecStateY, + const AdJacobianParams& adJac, + double errorTol, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -850,11 +905,15 @@ void LumpedRateModelWithPores::leanConsistentInitialState(cons * * @param [in] t Current time point * @param [in] vecStateY (Lean) consistently initialized state vector - * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time derivatives. - * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during execution of the function. + * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time + * derivatives. + * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during + * execution of the function. */ template -void LumpedRateModelWithPores::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem) +void LumpedRateModelWithPores::leanConsistentInitialTimeDerivative( + double t, double const* const vecStateY, double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -928,25 +987,26 @@ void LumpedRateModelWithPores::initializeSensitivityStates(con * @brief Computes consistent initial values and time derivatives of sensitivity subsystems * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, * the sensitivity system for a parameter @f$ p @f$ reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of this linear DAE, @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ - * have to be consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the time - * derivative \f$ \dot{s}_0 \f$ such that they are consistent. + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of this linear DAE, @f$ s_0 = + * \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ have to be + * consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the + * time derivative \f$ \dot{s}_0 \f$ such that they are consistent. * - * The process follows closely the one of consistentInitialConditions() and, in fact, is a linearized version of it. - * This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ are - * related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them - * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p}. @f$ - *
                            - *
                          1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria). - * Once all @f$ c_i @f$, @f$ c_{p,i} @f$, and @f$ q_i^{(j)} @f$ have been computed, solve for the - * fluxes @f$ j_{f,i} @f$. Let @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at this point, we have - * \f[ \left( \frac{\partial F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, y_0, \dot{y}_0) \right)_{\mathcal{I}_a} = 0. \f]
                          2. - *
                          3. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. - * However, because of the algebraic equations, we need additional conditions to fully determine + * The process follows closely the one of consistentInitialConditions() and, in fact, is a linearized version + * of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ + * are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them + * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial + * \dot{y}_0}{\partial p}. @f$
                            1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, + * reaction equilibria). Once all @f$ c_i @f$, @f$ c_{p,i} @f$, and @f$ q_i^{(j)} @f$ have been computed, solve for the + * fluxes @f$ j_{f,i} @f$. Let @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at + * this point, we have \f[ \left( \frac{\partial F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, + * y_0, \dot{y}_0) \right)_{\mathcal{I}_a} = 0. \f]
                            2. Compute the time derivatives of the sensitivity @f$ \dot{s} + * @f$ such that the differential equations hold. However, because of the algebraic equations, we need additional + * conditions to fully determine * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the - * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting system - * has a similar structure as the system Jacobian. + * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting + * system has a similar structure as the system Jacobian. * @f[ \begin{align} * \left[\begin{array}{c|ccc|c} * \dot{J}_0 & & & & \\ @@ -962,8 +1022,9 @@ void LumpedRateModelWithPores::initializeSensitivityStates(con * @f$ J_{i,f} @f$ matrices in the right column are missing. * * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. - * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] - * which is 0 for algebraic equations (@f$ -\frac{\partial^2 F}{\partial t \partial p}@f$, to be more precise). + * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - + * \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] which is 0 for algebraic equations (@f$ -\frac{\partial^2 + * F}{\partial t \partial p}@f$, to be more precise). * * The linear system is solved by backsubstitution. First, the diagonal blocks are solved in parallel. * Then, the equations for the fluxes @f$ j_f @f$ are solved by substituting in the solution of the @@ -978,8 +1039,9 @@ void LumpedRateModelWithPores::initializeSensitivityStates(con * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ template -void LumpedRateModelWithPores::consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +void LumpedRateModelWithPores::consistentInitialSensitivity( + const SimulationTime& simTime, const ConstSimulationState& simState, std::vector& vecSensY, + std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -1014,7 +1076,8 @@ void LumpedRateModelWithPores::consistentInitialSensitivity(co #endif { // Reuse memory of band matrix for dense matrix - linalg::DenseMatrixView jacobianMatrix(_jacPdisc[type].data() + pblk * probSize * probSize, _jacPdisc[type].pivot() + pblk * probSize, probSize, probSize); + linalg::DenseMatrixView jacobianMatrix(_jacPdisc[type].data() + pblk * probSize * probSize, + _jacPdisc[type].pivot() + pblk * probSize, probSize, probSize); // Get workspace memory LinearBufferAllocator tlmAlloc = threadLocalMem.get(); @@ -1025,11 +1088,15 @@ void LumpedRateModelWithPores::consistentInitialSensitivity(co BufferedArray rhsUnmaskedBuffer = tlmAlloc.array(idxr.strideParBound(type)); double* const rhsUnmasked = static_cast(rhsUnmaskedBuffer); - double* const maskedMultiplier = _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); - double* const scaleFactors = _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); + double* const maskedMultiplier = + _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); + double* const scaleFactors = + _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); const int jacRowOffset = idxr.strideParBlock(type) * pblk + _disc.nComp; - const int localQOffset = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}) + idxr.strideParLiquid(); + const int localQOffset = + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}) + + idxr.strideParLiquid(); // Extract subproblem Jacobian from full Jacobian jacobianMatrix.setAll(0.0); @@ -1039,11 +1106,14 @@ void LumpedRateModelWithPores::consistentInitialSensitivity(co linalg::selectVectorSubset(sensYdot + localQOffset, mask, rhs); // Zero out masked elements - std::copy_n(sensY + localQOffset - idxr.strideParLiquid(), _disc.nComp + _disc.strideBound[type], maskedMultiplier); + std::copy_n(sensY + localQOffset - idxr.strideParLiquid(), _disc.nComp + _disc.strideBound[type], + maskedMultiplier); linalg::fillVectorSubset(maskedMultiplier + _disc.nComp, mask, 0.0); // Assemble right hand side - _jacP[type].submatrixMultiplyVector(maskedMultiplier, jacRowOffset, -static_cast(_disc.nComp), _disc.strideBound[type], _disc.nComp + _disc.strideBound[type], rhsUnmasked); + _jacP[type].submatrixMultiplyVector(maskedMultiplier, jacRowOffset, -static_cast(_disc.nComp), + _disc.strideBound[type], _disc.nComp + _disc.strideBound[type], + rhsUnmasked); linalg::vectorSubsetAdd(rhsUnmasked, mask, -1.0, 1.0, rhs); // Precondition @@ -1098,11 +1168,15 @@ void LumpedRateModelWithPores::consistentInitialSensitivity(co if (_binding[type]->hasQuasiStationaryReactions()) { // Get iterators to beginning of solid phase - linalg::BandMatrix::RowIterator jacSolidOrig = _jacP[type].row(idxr.strideParBlock(type) * pblk + static_cast(idxr.strideParLiquid())); + linalg::BandMatrix::RowIterator jacSolidOrig = _jacP[type].row( + idxr.strideParBlock(type) * pblk + static_cast(idxr.strideParLiquid())); linalg::FactorizableBandMatrix::RowIterator jacSolid = jac - idxr.strideParBound(type); int const* const mask = _binding[type]->reactionQuasiStationarity(); - double* const qShellDot = sensYdot + idxr.offsetCp(ParticleTypeIndex{static_cast(type)}, ParticleIndex{pblk}) + idxr.strideParLiquid(); + double* const qShellDot = + sensYdot + + idxr.offsetCp(ParticleTypeIndex{static_cast(type)}, ParticleIndex{pblk}) + + idxr.strideParLiquid(); // Copy row from original Jacobian and set right hand side for (int i = 0; i < idxr.strideParBound(type); ++i, ++jacSolid, ++jacSolidOrig) @@ -1133,7 +1207,8 @@ void LumpedRateModelWithPores::consistentInitialSensitivity(co } // Solve - const bool result2 = _jacPdisc[type].solve(scaleFactors, sensYdot + idxr.offsetCp(ParticleTypeIndex{static_cast(type)})); + const bool result2 = _jacPdisc[type].solve( + scaleFactors, sensYdot + idxr.offsetCp(ParticleTypeIndex{static_cast(type)})); if (!result2) { LOG(Error) << "Solve() failed for par type block " << type; @@ -1157,20 +1232,20 @@ void LumpedRateModelWithPores::consistentInitialSensitivity(co * @brief Computes approximately / partially consistent initial values and time derivatives of sensitivity subsystems * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, * the sensitivity system for a parameter @f$ p @f$ reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of this linear DAE, @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ - * have to be consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the time - * derivative \f$ \dot{s}_0 \f$ such that they are consistent. + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of this linear DAE, @f$ s_0 = + * \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ have to be + * consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the + * time derivative \f$ \dot{s}_0 \f$ such that they are consistent. * - * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized version of it. - * This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ are - * related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them - * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p}. @f$ - *
                                - *
                              1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations). - * Only solve for the fluxes @f$ j_{f,i} @f$ (only linear equations).
                              2. - *
                              3. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. - * However, because of the algebraic equations, we need additional conditions to fully determine + * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized + * version of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ + * \dot{s}_0 \f$ are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by + * differentiating them with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = + * \frac{\partial \dot{y}_0}{\partial p}. @f$
                                1. Keep state and time derivative vectors as they are (i.e., do not + * solve algebraic equations). Only solve for the fluxes @f$ j_{f,i} @f$ (only linear equations).
                                2. Compute the + * time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. However, because of + * the algebraic equations, we need additional conditions to fully determine * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting * equations are stated below: @@ -1184,8 +1259,9 @@ void LumpedRateModelWithPores::consistentInitialSensitivity(co * where @f$ \dot{J}_0 @f$ denotes the bulk block Jacobian with respect to @f$ \dot{y}@f$. * * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. - * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] - * which is 0 for algebraic equations (@f$ -\frac{\partial^2 F}{\partial t \partial p}@f$, to be more precise). + * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - + * \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] which is 0 for algebraic equations (@f$ -\frac{\partial^2 + * F}{\partial t \partial p}@f$, to be more precise). * * The linear system is solved by backsubstitution. First, the bulk block is solved. * Then, the equations for the fluxes @f$ j_f @f$ are solved by substituting in the solution of the @@ -1200,8 +1276,9 @@ void LumpedRateModelWithPores::consistentInitialSensitivity(co * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ template -void LumpedRateModelWithPores::leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +void LumpedRateModelWithPores::leanConsistentInitialSensitivity( + const SimulationTime& simTime, const ConstSimulationState& simState, std::vector& vecSensY, + std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -1269,6 +1346,6 @@ void LumpedRateModelWithPores::solveForFluxes(double* const ve _jacFP[type].multiplySubtract(vecState + idxr.offsetCp(ParticleTypeIndex{type}), jf); } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/LumpedRateModelWithPores-LinearSolver.cpp b/src/libcadet/model/LumpedRateModelWithPores-LinearSolver.cpp index 83e372b23..69897e1c6 100644 --- a/src/libcadet/model/LumpedRateModelWithPores-LinearSolver.cpp +++ b/src/libcadet/model/LumpedRateModelWithPores-LinearSolver.cpp @@ -25,11 +25,11 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include - #include +#include +#include - typedef tbb::flow::continue_node< tbb::flow::continue_msg > node_t; - typedef const tbb::flow::continue_msg & msg_t; +typedef tbb::flow::continue_node node_t; +typedef const tbb::flow::continue_msg& msg_t; #endif namespace cadet @@ -40,12 +40,16 @@ namespace model /** * @brief Computes the solution of the linear system involving the system Jacobian - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + b \f] * has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the - * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) \f$, - * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p rhs. + * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) + \f$, + * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p + rhs. * - * The full Jacobian @f$ J = \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) @f$ is given by + * The full Jacobian @f$ J = \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} + \right) @f$ is given by * @f[ \begin{align} J = \left[\begin{array}{c|ccc|c} @@ -86,13 +90,16 @@ namespace model * Note that @f$ J_f = I @f$ is the identity matrix and that the off-diagonal blocks @f$ J_{i,f} @f$ * and @f$ J_{f,i} @f$ for @f$ i = 0, \dots, N_{z} @f$ are sparse. * - * Exploiting the decomposition, the solution procedure @f$ x = J^{-1}b = \left( LU \right)^{-1}b = U^{-1} L^{-1} b @f$ + * Exploiting the decomposition, the solution procedure @f$ x = J^{-1}b = \left( LU \right)^{-1}b = U^{-1} + L^{-1} b @f$ * works as follows: * -# Factorize the diagonal blocks @f$ J_0, \dots, J_{N_z} @f$ - * -# Solve @f$ y = L^{-1} b @f$ by forward substitution. This is accomplished by first solving the diagonal + * -# Solve @f$ y = L^{-1} b @f$ by forward substitution. This is accomplished by first solving the + diagonal * blocks independently, that is, * @f[ y_i = J_{i}^{-1} b_i. @f] - * Then, calculate the flux-part @f$ y_f @f$ by substituting in the already calculated solutions @f$ y_i @f$: + * Then, calculate the flux-part @f$ y_f @f$ by substituting in the already calculated solutions @f$ y_i + @f$: * @f[ y_f = b_f - \sum_{i=0}^{N_z} J_{f,i} y_i. @f] * -# Solve the Schur-complement @f$ S x_f = y_f @f$ using an iterative method that only requires * matrix-vector products. The already inverted diagonal blocks @f$ J_i^{-1} @f$ come in handy here. @@ -105,12 +112,14 @@ namespace model * @param [in] outerTol Error tolerance for the solution of the linear system from outer Newton iteration * @param [in,out] rhs On entry the right hand side of the linear equation system, on exit the solution * @param [in] weight Vector with error weights - * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is evaluated + * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is + evaluated * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ template -int LumpedRateModelWithPores::linearSolve(double t, double alpha, double outerTol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) +int LumpedRateModelWithPores::linearSolve(double t, double alpha, double outerTol, double* const rhs, + double const* const weight, + const ConstSimulationState& simState) { BENCH_SCOPE(_timerLinearSolve); @@ -131,15 +140,15 @@ int LumpedRateModelWithPores::linearSolve(double t, double alp node_t A(g, [&](msg_t) #endif { - // Assemble and factorize discretized bulk Jacobian - const bool result = _convDispOp.assembleAndFactorizeDiscretizedJacobian(alpha); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Factorize() failed for bulk block"; - } + // Assemble and factorize discretized bulk Jacobian + const bool result = _convDispOp.assembleAndFactorizeDiscretizedJacobian(alpha); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Factorize() failed for bulk block"; + } } CADET_PARNODE_END; - // Process the particle blocks + // Process the particle blocks #ifdef CADET_PARALLELIZE node_t B(g, [&](msg_t) #endif @@ -150,49 +159,48 @@ int LumpedRateModelWithPores::linearSolve(double t, double alp for (unsigned int type = 0; type < _disc.nParType; ++type) #endif { - // Assemble - assembleDiscretizedJacobianParticleBlock(type, alpha, idxr); - - // Factorize - const bool result = _jacPdisc[type].factorize(); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Factorize() failed for par type block " << type; - } + // Assemble + assembleDiscretizedJacobianParticleBlock(type, alpha, idxr); + + // Factorize + const bool result = _jacPdisc[type].factorize(); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Factorize() failed for par type block " << type; + } } CADET_PARFOR_END; } CADET_PARNODE_END; #ifndef CADET_PARALLELIZE // Do not factorize again at next call without changed Jacobians _factorizeJacobian = false; - } // if (_factorizeJacobian) +} // if (_factorizeJacobian) #endif - // ====== Step 1.5: Solve J c_uo = b_uo - A * c_in = b_uo - A*b_in +// ====== Step 1.5: Solve J c_uo = b_uo - A * c_in = b_uo - A*b_in - // rhs is passed twice but due to the values in jacA the writes happen to a different area of the rhs than the reads. +// rhs is passed twice but due to the values in jacA the writes happen to a different area of the rhs than the reads. #ifdef CADET_PARALLELIZE node_t C(g, [&](msg_t) #endif { - _jacInlet.multiplySubtract(rhs, rhs + idxr.offsetC()); + _jacInlet.multiplySubtract(rhs, rhs + idxr.offsetC()); } CADET_PARNODE_END; // ==== Step 2: Solve diagonal Jacobian blocks J_i to get y_i = J_i^{-1} b_i // The result is stored in rhs (in-place solution) - // Threads that are done with solving the bulk column blocks can proceed // to solving the particle blocks #ifdef CADET_PARALLELIZE node_t D(g, [&](msg_t) #endif { - const bool result = _convDispOp.solveDiscretizedJacobian(rhs + idxr.offsetC()); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Solve() failed for bulk block"; - } + const bool result = _convDispOp.solveDiscretizedJacobian(rhs + idxr.offsetC()); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Solve() failed for bulk block"; + } } CADET_PARNODE_END; #ifdef CADET_PARALLELIZE @@ -202,14 +210,15 @@ int LumpedRateModelWithPores::linearSolve(double t, double alp #ifdef CADET_PARALLELIZE tbb::parallel_for(std::size_t(0), static_cast(_disc.nParType), [&](std::size_t type) #else - for (unsigned int type = 0; type < _disc.nParType; ++type) + for (unsigned int type = 0; type < _disc.nParType; ++type) #endif { - const bool result = _jacPdisc[type].solve(rhs + idxr.offsetCp(ParticleTypeIndex{static_cast(type)})); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Solve() failed for par type block " << type; - } + const bool result = + _jacPdisc[type].solve(rhs + idxr.offsetCp(ParticleTypeIndex{static_cast(type)})); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Solve() failed for par type block " << type; + } } CADET_PARFOR_END; } CADET_PARNODE_END; @@ -221,40 +230,40 @@ int LumpedRateModelWithPores::linearSolve(double t, double alp node_t F(g, [&](msg_t) #endif { - _jacFC.multiplySubtract(rhs + idxr.offsetC(), rhs + idxr.offsetJf()); - for (unsigned int type = 0; type < _disc.nParType; ++type) - _jacFP[type].multiplySubtract(rhs + idxr.offsetCp(ParticleTypeIndex{type}), rhs + idxr.offsetJf()); + _jacFC.multiplySubtract(rhs + idxr.offsetC(), rhs + idxr.offsetJf()); + for (unsigned int type = 0; type < _disc.nParType; ++type) + _jacFP[type].multiplySubtract(rhs + idxr.offsetCp(ParticleTypeIndex{type}), rhs + idxr.offsetJf()); - // Now, rhs contains the full intermediate solution y = L^{-1} b + // Now, rhs contains the full intermediate solution y = L^{-1} b - // Initialize temporary storage by copying over the fluxes - // Note that the rest of _tempState is zeroed out in schurComplementMatrixVector() - std::copy(rhs + idxr.offsetJf(), rhs + numDofs(), _tempState + idxr.offsetJf()); + // Initialize temporary storage by copying over the fluxes + // Note that the rest of _tempState is zeroed out in schurComplementMatrixVector() + std::copy(rhs + idxr.offsetJf(), rhs + numDofs(), _tempState + idxr.offsetJf()); - // ==== Step 3: Solve Schur-complement to get x_f = S^{-1} y_f - // Column and particle parts remain unchanged. - // The only thing to be done is the iterative (and approximate) - // solution of the Schur complement system: - // S * x_f = y_f + // ==== Step 3: Solve Schur-complement to get x_f = S^{-1} y_f + // Column and particle parts remain unchanged. + // The only thing to be done is the iterative (and approximate) + // solution of the Schur complement system: + // S * x_f = y_f - // Note that rhs is updated in-place with the solution of the Schur-complement - // The temporary storage is only needed to hold the right hand side of the Schur-complement - const double tolerance = std::sqrt(static_cast(numDofs())) * outerTol * _schurSafety; + // Note that rhs is updated in-place with the solution of the Schur-complement + // The temporary storage is only needed to hold the right hand side of the Schur-complement + const double tolerance = std::sqrt(static_cast(numDofs())) * outerTol * _schurSafety; - BENCH_START(_timerGmres); - _gmres.solve(tolerance, weight + idxr.offsetJf(), _tempState + idxr.offsetJf(), rhs + idxr.offsetJf()); - BENCH_STOP(_timerGmres); + BENCH_START(_timerGmres); + _gmres.solve(tolerance, weight + idxr.offsetJf(), _tempState + idxr.offsetJf(), rhs + idxr.offsetJf()); + BENCH_STOP(_timerGmres); - // Remove temporary results that are leftovers from schurComplementMatrixVector() - std::fill(_tempState + idxr.offsetC(), _tempState + idxr.offsetJf(), 0.0); + // Remove temporary results that are leftovers from schurComplementMatrixVector() + std::fill(_tempState + idxr.offsetC(), _tempState + idxr.offsetJf(), 0.0); - // At this point, rhs contains the intermediate solution [y_0, ..., y_{N_z}, x_f] + // At this point, rhs contains the intermediate solution [y_0, ..., y_{N_z}, x_f] - // ==== Step 4: Solve U * x = y by backward substitution - // The fluxes are already solved and remain unchanged + // ==== Step 4: Solve U * x = y by backward substitution + // The fluxes are already solved and remain unchanged - // Compute tempState_0 = J_{0,f} * y_f - _jacCF.multiplyAdd(rhs + idxr.offsetJf(), _tempState + idxr.offsetC()); + // Compute tempState_0 = J_{0,f} * y_f + _jacCF.multiplyAdd(rhs + idxr.offsetJf(), _tempState + idxr.offsetC()); } CADET_PARNODE_END; // Threads that are done with solving the bulk column blocks can proceed @@ -263,19 +272,19 @@ int LumpedRateModelWithPores::linearSolve(double t, double alp node_t G(g, [&](msg_t) #endif { - double* const localCol = _tempState + idxr.offsetC(); - double* const rhsCol = rhs + idxr.offsetC(); + double* const localCol = _tempState + idxr.offsetC(); + double* const rhsCol = rhs + idxr.offsetC(); - // Apply J_0^{-1} to tempState_0 - const bool result = _convDispOp.solveDiscretizedJacobian(localCol); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Solve() failed for bulk block"; - } + // Apply J_0^{-1} to tempState_0 + const bool result = _convDispOp.solveDiscretizedJacobian(localCol); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Solve() failed for bulk block"; + } - // Compute rhs_0 = y_0 - J_0^{-1} * J_{0,f} * y_f = y_0 - tempState_0 - for (unsigned int i = 0; i < _disc.nCol * _disc.nComp; ++i) - rhsCol[i] -= localCol[i]; + // Compute rhs_0 = y_0 - J_0^{-1} * J_{0,f} * y_f = y_0 - tempState_0 + for (unsigned int i = 0; i < _disc.nCol * _disc.nComp; ++i) + rhsCol[i] -= localCol[i]; } CADET_PARNODE_END; #ifdef CADET_PARALLELIZE @@ -285,24 +294,24 @@ int LumpedRateModelWithPores::linearSolve(double t, double alp #ifdef CADET_PARALLELIZE tbb::parallel_for(std::size_t(0), static_cast(_disc.nParType), [&](std::size_t type) #else - for (unsigned int type = 0; type < _disc.nParType; ++type) + for (unsigned int type = 0; type < _disc.nParType; ++type) #endif { - double* const localPar = _tempState + idxr.offsetCp(ParticleTypeIndex{static_cast(type)}); - double* const rhsPar = rhs + idxr.offsetCp(ParticleTypeIndex{static_cast(type)}); + double* const localPar = _tempState + idxr.offsetCp(ParticleTypeIndex{static_cast(type)}); + double* const rhsPar = rhs + idxr.offsetCp(ParticleTypeIndex{static_cast(type)}); - // Compute tempState_i = J_{i,f} * y_f - _jacPF[type].multiplyAdd(rhs + idxr.offsetJf(), localPar); - // Apply J_i^{-1} to tempState_i - const bool result = _jacPdisc[type].solve(localPar); - if (cadet_unlikely(!result)) - { - LOG(Error) << "Solve() failed for par type block " << type; - } + // Compute tempState_i = J_{i,f} * y_f + _jacPF[type].multiplyAdd(rhs + idxr.offsetJf(), localPar); + // Apply J_i^{-1} to tempState_i + const bool result = _jacPdisc[type].solve(localPar); + if (cadet_unlikely(!result)) + { + LOG(Error) << "Solve() failed for par type block " << type; + } - // Compute rhs_i = y_i - J_i^{-1} * J_{i,f} * y_f = y_i - tempState_i - for (unsigned int i = 0; i < idxr.strideParBlock(type) * _disc.nCol; ++i) - rhsPar[i] -= localPar[i]; + // Compute rhs_i = y_i - J_i^{-1} * J_{i,f} * y_f = y_i - tempState_i + for (unsigned int i = 0; i < idxr.strideParBlock(type) * _disc.nCol; ++i) + rhsPar[i] -= localPar[i]; } CADET_PARFOR_END; } CADET_PARNODE_END; @@ -310,8 +319,8 @@ int LumpedRateModelWithPores::linearSolve(double t, double alp // Create TBB dependency graph if (_factorizeJacobian) { - make_edge(A, C); - make_edge(B, C); + make_edge(A, C); + make_edge(B, C); } make_edge(C, D); @@ -324,11 +333,11 @@ int LumpedRateModelWithPores::linearSolve(double t, double alp // Start the graph running if (_factorizeJacobian) { - // Do not factorize again at next call without changed Jacobians - _factorizeJacobian = false; + // Do not factorize again at next call without changed Jacobians + _factorizeJacobian = false; - A.try_put(tbb::flow::continue_msg()); - B.try_put(tbb::flow::continue_msg()); + A.try_put(tbb::flow::continue_msg()); + B.try_put(tbb::flow::continue_msg()); } else C.try_put(tbb::flow::continue_msg()); @@ -339,45 +348,46 @@ int LumpedRateModelWithPores::linearSolve(double t, double alp // The full solution is now stored in rhs return 0; -} + } -/** - * @brief Performs the matrix-vector product @f$ z = Sx @f$ with the Schur-complement @f$ S @f$ from the Jacobian - * @details The Schur-complement @f$ S @f$ is given by - * @f[ \begin{align} - S &= J_f - J_{f,0} \, J_0^{-1} \, J_{0,f} - \sum_{p=1}^{N_z}{J_{f,p} \, J_p^{-1} \, J_{p,f}} \\ - &= I - \sum_{p=0}^{N_z}{J_{f,p} \, J_p^{-1} \, J_{p,f}}, - \end{align} @f] - * where @f$ J_f = I @f$ is the identity matrix and the off-diagonal blocks @f$ J_{i,f} @f$ - * and @f$ J_{f,i} @f$ for @f$ i = 0, \dots, N_{z} @f$ are sparse. - * - * The matrix-vector multiplication is executed in parallel as follows: - * -# Compute @f$ J_{f,i} \, J_i^{-1} \, J_{i,f} @f$ independently (in parallel with respect to index @f$ i @f$) - * -# Subtract the result from @f$ z @f$ - * - * @param [in] x Vector @f$ x @f$ the matrix @f$ S @f$ is multiplied with - * @param [out] z Result of the matrix-vector multiplication - * @return @c 0 if successful, any other value in case of failure - */ -template -int LumpedRateModelWithPores::schurComplementMatrixVector(double const* x, double* z) const -{ - BENCH_SCOPE(_timerMatVec); + /** + * @brief Performs the matrix-vector product @f$ z = Sx @f$ with the Schur-complement @f$ S @f$ from the Jacobian + * @details The Schur-complement @f$ S @f$ is given by + * @f[ \begin{align} + S &= J_f - J_{f,0} \, J_0^{-1} \, J_{0,f} - \sum_{p=1}^{N_z}{J_{f,p} \, J_p^{-1} \, J_{p,f}} \\ + &= I - \sum_{p=0}^{N_z}{J_{f,p} \, J_p^{-1} \, J_{p,f}}, + \end{align} @f] + * where @f$ J_f = I @f$ is the identity matrix and the off-diagonal blocks @f$ J_{i,f} @f$ + * and @f$ J_{f,i} @f$ for @f$ i = 0, \dots, N_{z} @f$ are sparse. + * + * The matrix-vector multiplication is executed in parallel as follows: + * -# Compute @f$ J_{f,i} \, J_i^{-1} \, J_{i,f} @f$ independently (in parallel with respect to index + @f$ i @f$) + * -# Subtract the result from @f$ z @f$ + * + * @param [in] x Vector @f$ x @f$ the matrix @f$ S @f$ is multiplied with + * @param [out] z Result of the matrix-vector multiplication + * @return @c 0 if successful, any other value in case of failure + */ + template + int LumpedRateModelWithPores::schurComplementMatrixVector(double const* x, double* z) const + { + BENCH_SCOPE(_timerMatVec); - // Copy x over to result z, which corresponds to the application of the identity matrix - std::copy(x, x + _disc.nCol * _disc.nComp * _disc.nParType, z); + // Copy x over to result z, which corresponds to the application of the identity matrix + std::copy(x, x + _disc.nCol * _disc.nComp * _disc.nParType, z); - Indexer idxr(_disc); - std::fill(_tempState + idxr.offsetC(), _tempState + idxr.offsetJf(), 0.0); + Indexer idxr(_disc); + std::fill(_tempState + idxr.offsetC(), _tempState + idxr.offsetJf(), 0.0); #ifdef CADET_PARALLELIZE - tbb::flow::graph g; + tbb::flow::graph g; #endif - // Solve bulk column blocks first + // Solve bulk column blocks first - // Apply J_{0,f} - _jacCF.multiplyAdd(x, _tempState + idxr.offsetC()); + // Apply J_{0,f} + _jacCF.multiplyAdd(x, _tempState + idxr.offsetC()); #ifdef CADET_PARALLELIZE node_t A(g, [&](msg_t) @@ -395,11 +405,11 @@ int LumpedRateModelWithPores::schurComplementMatrixVector(doub node_t B(g, [&](msg_t) #endif { - // Handle particle blocks + // Handle particle blocks #ifdef CADET_PARALLELIZE tbb::parallel_for(std::size_t(0), static_cast(_disc.nParType), [&](std::size_t type) #else - for (unsigned int type = 0; type < _disc.nParType; ++type) + for (unsigned int type = 0; type < _disc.nParType; ++type) #endif { // Get this thread's temporary memory block @@ -443,91 +453,94 @@ int LumpedRateModelWithPores::schurComplementMatrixVector(doub #endif return 0; -} - -/** - * @brief Assembles a particle Jacobian block @f$ J_i @f$ (@f$ i > 0 @f$) of the time-discretized equations - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The system Jacobian of the original equations, - * \f[ \frac{\partial F}{\partial y}, \f] - * is already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible - * for adding - * \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] - * to the system Jacobian, which yields the Jacobian of the time-discretized equations - * \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] - * when a BDF method is used. The time integrator needs to solve this equation for @f$ y_0 @f$, which requires - * the solution of the linear system mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). - * - * @param [in] parType Index of the particle type block - * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) - * @param [in] idxr Indexer - */ -template -void LumpedRateModelWithPores::assembleDiscretizedJacobianParticleBlock(unsigned int parType, double alpha, const Indexer& idxr) -{ - linalg::FactorizableBandMatrix& fbm = _jacPdisc[parType]; - const linalg::BandMatrix& bm = _jacP[parType]; - - // Copy normal matrix over to factorizable matrix - fbm.copyOver(bm); - - // Add time derivatives to particle shells - linalg::FactorizableBandMatrix::RowIterator jac = fbm.row(0); - for (unsigned int j = 0; j < _disc.nCol; ++j) - { - // Mobile and solid phase (advances jac accordingly) - addTimeDerivativeToJacobianParticleBlock(jac, idxr, alpha, parType); } -} -/** - * @brief Adds Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ to bead rows of system Jacobian - * @details Actually adds @f$ \alpha \frac{\partial F}{\partial \dot{y}} @f$, which is useful - * for constructing the linear system in BDF time discretization. - * @param [in,out] jac On entry, RowIterator of the particle block pointing to the beginning of a bead shell; - * on exit, the iterator points to the end of the bead shell - * @param [in] idxr Indexer - * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) - * @param [in] parType Index of the particle type - */ -template -void LumpedRateModelWithPores::addTimeDerivativeToJacobianParticleBlock(linalg::FactorizableBandMatrix::RowIterator& jac, const Indexer& idxr, double alpha, unsigned int parType) -{ - // Mobile phase - for (int comp = 0; comp < static_cast(_disc.nComp); ++comp, ++jac) + /** + * @brief Assembles a particle Jacobian block @f$ J_i @f$ (@f$ i > 0 @f$) of the time-discretized equations + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) + * x = b \f] has to be solved. The system Jacobian of the original equations, \f[ \frac{\partial F}{\partial y}, \f] + * is already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is + * responsible for adding \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] to the system Jacobian, which yields + * the Jacobian of the time-discretized equations \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] when + * a BDF method is used. The time integrator needs to solve this equation for @f$ y_0 @f$, which requires the + * solution of the linear system mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). + * + * @param [in] parType Index of the particle type block + * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) + * @param [in] idxr Indexer + */ + template + void LumpedRateModelWithPores::assembleDiscretizedJacobianParticleBlock(unsigned int parType, + double alpha, + const Indexer& idxr) { - // Add derivative with respect to dc_p / dt to Jacobian - jac[0] += alpha; + linalg::FactorizableBandMatrix& fbm = _jacPdisc[parType]; + const linalg::BandMatrix& bm = _jacP[parType]; - const double invBetaP = (1.0 - static_cast(_parPorosity[parType])) / (static_cast(_poreAccessFactor[parType * _disc.nComp + comp]) * static_cast(_parPorosity[parType])); + // Copy normal matrix over to factorizable matrix + fbm.copyOver(bm); - // Add derivative with respect to dq / dt to Jacobian - const int nBound = static_cast(_disc.nBound[parType * _disc.nComp + comp]); - for (int i = 0; i < nBound; ++i) + // Add time derivatives to particle shells + linalg::FactorizableBandMatrix::RowIterator jac = fbm.row(0); + for (unsigned int j = 0; j < _disc.nCol; ++j) { - // Index explanation: - // -comp -> go back to beginning of liquid phase - // + strideParLiquid() skip to solid phase - // + offsetBoundComp() jump to component (skips all bound states of previous components) - // + i go to current bound state - jac[idxr.strideParLiquid() - comp + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{static_cast(comp)}) + i] += alpha * invBetaP; + // Mobile and solid phase (advances jac accordingly) + addTimeDerivativeToJacobianParticleBlock(jac, idxr, alpha, parType); } } - // Solid phase - int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.strideBound[parType]; ++bnd, ++jac) + /** + * @brief Adds Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ to bead rows of system Jacobian + * @details Actually adds @f$ \alpha \frac{\partial F}{\partial \dot{y}} @f$, which is useful + * for constructing the linear system in BDF time discretization. + * @param [in,out] jac On entry, RowIterator of the particle block pointing to the beginning of a bead shell; + * on exit, the iterator points to the end of the bead shell + * @param [in] idxr Indexer + * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) + * @param [in] parType Index of the particle type + */ + template + void LumpedRateModelWithPores::addTimeDerivativeToJacobianParticleBlock( + linalg::FactorizableBandMatrix::RowIterator& jac, const Indexer& idxr, double alpha, unsigned int parType) { - // Add derivative with respect to dynamic states to Jacobian - if (qsReaction[bnd]) - continue; + // Mobile phase + for (int comp = 0; comp < static_cast(_disc.nComp); ++comp, ++jac) + { + // Add derivative with respect to dc_p / dt to Jacobian + jac[0] += alpha; - // Add derivative with respect to dq / dt to Jacobian - jac[0] += alpha; - } -} + const double invBetaP = (1.0 - static_cast(_parPorosity[parType])) / + (static_cast(_poreAccessFactor[parType * _disc.nComp + comp]) * + static_cast(_parPorosity[parType])); + // Add derivative with respect to dq / dt to Jacobian + const int nBound = static_cast(_disc.nBound[parType * _disc.nComp + comp]); + for (int i = 0; i < nBound; ++i) + { + // Index explanation: + // -comp -> go back to beginning of liquid phase + // + strideParLiquid() skip to solid phase + // + offsetBoundComp() jump to component (skips all bound states of previous components) + // + i go to current bound state + jac[idxr.strideParLiquid() - comp + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{static_cast(comp)}) + + i] += alpha * invBetaP; + } + } + + // Solid phase + int const* const qsReaction = _binding[parType]->reactionQuasiStationarity(); + for (unsigned int bnd = 0; bnd < _disc.strideBound[parType]; ++bnd, ++jac) + { + // Add derivative with respect to dynamic states to Jacobian + if (qsReaction[bnd]) + continue; + + // Add derivative with respect to dq / dt to Jacobian + jac[0] += alpha; + } + } -} // namespace model + } // namespace model -} // namespace cadet + } // namespace cadet diff --git a/src/libcadet/model/LumpedRateModelWithPores.cpp b/src/libcadet/model/LumpedRateModelWithPores.cpp index e9e7b8c79..1e6d8058a 100644 --- a/src/libcadet/model/LumpedRateModelWithPores.cpp +++ b/src/libcadet/model/LumpedRateModelWithPores.cpp @@ -39,7 +39,7 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include +#include #endif namespace cadet @@ -52,20 +52,18 @@ constexpr double SurfVolRatioSphere = 3.0; constexpr double SurfVolRatioCylinder = 2.0; constexpr double SurfVolRatioSlab = 1.0; -template -int schurComplementMultiplierLRMPores(void* userData, double const* x, double* z) +template int schurComplementMultiplierLRMPores(void* userData, double const* x, double* z) { typedef LumpedRateModelWithPores LRMP; LRMP* const lrm = static_cast(userData); return lrm->schurComplementMatrixVector(x, z); } - template -LumpedRateModelWithPores::LumpedRateModelWithPores(UnitOpIdx unitOpIdx) : UnitOperationBase(unitOpIdx), - _dynReactionBulk(nullptr), _filmDiffDep(nullptr), _jacP(0), _jacPdisc(0), _jacPF(0), _jacFP(0), _jacInlet(), _analyticJac(true), - _jacobianAdDirs(0), _factorizeJacobian(false), _tempState(nullptr), _initC(0), _initCp(0), _initQ(0), - _initState(0), _initStateDot(0) +LumpedRateModelWithPores::LumpedRateModelWithPores(UnitOpIdx unitOpIdx) + : UnitOperationBase(unitOpIdx), _dynReactionBulk(nullptr), _filmDiffDep(nullptr), _jacP(0), _jacPdisc(0), _jacPF(0), + _jacFP(0), _jacInlet(), _analyticJac(true), _jacobianAdDirs(0), _factorizeJacobian(false), _tempState(nullptr), + _initC(0), _initCp(0), _initQ(0), _initState(0), _initStateDot(0) { } @@ -103,9 +101,7 @@ unsigned int LumpedRateModelWithPores::numPureDofs() const CAD return _disc.nComp * _disc.nCol * (1 + _disc.nParType) + _disc.parTypeOffset[_disc.nParType]; } - -template -bool LumpedRateModelWithPores::usesAD() const CADET_NOEXCEPT +template bool LumpedRateModelWithPores::usesAD() const CADET_NOEXCEPT { #ifdef CADET_CHECK_ANALYTIC_JACOBIAN // We always need AD if we want to check the analytical Jacobian @@ -117,7 +113,8 @@ bool LumpedRateModelWithPores::usesAD() const CADET_NOEXCEPT } template -bool LumpedRateModelWithPores::configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper) +bool LumpedRateModelWithPores::configureModelDiscretization(IParameterProvider& paramProvider, + const IConfigHelper& helper) { // ==== Read discretization _disc.nComp = paramProvider.getInt("NCOMP"); @@ -130,7 +127,8 @@ bool LumpedRateModelWithPores::configureModelDiscretization(IP _disc.nCol = paramProvider.getInt("NCOL"); - if (!newNBoundInterface && paramProvider.exists("NBOUND")) // done here and in this order for backwards compatibility + if (!newNBoundInterface && + paramProvider.exists("NBOUND")) // done here and in this order for backwards compatibility nBound = paramProvider.getIntArray("NBOUND"); else { @@ -139,12 +137,15 @@ bool LumpedRateModelWithPores::configureModelDiscretization(IP paramProvider.pushScope("discretization"); } if (nBound.size() < _disc.nComp) - throw InvalidParameterException("Field NBOUND contains too few elements (NCOMP = " + std::to_string(_disc.nComp) + " required)"); + throw InvalidParameterException( + "Field NBOUND contains too few elements (NCOMP = " + std::to_string(_disc.nComp) + " required)"); if (nBound.size() % _disc.nComp != 0) - throw InvalidParameterException("Field NBOUND must have a size divisible by NCOMP (" + std::to_string(_disc.nComp) + ")"); + throw InvalidParameterException("Field NBOUND must have a size divisible by NCOMP (" + + std::to_string(_disc.nComp) + ")"); - if (!newNPartypeInterface && paramProvider.exists("NPARTYPE")) // done here and in this order for backwards compatibility + if (!newNPartypeInterface && + paramProvider.exists("NPARTYPE")) // done here and in this order for backwards compatibility { _disc.nParType = paramProvider.getInt("NPARTYPE"); _disc.nBound = new unsigned int[_disc.nComp * _disc.nParType]; @@ -210,7 +211,7 @@ bool LumpedRateModelWithPores::configureModelDiscretization(IP _disc.parTypeOffset[0] = 0; for (unsigned int j = 1; j < _disc.nParType + 1; ++j) { - _disc.parTypeOffset[j] = _disc.parTypeOffset[j-1] + (_disc.nComp + _disc.strideBound[j-1]) * _disc.nCol; + _disc.parTypeOffset[j] = _disc.parTypeOffset[j - 1] + (_disc.nComp + _disc.strideBound[j - 1]) * _disc.nCol; } // Determine whether analytic Jacobian should be used but don't set it right now. @@ -222,7 +223,9 @@ bool LumpedRateModelWithPores::configureModelDiscretization(IP #endif // Initialize and configure GMRES for solving the Schur-complement - _gmres.initialize(_disc.nCol * _disc.nComp * _disc.nParType, paramProvider.getInt("MAX_KRYLOV"), linalg::toOrthogonalization(paramProvider.getInt("GS_TYPE")), paramProvider.getInt("MAX_RESTARTS")); + _gmres.initialize(_disc.nCol * _disc.nComp * _disc.nParType, paramProvider.getInt("MAX_KRYLOV"), + linalg::toOrthogonalization(paramProvider.getInt("GS_TYPE")), + paramProvider.getInt("MAX_RESTARTS")); _gmres.matrixVectorMultiplier(&schurComplementMultiplierLRMPores, this); _schurSafety = paramProvider.getDouble("SCHUR_SAFETY"); @@ -245,7 +248,8 @@ bool LumpedRateModelWithPores::configureModelDiscretization(IP pg.resize(_disc.nParType, pg[0]); } else if (pg.size() < _disc.nParType) - throw InvalidParameterException("Field PAR_GEOM contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field PAR_GEOM contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); for (unsigned int i = 0; i < _disc.nParType; ++i) { @@ -256,13 +260,15 @@ bool LumpedRateModelWithPores::configureModelDiscretization(IP else if (pg[i] == "SLAB") _parGeomSurfToVol[i] = SurfVolRatioSlab; else - throw InvalidParameterException("Unknown particle geometry type \"" + pg[i] + "\" at index " + std::to_string(i) + " of field PAR_GEOM"); + throw InvalidParameterException("Unknown particle geometry type \"" + pg[i] + "\" at index " + + std::to_string(i) + " of field PAR_GEOM"); } } paramProvider.popScope(); - const bool transportSuccess = _convDispOp.configureModelDiscretization(paramProvider, helper, _disc.nComp, _disc.nCol); + const bool transportSuccess = + _convDispOp.configureModelDiscretization(paramProvider, helper, _disc.nComp, _disc.nCol); if (paramProvider.exists("FILM_DIFFUSION_DEP")) { @@ -285,8 +291,10 @@ bool LumpedRateModelWithPores::configureModelDiscretization(IP _jacPdisc.resize(_disc.nParType); for (unsigned int i = 0; i < _disc.nParType; ++i) { - _jacPdisc[i].resize(_disc.nCol * (_disc.nComp + _disc.strideBound[i]), _disc.nComp + _disc.strideBound[i] - 1, _disc.nComp + _disc.strideBound[i] - 1); - _jacP[i].resize(_disc.nCol * (_disc.nComp + _disc.strideBound[i]), _disc.nComp + _disc.strideBound[i] - 1, _disc.nComp + _disc.strideBound[i] - 1); + _jacPdisc[i].resize(_disc.nCol * (_disc.nComp + _disc.strideBound[i]), _disc.nComp + _disc.strideBound[i] - 1, + _disc.nComp + _disc.strideBound[i] - 1); + _jacP[i].resize(_disc.nCol * (_disc.nComp + _disc.strideBound[i]), _disc.nComp + _disc.strideBound[i] - 1, + _disc.nComp + _disc.strideBound[i] - 1); } _jacPF.resize(_disc.nParType); @@ -307,7 +315,7 @@ bool LumpedRateModelWithPores::configureModelDiscretization(IP clearBindingModels(); _binding = std::vector(_disc.nParType, nullptr); - std::vector bindModelNames = { "NONE" }; + std::vector bindModelNames = {"NONE"}; if (paramProvider.exists("ADSORPTION_MODEL")) bindModelNames = paramProvider.getStringArray("ADSORPTION_MODEL"); @@ -320,7 +328,8 @@ bool LumpedRateModelWithPores::configureModelDiscretization(IP } if (!_singleBinding && (bindModelNames.size() < _disc.nParType)) - throw InvalidParameterException("Field ADSORPTION_MODEL contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field ADSORPTION_MODEL contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); else if (_singleBinding && (bindModelNames.size() != 1)) throw InvalidParameterException("Field ADSORPTION_MODEL requires (only) 1 element"); @@ -338,8 +347,12 @@ bool LumpedRateModelWithPores::configureModelDiscretization(IP if (!_binding[i]) throw InvalidParameterException("Unknown binding model " + bindModelNames[i]); - MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", _singleBinding, i, _disc.nParType == 1, _binding[i]->usesParamProviderInDiscretizationConfig()); - bindingConfSuccess = _binding[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, _disc.boundOffset + i * _disc.nComp) && bindingConfSuccess; + MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", _singleBinding, i, _disc.nParType == 1, + _binding[i]->usesParamProviderInDiscretizationConfig()); + bindingConfSuccess = + _binding[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, + _disc.boundOffset + i * _disc.nComp) && + bindingConfSuccess; } } @@ -357,7 +370,8 @@ bool LumpedRateModelWithPores::configureModelDiscretization(IP if (_dynReactionBulk->usesParamProviderInDiscretizationConfig()) paramProvider.pushScope("reaction_bulk"); - reactionConfSuccess = _dynReactionBulk->configureModelDiscretization(paramProvider, _disc.nComp, nullptr, nullptr); + reactionConfSuccess = + _dynReactionBulk->configureModelDiscretization(paramProvider, _disc.nComp, nullptr, nullptr); if (_dynReactionBulk->usesParamProviderInDiscretizationConfig()) paramProvider.popScope(); @@ -379,7 +393,8 @@ bool LumpedRateModelWithPores::configureModelDiscretization(IP } if (!_singleDynReaction && (dynReactModelNames.size() < _disc.nParType)) - throw InvalidParameterException("Field REACTION_MODEL_PARTICLES contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field REACTION_MODEL_PARTICLES contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); else if (_singleDynReaction && (dynReactModelNames.size() != 1)) throw InvalidParameterException("Field REACTION_MODEL_PARTICLES requires (only) 1 element"); @@ -396,8 +411,13 @@ bool LumpedRateModelWithPores::configureModelDiscretization(IP if (!_dynReaction[i]) throw InvalidParameterException("Unknown dynamic reaction model " + dynReactModelNames[i]); - MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", _singleDynReaction, i, _disc.nParType == 1, _dynReaction[i]->usesParamProviderInDiscretizationConfig()); - reactionConfSuccess = _dynReaction[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, _disc.boundOffset + i * _disc.nComp) && reactionConfSuccess; + MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", _singleDynReaction, i, + _disc.nParType == 1, + _dynReaction[i]->usesParamProviderInDiscretizationConfig()); + reactionConfSuccess = _dynReaction[i]->configureModelDiscretization( + paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, + _disc.boundOffset + i * _disc.nComp) && + reactionConfSuccess; } } } @@ -418,19 +438,25 @@ bool LumpedRateModelWithPores::configure(IParameterProvider& p if (_filmDiffDep) { if (!_filmDiffDep->configure(paramProvider, _unitOpIdx, ParTypeIndep, BoundStateIndep, "FILM_DIFFUSION_DEP")) - throw InvalidParameterException("Failed to configure film diffusion parameter dependency (FILM_DIFFUSION_DEP)"); + throw InvalidParameterException( + "Failed to configure film diffusion parameter dependency (FILM_DIFFUSION_DEP)"); } // Read geometry parameters _colPorosity = paramProvider.getDouble("COL_POROSITY"); - _singleParRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parRadius, "PAR_RADIUS", _disc.nParType, _unitOpIdx); - _singleParPorosity = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parPorosity, "PAR_POROSITY", _disc.nParType, _unitOpIdx); + _singleParRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parRadius, "PAR_RADIUS", + _disc.nParType, _unitOpIdx); + _singleParPorosity = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parPorosity, "PAR_POROSITY", + _disc.nParType, _unitOpIdx); // Read vectorial parameters (which may also be section dependent; transport) - _filmDiffusionMode = readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _filmDiffusion, "FILM_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); + _filmDiffusionMode = readAndRegisterMultiplexCompTypeSecParam( + paramProvider, _parameters, _filmDiffusion, "FILM_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); if (paramProvider.exists("PORE_ACCESSIBILITY")) - _poreAccessFactorMode = readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _poreAccessFactor, "PORE_ACCESSIBILITY", _disc.nParType, _disc.nComp, _unitOpIdx); + _poreAccessFactorMode = + readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _poreAccessFactor, + "PORE_ACCESSIBILITY", _disc.nParType, _disc.nComp, _unitOpIdx); else { _poreAccessFactorMode = MultiplexMode::ComponentType; @@ -452,7 +478,8 @@ bool LumpedRateModelWithPores::configure(IParameterProvider& p // Expand to all axial cells _parTypeVolFrac.resize(_disc.nCol * _disc.nParType, 1.0); for (unsigned int i = 1; i < _disc.nCol; ++i) - std::copy(_parTypeVolFrac.begin(), _parTypeVolFrac.begin() + _disc.nParType, _parTypeVolFrac.begin() + _disc.nParType * i); + std::copy(_parTypeVolFrac.begin(), _parTypeVolFrac.begin() + _disc.nParType, + _parTypeVolFrac.begin() + _disc.nParType * i); } else _axiallyConstantParTypeVolFrac = false; @@ -465,49 +492,76 @@ bool LumpedRateModelWithPores::configure(IParameterProvider& p // Check whether all sizes are matched if (_disc.nParType != _parRadius.size()) - throw InvalidParameterException("Number of elements in field PAR_RADIUS does not match number of particle types"); + throw InvalidParameterException( + "Number of elements in field PAR_RADIUS does not match number of particle types"); if (_disc.nParType * _disc.nCol != _parTypeVolFrac.size()) - throw InvalidParameterException("Number of elements in field PAR_TYPE_VOLFRAC does not match number of particle types"); + throw InvalidParameterException( + "Number of elements in field PAR_TYPE_VOLFRAC does not match number of particle types"); if (_disc.nParType != _parPorosity.size()) - throw InvalidParameterException("Number of elements in field PAR_POROSITY does not match number of particle types"); - - if ((_filmDiffusion.size() < _disc.nComp * _disc.nParType) || (_filmDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) - throw InvalidParameterException("Number of elements in field FILM_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + ")"); + throw InvalidParameterException( + "Number of elements in field PAR_POROSITY does not match number of particle types"); + + if ((_filmDiffusion.size() < _disc.nComp * _disc.nParType) || + (_filmDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) + throw InvalidParameterException( + "Number of elements in field FILM_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + + std::to_string(_disc.nComp * _disc.nParType) + ")"); if (_disc.nComp * _disc.nParType != _poreAccessFactor.size()) - throw InvalidParameterException("Number of elements in field PORE_ACCESSIBILITY differs from NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + ")"); + throw InvalidParameterException( + "Number of elements in field PORE_ACCESSIBILITY differs from NCOMP * NPARTYPE (" + + std::to_string(_disc.nComp * _disc.nParType) + ")"); // Check that particle volume fractions sum to 1.0 for (unsigned int i = 0; i < _disc.nCol; ++i) { - const double volFracSum = std::accumulate(_parTypeVolFrac.begin() + i * _disc.nParType, _parTypeVolFrac.begin() + (i+1) * _disc.nParType, 0.0, + const double volFracSum = std::accumulate( + _parTypeVolFrac.begin() + i * _disc.nParType, _parTypeVolFrac.begin() + (i + 1) * _disc.nParType, 0.0, [](double a, const active& b) -> double { return a + static_cast(b); }); if (std::abs(1.0 - volFracSum) > 1e-10) - throw InvalidParameterException("Sum of field PAR_TYPE_VOLFRAC differs from 1.0 (is " + std::to_string(volFracSum) + ") in axial cell " + std::to_string(i)); + throw InvalidParameterException("Sum of field PAR_TYPE_VOLFRAC differs from 1.0 (is " + + std::to_string(volFracSum) + ") in axial cell " + std::to_string(i)); } // Add parameters to map - _parameters[makeParamId(hashString("COL_POROSITY"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_colPorosity; + _parameters[makeParamId(hashString("COL_POROSITY"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_colPorosity; if (_axiallyConstantParTypeVolFrac) { // Register only the first nParType items for (unsigned int i = 0; i < _disc.nParType; ++i) - _parameters[makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, i, BoundStateIndep, ReactionIndep, SectionIndep)] = &_parTypeVolFrac[i]; + _parameters[makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, i, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_parTypeVolFrac[i]; } else - registerParam2DArray(_parameters, _parTypeVolFrac, [=](bool multi, unsigned cell, unsigned int type) { return makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, type, BoundStateIndep, ReactionIndep, cell); }, _disc.nParType); + registerParam2DArray( + _parameters, _parTypeVolFrac, + [=](bool multi, unsigned cell, unsigned int type) { + return makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, type, BoundStateIndep, + ReactionIndep, cell); + }, + _disc.nParType); // Register initial conditions parameters - registerParam1DArray(_parameters, _initC, [=](bool multi, unsigned int comp) { return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep); }); + registerParam1DArray(_parameters, _initC, [=](bool multi, unsigned int comp) { + return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep); + }); if (_singleBinding) { for (unsigned int c = 0; c < _disc.nComp; ++c) - _parameters[makeParamId(hashString("INIT_CP"), _unitOpIdx, c, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_initCp[c]; + _parameters[makeParamId(hashString("INIT_CP"), _unitOpIdx, c, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep)] = &_initCp[c]; } else - registerParam2DArray(_parameters, _initCp, [=](bool multi, unsigned int type, unsigned int comp) { return makeParamId(hashString("INIT_CP"), _unitOpIdx, comp, type, BoundStateIndep, ReactionIndep, SectionIndep); }, _disc.nComp); - + registerParam2DArray( + _parameters, _initCp, + [=](bool multi, unsigned int type, unsigned int comp) { + return makeParamId(hashString("INIT_CP"), _unitOpIdx, comp, type, BoundStateIndep, ReactionIndep, + SectionIndep); + }, + _disc.nComp); if (!_binding.empty()) { @@ -550,10 +604,10 @@ bool LumpedRateModelWithPores::configure(IParameterProvider& p { for (unsigned int type = 0; type < _disc.nParType; ++type) { - if (!_binding[type] || !_binding[type]->requiresConfiguration()) - continue; + if (!_binding[type] || !_binding[type]->requiresConfiguration()) + continue; - // Check whether required = true and no isActive() check should be performed + // Check whether required = true and no isActive() check should be performed MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", type, _disc.nParType == 1, false); if (!scopeGuard.isActive()) continue; @@ -577,18 +631,20 @@ bool LumpedRateModelWithPores::configure(IParameterProvider& p if (_dynReaction[0] && _dynReaction[0]->requiresConfiguration()) { MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", true); - dynReactionConfSuccess = _dynReaction[0]->configure(paramProvider, _unitOpIdx, ParTypeIndep) && dynReactionConfSuccess; + dynReactionConfSuccess = + _dynReaction[0]->configure(paramProvider, _unitOpIdx, ParTypeIndep) && dynReactionConfSuccess; } } else { for (unsigned int type = 0; type < _disc.nParType; ++type) { - if (!_dynReaction[type] || !_dynReaction[type]->requiresConfiguration()) - continue; + if (!_dynReaction[type] || !_dynReaction[type]->requiresConfiguration()) + continue; MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", type, _disc.nParType == 1, true); - dynReactionConfSuccess = _dynReaction[type]->configure(paramProvider, _unitOpIdx, type) && dynReactionConfSuccess; + dynReactionConfSuccess = + _dynReaction[type]->configure(paramProvider, _unitOpIdx, type) && dynReactionConfSuccess; } } @@ -607,7 +663,8 @@ unsigned int LumpedRateModelWithPores::threadLocalMemorySize() lms.fitBlock(_binding[i]->workspaceSize(_disc.nComp, _disc.strideBound[i], _disc.nBound + i * _disc.nComp)); if (_dynReaction[i] && _dynReaction[i]->requiresWorkspace()) - lms.fitBlock(_dynReaction[i]->workspaceSize(_disc.nComp, _disc.strideBound[i], _disc.nBound + i * _disc.nComp)); + lms.fitBlock( + _dynReaction[i]->workspaceSize(_disc.nComp, _disc.strideBound[i], _disc.nBound + i * _disc.nComp)); } if (_dynReactionBulk && _dynReactionBulk->requiresWorkspace()) @@ -673,7 +730,8 @@ void LumpedRateModelWithPores::useAnalyticJacobian(const bool } template -void LumpedRateModelWithPores::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) +void LumpedRateModelWithPores::notifyDiscontinuousSectionTransition( + double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) { // Setup flux Jacobian blocks at the beginning of the simulation or in case of // section dependent film or particle diffusion coefficients @@ -716,7 +774,8 @@ void LumpedRateModelWithPores::setFlowRates(active const* in, } template -void LumpedRateModelWithPores::reportSolution(ISolutionRecorder& recorder, double const* const solution) const +void LumpedRateModelWithPores::reportSolution(ISolutionRecorder& recorder, + double const* const solution) const { Exporter expr(_disc, *this, solution); recorder.beginUnitOperation(_unitOpIdx, *this, expr); @@ -730,7 +789,6 @@ void LumpedRateModelWithPores::reportSolutionStructure(ISoluti recorder.unitOperationStructure(_unitOpIdx, *this, expr); } - template unsigned int LumpedRateModelWithPores::requiredADdirs() const CADET_NOEXCEPT { @@ -761,7 +819,9 @@ void LumpedRateModelWithPores::prepareADvectors(const AdJacobi const unsigned int lowerParBandwidth = _jacP[type].lowerBandwidth(); const unsigned int upperParBandwidth = _jacP[type].upperBandwidth(); - ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + idxr.offsetCp(ParticleTypeIndex{type}), adJac.adDirOffset, idxr.strideParBlock(type) * _disc.nCol, lowerParBandwidth, upperParBandwidth, lowerParBandwidth); + ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + idxr.offsetCp(ParticleTypeIndex{type}), adJac.adDirOffset, + idxr.strideParBlock(type) * _disc.nCol, lowerParBandwidth, + upperParBandwidth, lowerParBandwidth); } } @@ -771,7 +831,8 @@ void LumpedRateModelWithPores::prepareADvectors(const AdJacobi * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) */ template -void LumpedRateModelWithPores::extractJacobianFromAD(active const* const adRes, unsigned int adDirOffset) +void LumpedRateModelWithPores::extractJacobianFromAD(active const* const adRes, + unsigned int adDirOffset) { Indexer idxr(_disc); @@ -782,7 +843,8 @@ void LumpedRateModelWithPores::extractJacobianFromAD(active co for (unsigned int type = 0; type < _disc.nParType; ++type) { linalg::BandMatrix& jacMat = _jacP[type]; - ad::extractBandedJacobianFromAd(adRes + idxr.offsetCp(ParticleTypeIndex{type}), adDirOffset, jacMat.lowerBandwidth(), jacMat); + ad::extractBandedJacobianFromAd(adRes + idxr.offsetCp(ParticleTypeIndex{type}), adDirOffset, + jacMat.lowerBandwidth(), jacMat); } } @@ -795,7 +857,8 @@ void LumpedRateModelWithPores::extractJacobianFromAD(active co * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) */ template -void LumpedRateModelWithPores::checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const +void LumpedRateModelWithPores::checkAnalyticJacobianAgainstAd(active const* const adRes, + unsigned int adDirOffset) const { Indexer idxr(_disc); @@ -809,7 +872,8 @@ void LumpedRateModelWithPores::checkAnalyticJacobianAgainstAd( for (unsigned int type = 0; type < _disc.nParType; ++type) { const linalg::BandMatrix& jacMat = _jacP[type]; - const double localDiff = ad::compareBandedJacobianWithAd(adRes + idxr.offsetCp(ParticleTypeIndex{type}), adDirOffset, jacMat.lowerBandwidth(), jacMat); + const double localDiff = ad::compareBandedJacobianWithAd(adRes + idxr.offsetCp(ParticleTypeIndex{type}), + adDirOffset, jacMat.lowerBandwidth(), jacMat); LOG(Debug) << "-> Par type " << type << " diff: " << localDiff; maxDiffPar = std::max(maxDiffPar, localDiff); } @@ -818,29 +882,40 @@ void LumpedRateModelWithPores::checkAnalyticJacobianAgainstAd( #endif template -int LumpedRateModelWithPores::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPores::jacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); _factorizeJacobian = true; if (_analyticJac) - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); else - return residualWithJacobian(simTime, ConstSimulationState{ simState.vecStateY, nullptr }, nullptr, adJac, threadLocalMem); + return residualWithJacobian(simTime, ConstSimulationState{simState.vecStateY, nullptr}, nullptr, adJac, + threadLocalMem); } template -int LumpedRateModelWithPores::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPores::residual(const SimulationTime& simTime, + const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); // Evaluate residual do not compute Jacobian or parameter sensitivities - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } template -int LumpedRateModelWithPores::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPores::residualWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); @@ -849,8 +924,11 @@ int LumpedRateModelWithPores::residualWithJacobian(const Simul } template -int LumpedRateModelWithPores::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, - const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity) +int LumpedRateModelWithPores::residual(const SimulationTime& simTime, + const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, + bool paramSensitivity) { if (updateJacobian) { @@ -861,7 +939,8 @@ int LumpedRateModelWithPores::residual(const SimulationTime& s { if (paramSensitivity) { - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -870,7 +949,8 @@ int LumpedRateModelWithPores::residual(const SimulationTime& s return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } else { @@ -885,9 +965,11 @@ int LumpedRateModelWithPores::residual(const SimulationTime& s // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -910,15 +992,18 @@ int LumpedRateModelWithPores::residual(const SimulationTime& s // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); // Only do comparison if we have a residuals vector (which is not always the case) if (res) { // Evaluate with analytical Jacobian which is stored in the band matrices - retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); // Compare AD with anaytic Jacobian checkAnalyticJacobianAgainstAd(adJac.adRes, adJac.adDirOffset); @@ -938,7 +1023,8 @@ int LumpedRateModelWithPores::residual(const SimulationTime& s // @todo Check if this is necessary ad::resetAd(adJac.adRes, numDofs()); - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -947,13 +1033,16 @@ int LumpedRateModelWithPores::residual(const SimulationTime& s return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } } template template -int LumpedRateModelWithPores::residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPores::residualImpl(double t, unsigned int secIdx, StateType const* const y, + double const* const yDot, ResidualType* const res, + util::ThreadLocalStorage& threadLocalMem) { // Reset Jacobian if (wantJac) @@ -976,7 +1065,8 @@ int LumpedRateModelWithPores::residualImpl(double t, unsigned { const unsigned int type = (pblk - 1) / _disc.nCol; const unsigned int par = (pblk - 1) % _disc.nCol; - residualParticle(t, type, par, secIdx, y, yDot, res, threadLocalMem); + residualParticle(t, type, par, secIdx, y, yDot, res, + threadLocalMem); } } CADET_PARFOR_END; @@ -998,10 +1088,13 @@ int LumpedRateModelWithPores::residualImpl(double t, unsigned template template -int LumpedRateModelWithPores::residualBulk(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPores::residualBulk(double t, unsigned int secIdx, StateType const* yBase, + double const* yDotBase, ResidualType* resBase, + util::ThreadLocalStorage& threadLocalMem) { if (wantRes) - _convDispOp.residual(*this, t, secIdx, yBase, yDotBase, resBase, wantJac, typename ParamSens::enabled()); + _convDispOp.residual(*this, t, secIdx, yBase, yDotBase, resBase, wantJac, + typename ParamSens::enabled()); else _convDispOp.jacobian(*this, t, secIdx, yBase, nullptr, nullptr); @@ -1023,7 +1116,9 @@ int LumpedRateModelWithPores::residualBulk(double t, unsigned if (wantJac) { // static_cast should be sufficient here, but this statement is also analyzed when wantJac = false - _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(y), -1.0, _convDispOp.jacobian().row(col * idxr.strideColCell()), tlmAlloc); + _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(y), -1.0, + _convDispOp.jacobian().row(col * idxr.strideColCell()), + tlmAlloc); } } @@ -1032,13 +1127,17 @@ int LumpedRateModelWithPores::residualBulk(double t, unsigned template template -int LumpedRateModelWithPores::residualParticle(double t, unsigned int parType, unsigned int colCell, unsigned int secIdx, StateType const* yBase, double const* yDotBase, ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPores::residualParticle(double t, unsigned int parType, unsigned int colCell, + unsigned int secIdx, StateType const* yBase, + double const* yDotBase, ResidualType* resBase, + util::ThreadLocalStorage& threadLocalMem) { Indexer idxr(_disc); // Go to the particle block of the given type and column cell StateType const* y = yBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colCell}); - double const* yDot = yDotBase ? yDotBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colCell}) : nullptr; + double const* yDot = + yDotBase ? yDotBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colCell}) : nullptr; ResidualType* res = wantRes ? resBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colCell}) : nullptr; // Prepare parameters @@ -1047,37 +1146,37 @@ int LumpedRateModelWithPores::residualParticle(double t, unsig // Midpoint of current column cell (z coordinate) - needed in externally dependent adsorption kinetic const double z = _convDispOp.relativeCoordinate(colCell); - const parts::cell::CellParameters cellResParams - { - _disc.nComp, - _disc.nBound + _disc.nComp * parType, - _disc.boundOffset + _disc.nComp * parType, - _disc.strideBound[parType], - _binding[parType]->reactionQuasiStationarity(), - _parPorosity[parType], - _poreAccessFactor.data() + _disc.nComp * parType, - _binding[parType], - (_dynReaction[parType] && (_dynReaction[parType]->numReactionsCombined() > 0)) ? _dynReaction[parType] : nullptr - }; + const parts::cell::CellParameters cellResParams{ + _disc.nComp, + _disc.nBound + _disc.nComp * parType, + _disc.boundOffset + _disc.nComp * parType, + _disc.strideBound[parType], + _binding[parType]->reactionQuasiStationarity(), + _parPorosity[parType], + _poreAccessFactor.data() + _disc.nComp * parType, + _binding[parType], + (_dynReaction[parType] && (_dynReaction[parType]->numReactionsCombined() > 0)) ? _dynReaction[parType] + : nullptr}; // Handle time derivatives, binding, dynamic reactions if (wantRes) - parts::cell::residualKernel( - t, secIdx, ColumnPosition{ z, 0.0, static_cast(radius) * 0.5 }, y, yDot, res, - _jacP[parType].row(colCell * idxr.strideParBlock(parType)), cellResParams, threadLocalMem.get() - ); + parts::cell::residualKernel( + t, secIdx, ColumnPosition{z, 0.0, static_cast(radius) * 0.5}, y, yDot, res, + _jacP[parType].row(colCell * idxr.strideParBlock(parType)), cellResParams, threadLocalMem.get()); else - parts::cell::residualKernel( - t, secIdx, ColumnPosition{ z, 0.0, static_cast(radius) * 0.5 }, y, yDot, res, - _jacP[parType].row(colCell * idxr.strideParBlock(parType)), cellResParams, threadLocalMem.get() - ); + parts::cell::residualKernel( + t, secIdx, ColumnPosition{z, 0.0, static_cast(radius) * 0.5}, y, yDot, res, + _jacP[parType].row(colCell * idxr.strideParBlock(parType)), cellResParams, threadLocalMem.get()); return 0; } template template -int LumpedRateModelWithPores::residualFlux(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, ResidualType* resBase) +int LumpedRateModelWithPores::residualFlux(double t, unsigned int secIdx, StateType const* yBase, + double const* yDotBase, ResidualType* resBase) { Indexer idxr(_disc); @@ -1104,7 +1203,8 @@ int LumpedRateModelWithPores::residualFlux(double t, unsigned const ParamType epsP = static_cast(_parPorosity[type]); const ParamType radius = static_cast(_parRadius[type]); - active const* const filmDiff = getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const filmDiff = + getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; active const* const poreAccFactor = _poreAccessFactor.data() + type * _disc.nComp; const ParamType jacCF_val = invBetaC * _parGeomSurfToVol[type] / radius; @@ -1118,9 +1218,11 @@ int LumpedRateModelWithPores::residualFlux(double t, unsigned const double relPos = _convDispOp.relativeCoordinate(colCell); const active curVelocity = _convDispOp.currentVelocity(relPos); - const active modifier = _filmDiffDep->getValue(*this, ColumnPosition{relPos, 0.0, 0.0}, comp, ParTypeIndep, BoundStateIndep, curVelocity); + const active modifier = _filmDiffDep->getValue(*this, ColumnPosition{relPos, 0.0, 0.0}, comp, ParTypeIndep, + BoundStateIndep, curVelocity); - resCol[i] += jacCF_val * static_cast(filmDiff[comp]) * static_cast(modifier) * static_cast(_parTypeVolFrac[type + _disc.nParType * colCell]) * yFluxType[i]; + resCol[i] += jacCF_val * static_cast(filmDiff[comp]) * static_cast(modifier) * + static_cast(_parTypeVolFrac[type + _disc.nParType * colCell]) * yFluxType[i]; } // J_{f,0} block, adds bulk volume state c_i to flux equation @@ -1141,10 +1243,13 @@ int LumpedRateModelWithPores::residualFlux(double t, unsigned for (unsigned int comp = 0; comp < _disc.nComp; ++comp) { - const active modifier = _filmDiffDep->getValue(*this, ColumnPosition{relPos, 0.0, 0.0}, comp, ParTypeIndep, BoundStateIndep, curVelocity); + const active modifier = _filmDiffDep->getValue(*this, ColumnPosition{relPos, 0.0, 0.0}, comp, + ParTypeIndep, BoundStateIndep, curVelocity); const unsigned int eq = pblk * idxr.strideColCell() + comp * idxr.strideColComp(); - resParType[pblk * idxr.strideParBlock(type) + comp] += jacPF_val / static_cast(poreAccFactor[comp]) * static_cast(filmDiff[comp]) * static_cast(modifier) * yFluxType[eq]; + resParType[pblk * idxr.strideParBlock(type) + comp] += + jacPF_val / static_cast(poreAccFactor[comp]) * static_cast(filmDiff[comp]) * + static_cast(modifier) * yFluxType[eq]; } } @@ -1194,7 +1299,8 @@ void LumpedRateModelWithPores::assembleOffdiagJac(double t, un const double jacCF_val = invBetaC * _parGeomSurfToVol[type] / radius; const double jacPF_val = -_parGeomSurfToVol[type] / (radius * epsP); - active const* const filmDiff = getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const filmDiff = + getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; active const* const poreAccFactor = _poreAccessFactor.data() + type * _disc.nComp; // Note that the J_f block, which is the identity matrix, is treated in the linear solver @@ -1207,10 +1313,13 @@ void LumpedRateModelWithPores::assembleOffdiagJac(double t, un const double relPos = _convDispOp.relativeCoordinate(colCell); const double curVelocity = static_cast(_convDispOp.currentVelocity(relPos)); - const double modifier = _filmDiffDep->getValue(*this, ColumnPosition{relPos, 0.0, 0.0}, comp, ParTypeIndep, BoundStateIndep, curVelocity); + const double modifier = _filmDiffDep->getValue(*this, ColumnPosition{relPos, 0.0, 0.0}, comp, ParTypeIndep, + BoundStateIndep, curVelocity); // Main diagonal corresponds to j_{f,i} (flux) state variable - _jacCF.addElement(eq, eq + typeOffset, jacCF_val * static_cast(filmDiff[comp]) * modifier * static_cast(_parTypeVolFrac[type + _disc.nParType * colCell])); + _jacCF.addElement(eq, eq + typeOffset, + jacCF_val * static_cast(filmDiff[comp]) * modifier * + static_cast(_parTypeVolFrac[type + _disc.nParType * colCell])); } // J_{f,0} block, adds bulk volume state c_i to flux equation @@ -1230,8 +1339,11 @@ void LumpedRateModelWithPores::assembleOffdiagJac(double t, un const unsigned int eq = typeOffset + pblk * idxr.strideColCell() + comp * idxr.strideColComp(); const unsigned int col = pblk * idxr.strideParBlock(type) + comp; - const double modifier = _filmDiffDep->getValue(*this, ColumnPosition{relPos, 0.0, 0.0}, comp, ParTypeIndep, BoundStateIndep, curVelocity); - _jacPF[type].addElement(col, eq, jacPF_val / static_cast(poreAccFactor[comp]) * static_cast(filmDiff[comp]) * modifier); + const double modifier = _filmDiffDep->getValue(*this, ColumnPosition{relPos, 0.0, 0.0}, comp, + ParTypeIndep, BoundStateIndep, curVelocity); + _jacPF[type].addElement(col, eq, + jacPF_val / static_cast(poreAccFactor[comp]) * + static_cast(filmDiff[comp]) * modifier); } } @@ -1249,8 +1361,10 @@ void LumpedRateModelWithPores::assembleOffdiagJac(double t, un } template -int LumpedRateModelWithPores::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, - const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPores::residualSensFwdWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidualSens); @@ -1260,18 +1374,23 @@ int LumpedRateModelWithPores::residualSensFwdWithJacobian(cons } template -int LumpedRateModelWithPores::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPores::residualSensFwdAdOnly(const SimulationTime& simTime, + const ConstSimulationState& simState, + active* const adRes, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidualSens); // Evaluate residual for all parameters using AD in vector mode - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adRes, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, adRes, threadLocalMem); } template -int LumpedRateModelWithPores::residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3) +int LumpedRateModelWithPores::residualSensFwdCombine( + const SimulationTime& simTime, const ConstSimulationState& simState, const std::vector& yS, + const std::vector& ySdot, const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3) { BENCH_SCOPE(_timerResidualSens); @@ -1281,10 +1400,12 @@ int LumpedRateModelWithPores::residualSensFwdCombine(const Sim for (std::size_t param = 0; param < yS.size(); ++param) { // Directional derivative (dF / dy) * s - multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, yS[param], 1.0, 0.0, tmp1); + multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, yS[param], 1.0, 0.0, + tmp1); // Directional derivative (dF / dyDot) * sDot - multiplyWithDerivativeJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, ySdot[param], tmp2); + multiplyWithDerivativeJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, ySdot[param], + tmp2); double* const ptrResS = resS[param]; @@ -1308,11 +1429,12 @@ int LumpedRateModelWithPores::residualSensFwdCombine(const Sim } /** - * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, \dot{y}\right) @f$) + * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, + * \dot{y}\right) @f$) * @details Actually, the operation @f$ z = \alpha \frac{\partial F}{\partial y} x + \beta z @f$ is performed. * - * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ once - * before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. + * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ + * once before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. * @param [in] simTime Current simulation time point * @param [in] simState Simulation state vectors * @param [in] yS Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial y} @f$ @@ -1321,7 +1443,10 @@ int LumpedRateModelWithPores::residualSensFwdCombine(const Sim * @param [in,out] ret Vector @f$ z @f$ which stores the result of the operation */ template -void LumpedRateModelWithPores::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret) +void LumpedRateModelWithPores::multiplyWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + double const* yS, double alpha, double beta, + double* ret) { Indexer idxr(_disc); @@ -1372,7 +1497,8 @@ void LumpedRateModelWithPores::multiplyWithJacobian(const Simu } /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ + * with a given vector * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). * @param [in] simTime Current simulation time point @@ -1381,7 +1507,9 @@ void LumpedRateModelWithPores::multiplyWithJacobian(const Simu * @param [out] ret Vector @f$ z @f$ which stores the result of the operation */ template -void LumpedRateModelWithPores::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret) +void LumpedRateModelWithPores::multiplyWithDerivativeJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + double const* sDot, double* ret) { Indexer idxr(_disc); @@ -1414,7 +1542,9 @@ void LumpedRateModelWithPores::multiplyWithDerivativeJacobian( // Add derivative with respect to dc_p / dt to Jacobian localRet[comp] = localSdot[comp]; - const double invBetaP = (1.0 - static_cast(_parPorosity[type])) / (static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * static_cast(_parPorosity[type])); + const double invBetaP = (1.0 - static_cast(_parPorosity[type])) / + (static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * + static_cast(_parPorosity[type])); // Add derivative with respect to dq / dt to Jacobian (normal equations) for (unsigned int i = 0; i < nBound[comp]; ++i) @@ -1462,7 +1592,8 @@ void LumpedRateModelWithPores::setExternalFunctions(IExternalF } template -unsigned int LumpedRateModelWithPores::localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT +unsigned int LumpedRateModelWithPores::localOutletComponentIndex(unsigned int port) const + CADET_NOEXCEPT { // Inlets are duplicated so need to be accounted for if (_convDispOp.forwardFlow()) @@ -1474,25 +1605,29 @@ unsigned int LumpedRateModelWithPores::localOutletComponentInd } template -unsigned int LumpedRateModelWithPores::localInletComponentIndex(unsigned int port) const CADET_NOEXCEPT +unsigned int LumpedRateModelWithPores::localInletComponentIndex(unsigned int port) const + CADET_NOEXCEPT { return 0; } template -unsigned int LumpedRateModelWithPores::localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT +unsigned int LumpedRateModelWithPores::localOutletComponentStride(unsigned int port) const + CADET_NOEXCEPT { return 1; } template -unsigned int LumpedRateModelWithPores::localInletComponentStride(unsigned int port) const CADET_NOEXCEPT +unsigned int LumpedRateModelWithPores::localInletComponentStride(unsigned int port) const + CADET_NOEXCEPT { return 1; } template -void LumpedRateModelWithPores::expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut) +void LumpedRateModelWithPores::expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, + double* expandOut) { // @todo Write this function } @@ -1505,7 +1640,8 @@ bool LumpedRateModelWithPores::setParameter(const ParameterId& // Intercept changes to PAR_TYPE_VOLFRAC when not specified per axial cell (but once globally) if (_axiallyConstantParTypeVolFrac && (pId.name == hashString("PAR_TYPE_VOLFRAC"))) { - if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep)) + if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep)) return false; if (pId.particleType >= _disc.nParType) return false; @@ -1518,12 +1654,15 @@ bool LumpedRateModelWithPores::setParameter(const ParameterId& if (multiplexTypeParameterValue(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, value, nullptr)) return true; - if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, nullptr)) + if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, + nullptr)) return true; - if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, _disc.nParType, _disc.nComp, value, nullptr)) + if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, + _disc.nParType, _disc.nComp, value, nullptr)) return true; - if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, _poreAccessFactor, _disc.nParType, _disc.nComp, value, nullptr)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, + _poreAccessFactor, _disc.nParType, _disc.nComp, value, nullptr)) return true; const int mpIc = multiplexInitialConditions(pId, value, false); @@ -1556,7 +1695,8 @@ void LumpedRateModelWithPores::setSensitiveParameterValue(cons // Intercept changes to PAR_TYPE_VOLFRAC when not specified per axial cell (but once globally) if (_axiallyConstantParTypeVolFrac && (pId.name == hashString("PAR_TYPE_VOLFRAC"))) { - if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep)) + if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep)) return; if (pId.particleType >= _disc.nParType) return; @@ -1570,14 +1710,18 @@ void LumpedRateModelWithPores::setSensitiveParameterValue(cons return; } - if (multiplexTypeParameterValue(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, value, &_sensParams)) + if (multiplexTypeParameterValue(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, value, + &_sensParams)) return; - if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, &_sensParams)) + if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, + &_sensParams)) return; - if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, _disc.nParType, _disc.nComp, value, &_sensParams)) + if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, + _disc.nParType, _disc.nComp, value, &_sensParams)) return; - if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, _poreAccessFactor, _disc.nParType, _disc.nComp, value, &_sensParams)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, + _poreAccessFactor, _disc.nParType, _disc.nComp, value, &_sensParams)) return; if (multiplexInitialConditions(pId, value, true) != 0) return; @@ -1600,14 +1744,16 @@ void LumpedRateModelWithPores::setSensitiveParameterValue(cons } template -bool LumpedRateModelWithPores::setSensitiveParameter(const ParameterId& pId, unsigned int adDirection, double adValue) +bool LumpedRateModelWithPores::setSensitiveParameter(const ParameterId& pId, unsigned int adDirection, + double adValue) { if (pId.unitOperation == _unitOpIdx) { // Intercept changes to PAR_TYPE_VOLFRAC when not specified per axial cell (but once globally) if (_axiallyConstantParTypeVolFrac && (pId.name == hashString("PAR_TYPE_VOLFRAC"))) { - if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep)) + if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep)) return false; if (pId.particleType >= _disc.nParType) return false; @@ -1622,25 +1768,30 @@ bool LumpedRateModelWithPores::setSensitiveParameter(const Par return true; } - if (multiplexTypeParameterAD(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, adDirection, adValue, _sensParams)) + if (multiplexTypeParameterAD(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, adDirection, adValue, + _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexTypeParameterAD(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, adDirection, adValue, _sensParams)) + if (multiplexTypeParameterAD(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, adDirection, + adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexCompTypeSecParameterAD(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) + if (multiplexCompTypeSecParameterAD(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, + _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexCompTypeSecParameterAD(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, _poreAccessFactor, _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) + if (multiplexCompTypeSecParameterAD(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, + _poreAccessFactor, _disc.nParType, _disc.nComp, adDirection, adValue, + _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; @@ -1676,7 +1827,6 @@ bool LumpedRateModelWithPores::setSensitiveParameter(const Par return UnitOperationBase::setSensitiveParameter(pId, adDirection, adValue); } - template int LumpedRateModelWithPores::Exporter::writeMobilePhase(double* buffer) const { @@ -1728,7 +1878,8 @@ int LumpedRateModelWithPores::Exporter::writeSolidPhase(unsign } template -int LumpedRateModelWithPores::Exporter::writeParticleMobilePhase(unsigned int parType, double* buffer) const +int LumpedRateModelWithPores::Exporter::writeParticleMobilePhase(unsigned int parType, + double* buffer) const { cadet_assert(parType < _disc.nParType); @@ -1798,9 +1949,9 @@ int LumpedRateModelWithPores::Exporter::writeOutlet(double* bu return _disc.nComp; } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet #include "model/LumpedRateModelWithPores-InitialConditions.cpp" #include "model/LumpedRateModelWithPores-LinearSolver.cpp" @@ -1829,6 +1980,6 @@ IUnitOperation* createRadialFVLRMP(UnitOpIdx uoId) return new RadialLRMP(uoId); } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/LumpedRateModelWithPores.hpp b/src/libcadet/model/LumpedRateModelWithPores.hpp index fcb14bd60..2c60ea81a 100644 --- a/src/libcadet/model/LumpedRateModelWithPores.hpp +++ b/src/libcadet/model/LumpedRateModelWithPores.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the lumped rate model with pores (LRMP). */ @@ -37,21 +37,26 @@ namespace { - template - struct LumpedRateModelWithPoresName { }; +template struct LumpedRateModelWithPoresName +{ +}; - template <> - struct LumpedRateModelWithPoresName +template <> struct LumpedRateModelWithPoresName +{ + static const char* identifier() CADET_NOEXCEPT { - static const char* identifier() CADET_NOEXCEPT { return "LUMPED_RATE_MODEL_WITH_PORES"; } - }; + return "LUMPED_RATE_MODEL_WITH_PORES"; + } +}; - template <> - struct LumpedRateModelWithPoresName +template <> struct LumpedRateModelWithPoresName +{ + static const char* identifier() CADET_NOEXCEPT { - static const char* identifier() CADET_NOEXCEPT { return "RADIAL_LUMPED_RATE_MODEL_WITH_PORES"; } - }; -} + return "RADIAL_LUMPED_RATE_MODEL_WITH_PORES"; + } +}; +} // namespace namespace cadet { @@ -64,12 +69,13 @@ class IDynamicReactionModel; /** * @brief Lumped rate model of liquid column chromatography with pores * @details See @cite Guiochon2006, @cite Gu1995, @cite Felinger2004 - * + * * @f[\begin{align} - \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 c_i}{\partial z^2} - \frac{1 - \varepsilon_c}{\varepsilon_c} \frac{3 k_{f,i}}{r_p} j_{f,i} \\ - \frac{\partial c_{p,i}}{\partial t} + \frac{1 - \varepsilon_p}{\varepsilon_p} \frac{\partial q_{i}}{\partial t} &= \frac{3 k_{f,i}}{\varepsilon_p r_p} j_{f,i} \\ - a \frac{\partial q_i}{\partial t} &= f_{\text{iso}}(c_p, q) -\end{align} @f] + \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 +c_i}{\partial z^2} - \frac{1 - \varepsilon_c}{\varepsilon_c} \frac{3 k_{f,i}}{r_p} j_{f,i} \\ + \frac{\partial c_{p,i}}{\partial t} + \frac{1 - \varepsilon_p}{\varepsilon_p} \frac{\partial q_{i}}{\partial t} &= +\frac{3 k_{f,i}}{\varepsilon_p r_p} j_{f,i} \\ a \frac{\partial q_i}{\partial t} &= f_{\text{iso}}(c_p, q) \end{align} +@f] @f[ \begin{align} j_{f,i} = c_i - c_{p,i} \end{align} @f] @@ -78,13 +84,12 @@ class IDynamicReactionModel; u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partial z}(t,0) \\ \frac{\partial c_i}{\partial z}(t,L) &= 0 \end{align} @f] - * Methods are described in @cite VonLieres2010a (WENO, linear solver), @cite Puttmann2013 @cite Puttmann2016 (forward sensitivities, AD, band compression) + * Methods are described in @cite VonLieres2010a (WENO, linear solver), @cite Puttmann2013 @cite Puttmann2016 (forward +sensitivities, AD, band compression) */ -template -class LumpedRateModelWithPores : public UnitOperationBase +template class LumpedRateModelWithPores : public UnitOperationBase { public: - LumpedRateModelWithPores(UnitOpIdx unitOpIdx); virtual ~LumpedRateModelWithPores() CADET_NOEXCEPT; @@ -93,59 +98,104 @@ class LumpedRateModelWithPores : public UnitOperationBase virtual bool usesAD() const CADET_NOEXCEPT; virtual unsigned int requiredADdirs() const CADET_NOEXCEPT; - virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT { return _unitOpIdx; } - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } + virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT + { + return _unitOpIdx; + } + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } virtual void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT; - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual bool canAccumulate() const CADET_NOEXCEPT { return false; } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual bool canAccumulate() const CADET_NOEXCEPT + { + return false; + } - static const char* identifier() CADET_NOEXCEPT { return LumpedRateModelWithPoresName::identifier(); } - virtual const char* unitOperationName() const CADET_NOEXCEPT { return identifier(); } + static const char* identifier() CADET_NOEXCEPT + { + return LumpedRateModelWithPoresName::identifier(); + } + virtual const char* unitOperationName() const CADET_NOEXCEPT + { + return identifier(); + } virtual bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper); virtual bool configure(IParameterProvider& paramProvider); - virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac); + virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac); virtual void useAnalyticJacobian(const bool analyticJac); virtual void reportSolution(ISolutionRecorder& recorder, double const* const solution) const; virtual void reportSolutionStructure(ISolutionRecorder& recorder) const; - virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem); + virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem); - virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3); + virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3); virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState); + const ConstSimulationState& simState); virtual void prepareADvectors(const AdJacobianParams& adJac) const; virtual void applyInitialCondition(const SimulationState& simState) const; virtual void readInitialCondition(IParameterProvider& paramProvider); - virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); virtual void initializeSensitivityStates(const std::vector& vecSensY) const; virtual void consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem); virtual void leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual bool hasInlet() const CADET_NOEXCEPT { return true; } - virtual bool hasOutlet() const CADET_NOEXCEPT { return true; } + virtual bool hasInlet() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasOutlet() const CADET_NOEXCEPT + { + return true; + } virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT; virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT; @@ -153,14 +203,19 @@ class LumpedRateModelWithPores : public UnitOperationBase virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT; virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size); - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { } + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) + { + } virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut); - virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret); - virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret); + virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret); + virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* sDot, double* ret); - inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double* ret) + inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double* ret) { multiplyWithJacobian(simTime, simState, yS, 1.0, 0.0, ret); } @@ -174,58 +229,43 @@ class LumpedRateModelWithPores : public UnitOperationBase #ifdef CADET_BENCHMARK_MODE virtual std::vector benchmarkTimings() const { - return std::vector({ - static_cast(numDofs()), - _timerResidual.totalElapsedTime(), - _timerResidualPar.totalElapsedTime(), - _timerResidualSens.totalElapsedTime(), - _timerResidualSensPar.totalElapsedTime(), - _timerJacobianPar.totalElapsedTime(), - _timerConsistentInit.totalElapsedTime(), - _timerConsistentInitPar.totalElapsedTime(), - _timerLinearSolve.totalElapsedTime(), - _timerFactorize.totalElapsedTime(), - _timerFactorizePar.totalElapsedTime(), - _timerMatVec.totalElapsedTime(), - _timerGmres.totalElapsedTime() - }); + return std::vector({static_cast(numDofs()), _timerResidual.totalElapsedTime(), + _timerResidualPar.totalElapsedTime(), _timerResidualSens.totalElapsedTime(), + _timerResidualSensPar.totalElapsedTime(), _timerJacobianPar.totalElapsedTime(), + _timerConsistentInit.totalElapsedTime(), _timerConsistentInitPar.totalElapsedTime(), + _timerLinearSolve.totalElapsedTime(), _timerFactorize.totalElapsedTime(), + _timerFactorizePar.totalElapsedTime(), _timerMatVec.totalElapsedTime(), + _timerGmres.totalElapsedTime()}); } virtual char const* const* benchmarkDescriptions() const { static const char* const desc[] = { - "DOFs", - "Residual", - "ResidualPar", - "ResidualSens", - "ResidualSensPar", - "JacobianPar", - "ConsistentInit", - "ConsistentInitPar", - "LinearSolve", - "Factorize", - "FactorizePar", - "MatVec", - "Gmres" - }; + "DOFs", "Residual", "ResidualPar", "ResidualSens", "ResidualSensPar", "JacobianPar", + "ConsistentInit", "ConsistentInitPar", "LinearSolve", "Factorize", "FactorizePar", "MatVec", + "Gmres"}; return desc; } #endif protected: - class Indexer; - int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity); + int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, + bool paramSensitivity); template - int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); + int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, + ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); template - int residualBulk(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem); + int residualBulk(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, + ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem); template - int residualParticle(double t, unsigned int parType, unsigned int colCell, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, util::ThreadLocalStorage& threadLocalMem); + int residualParticle(double t, unsigned int parType, unsigned int colCell, unsigned int secIdx, StateType const* y, + double const* yDot, ResidualType* res, util::ThreadLocalStorage& threadLocalMem); template int residualFlux(double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res); @@ -236,7 +276,8 @@ class LumpedRateModelWithPores : public UnitOperationBase int schurComplementMatrixVector(double const* x, double* z) const; void assembleDiscretizedJacobianParticleBlock(unsigned int type, double alpha, const Indexer& idxr); - void addTimeDerivativeToJacobianParticleBlock(linalg::FactorizableBandMatrix::RowIterator& jac, const Indexer& idxr, double alpha, unsigned int parType); + void addTimeDerivativeToJacobianParticleBlock(linalg::FactorizableBandMatrix::RowIterator& jac, const Indexer& idxr, + double alpha, unsigned int parType); void solveForFluxes(double* const vecState, const Indexer& idxr); unsigned int numAdDirsForJacobian() const CADET_NOEXCEPT; @@ -250,36 +291,46 @@ class LumpedRateModelWithPores : public UnitOperationBase struct Discretization { - unsigned int nComp; //!< Number of components - unsigned int nCol; //!< Number of column cells - unsigned int nParType; //!< Number of particle types - unsigned int* parTypeOffset; //!< Array with offsets (in particle block) to particle type, additional last element contains total number of particle DOFs - unsigned int* nBound; //!< Array with number of bound states for each component and particle type (particle type major ordering) - unsigned int* boundOffset; //!< Array with offset to the first bound state of each component in the solid phase (particle type major ordering) - unsigned int* strideBound; //!< Total number of bound states for each particle type, additional last element contains total number of bound states for all types - unsigned int* nBoundBeforeType; //!< Array with number of bound states before a particle type (cumulative sum of strideBound) + unsigned int nComp; //!< Number of components + unsigned int nCol; //!< Number of column cells + unsigned int nParType; //!< Number of particle types + unsigned int* parTypeOffset; //!< Array with offsets (in particle block) to particle type, additional last + //!< element contains total number of particle DOFs + unsigned int* nBound; //!< Array with number of bound states for each component and particle type (particle type + //!< major ordering) + unsigned int* boundOffset; //!< Array with offset to the first bound state of each component in the solid phase + //!< (particle type major ordering) + unsigned int* strideBound; //!< Total number of bound states for each particle type, additional last element + //!< contains total number of bound states for all types + unsigned int* nBoundBeforeType; //!< Array with number of bound states before a particle type (cumulative sum of + //!< strideBound) }; Discretization _disc; //!< Discretization info -// IExternalFunction* _extFun; //!< External function (owned by library user) + // IExternalFunction* _extFun; //!< External function (owned by library user) - ConvDispOperator _convDispOp; //!< Convection dispersion operator for interstitial volume transport - IDynamicReactionModel* _dynReactionBulk; //!< Dynamic reactions in the bulk volume + ConvDispOperator _convDispOp; //!< Convection dispersion operator for interstitial volume transport + IDynamicReactionModel* _dynReactionBulk; //!< Dynamic reactions in the bulk volume IParameterParameterDependence* _filmDiffDep; //!< Film diffusion dependency on local velocity std::vector _jacP; //!< Particle jacobian diagonal blocks (all of them for each particle type) - std::vector _jacPdisc; //!< Particle jacobian diagonal blocks (all of them for each particle type) with time derivatives from BDF method + std::vector _jacPdisc; //!< Particle jacobian diagonal blocks (all of them for each + //!< particle type) with time derivatives from BDF method - linalg::DoubleSparseMatrix _jacCF; //!< Jacobian block connecting interstitial states and fluxes (interstitial transport equation) + linalg::DoubleSparseMatrix + _jacCF; //!< Jacobian block connecting interstitial states and fluxes (interstitial transport equation) linalg::DoubleSparseMatrix _jacFC; //!< Jacobian block connecting fluxes and interstitial states (flux equation) - std::vector _jacPF; //!< Jacobian blocks connecting particle states and fluxes (particle transport boundary condition) - std::vector _jacFP; //!< Jacobian blocks connecting fluxes and particle states (flux equation) + std::vector + _jacPF; //!< Jacobian blocks connecting particle states and fluxes (particle transport boundary condition) + std::vector + _jacFP; //!< Jacobian blocks connecting fluxes and particle states (flux equation) linalg::DoubleSparseMatrix _jacInlet; //!< Jacobian inlet DOF block matrix connects inlet DOFs to first bulk cells - active _colPorosity; //!< Column porosity (external porosity) \f$ \varepsilon_c \f$ - std::vector _parGeomSurfToVol; //!< Particle surface to volume ratio factor (i.e., 3.0 for spherical, 2.0 for cylindrical, 1.0 for hexahedral) - std::vector _parRadius; //!< Particle radius \f$ r_p \f$ + active _colPorosity; //!< Column porosity (external porosity) \f$ \varepsilon_c \f$ + std::vector _parGeomSurfToVol; //!< Particle surface to volume ratio factor (i.e., 3.0 for spherical, 2.0 + //!< for cylindrical, 1.0 for hexahedral) + std::vector _parRadius; //!< Particle radius \f$ r_p \f$ bool _singleParRadius; std::vector _parPorosity; //!< Particle porosity (internal porosity) \f$ \varepsilon_p \f$ bool _singleParPorosity; @@ -291,19 +342,20 @@ class LumpedRateModelWithPores : public UnitOperationBase std::vector _poreAccessFactor; //!< Pore accessibility factor \f$ F_{\text{acc}} \f$ MultiplexMode _poreAccessFactorMode; - bool _axiallyConstantParTypeVolFrac; //!< Determines whether particle type volume fraction is homogeneous across axial coordinate - bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used - unsigned int _jacobianAdDirs; //!< Number of AD seed vectors required for Jacobian computation + bool _axiallyConstantParTypeVolFrac; //!< Determines whether particle type volume fraction is homogeneous across + //!< axial coordinate + bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used + unsigned int _jacobianAdDirs; //!< Number of AD seed vectors required for Jacobian computation bool _factorizeJacobian; //!< Determines whether the Jacobian needs to be factorized double* _tempState; //!< Temporary storage with the size of the state vector or larger if binding models require it linalg::Gmres _gmres; //!< GMRES algorithm for the Schur-complement in linearSolve() - double _schurSafety; //!< Safety factor for Schur-complement solution + double _schurSafety; //!< Safety factor for Schur-complement solution - std::vector _initC; //!< Liquid bulk phase initial conditions - std::vector _initCp; //!< Liquid particle phase initial conditions - std::vector _initQ; //!< Solid phase initial conditions - std::vector _initState; //!< Initial conditions for state vector if given + std::vector _initC; //!< Liquid bulk phase initial conditions + std::vector _initCp; //!< Liquid particle phase initial conditions + std::vector _initQ; //!< Solid phase initial conditions + std::vector _initState; //!< Initial conditions for state vector if given std::vector _initStateDot; //!< Initial conditions for time derivative BENCH_TIMER(_timerResidual) @@ -320,52 +372,132 @@ class LumpedRateModelWithPores : public UnitOperationBase BENCH_TIMER(_timerGmres) // Wrapper for calling the corresponding function in GeneralRateModel class - template - friend int schurComplementMultiplierLRMPores(void* userData, double const* x, double* z); + template friend int schurComplementMultiplierLRMPores(void* userData, double const* x, double* z); class Indexer { public: - Indexer(const Discretization& disc) : _disc(disc) { } + Indexer(const Discretization& disc) : _disc(disc) + { + } // Strides - inline int strideColCell() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideColComp() const CADET_NOEXCEPT { return 1; } + inline int strideColCell() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideColComp() const CADET_NOEXCEPT + { + return 1; + } - inline int strideParComp() const CADET_NOEXCEPT { return 1; } - inline int strideParLiquid() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideParBound(int parType) const CADET_NOEXCEPT { return static_cast(_disc.strideBound[parType]); } - inline int strideParBlock(int parType) const CADET_NOEXCEPT { return strideParLiquid() + strideParBound(parType); } + inline int strideParComp() const CADET_NOEXCEPT + { + return 1; + } + inline int strideParLiquid() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideParBound(int parType) const CADET_NOEXCEPT + { + return static_cast(_disc.strideBound[parType]); + } + inline int strideParBlock(int parType) const CADET_NOEXCEPT + { + return strideParLiquid() + strideParBound(parType); + } - inline int strideFluxCell() const CADET_NOEXCEPT { return static_cast(_disc.nComp) * static_cast(_disc.nParType); } - inline int strideFluxParType() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideFluxComp() const CADET_NOEXCEPT { return 1; } + inline int strideFluxCell() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp) * static_cast(_disc.nParType); + } + inline int strideFluxParType() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideFluxComp() const CADET_NOEXCEPT + { + return 1; + } // Offsets - inline int offsetC() const CADET_NOEXCEPT { return _disc.nComp; } - inline int offsetCp() const CADET_NOEXCEPT { return _disc.nComp * _disc.nCol + offsetC(); } - inline int offsetCp(ParticleTypeIndex pti) const CADET_NOEXCEPT { return offsetCp() + _disc.parTypeOffset[pti.value]; } - inline int offsetCp(ParticleTypeIndex pti, ParticleIndex pi) const CADET_NOEXCEPT { return offsetCp() + _disc.parTypeOffset[pti.value] + strideParBlock(pti.value) * pi.value; } - inline int offsetJf() const CADET_NOEXCEPT { return offsetCp() + _disc.parTypeOffset[_disc.nParType]; } - inline int offsetJf(ParticleTypeIndex pti) const CADET_NOEXCEPT { return offsetJf() + pti.value * _disc.nCol * _disc.nComp; } - inline int offsetBoundComp(ParticleTypeIndex pti, ComponentIndex comp) const CADET_NOEXCEPT { return _disc.boundOffset[pti.value * _disc.nComp + comp.value]; } + inline int offsetC() const CADET_NOEXCEPT + { + return _disc.nComp; + } + inline int offsetCp() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nCol + offsetC(); + } + inline int offsetCp(ParticleTypeIndex pti) const CADET_NOEXCEPT + { + return offsetCp() + _disc.parTypeOffset[pti.value]; + } + inline int offsetCp(ParticleTypeIndex pti, ParticleIndex pi) const CADET_NOEXCEPT + { + return offsetCp() + _disc.parTypeOffset[pti.value] + strideParBlock(pti.value) * pi.value; + } + inline int offsetJf() const CADET_NOEXCEPT + { + return offsetCp() + _disc.parTypeOffset[_disc.nParType]; + } + inline int offsetJf(ParticleTypeIndex pti) const CADET_NOEXCEPT + { + return offsetJf() + pti.value * _disc.nCol * _disc.nComp; + } + inline int offsetBoundComp(ParticleTypeIndex pti, ComponentIndex comp) const CADET_NOEXCEPT + { + return _disc.boundOffset[pti.value * _disc.nComp + comp.value]; + } // Return pointer to first element of state variable in state vector - template inline real_t* c(real_t* const data) const { return data + offsetC(); } - template inline real_t const* c(real_t const* const data) const { return data + offsetC(); } + template inline real_t* c(real_t* const data) const + { + return data + offsetC(); + } + template inline real_t const* c(real_t const* const data) const + { + return data + offsetC(); + } - template inline real_t* cp(real_t* const data) const { return data + offsetCp(); } - template inline real_t const* cp(real_t const* const data) const { return data + offsetCp(); } + template inline real_t* cp(real_t* const data) const + { + return data + offsetCp(); + } + template inline real_t const* cp(real_t const* const data) const + { + return data + offsetCp(); + } - template inline real_t* q(real_t* const data) const { return data + offsetCp() + strideParLiquid(); } - template inline real_t const* q(real_t const* const data) const { return data + offsetCp() + strideParLiquid(); } + template inline real_t* q(real_t* const data) const + { + return data + offsetCp() + strideParLiquid(); + } + template inline real_t const* q(real_t const* const data) const + { + return data + offsetCp() + strideParLiquid(); + } - template inline real_t* jf(real_t* const data) const { return data + offsetJf(); } - template inline real_t const* jf(real_t const* const data) const { return data + offsetJf(); } + template inline real_t* jf(real_t* const data) const + { + return data + offsetJf(); + } + template inline real_t const* jf(real_t const* const data) const + { + return data + offsetJf(); + } // Return specific variable in state vector - template inline real_t& c(real_t* const data, unsigned int col, unsigned int comp) const { return data[offsetC() + comp + col * strideColCell()]; } - template inline const real_t& c(real_t const* const data, unsigned int col, unsigned int comp) const { return data[offsetC() + comp + col * strideColCell()]; } + template inline real_t& c(real_t* const data, unsigned int col, unsigned int comp) const + { + return data[offsetC() + comp + col * strideColCell()]; + } + template + inline const real_t& c(real_t const* const data, unsigned int col, unsigned int comp) const + { + return data[offsetC() + comp + col * strideColCell()]; + } protected: const Discretization& _disc; @@ -374,29 +506,85 @@ class LumpedRateModelWithPores : public UnitOperationBase class Exporter : public ISolutionExporter { public: - - Exporter(const Discretization& disc, const LumpedRateModelWithPores& model, double const* data) : _disc(disc), _idx(disc), _model(model), _data(data) { } + Exporter(const Discretization& disc, const LumpedRateModelWithPores& model, double const* data) + : _disc(disc), _idx(disc), _model(model), _data(data) + { + } Exporter(const Discretization&& disc, const LumpedRateModelWithPores& model, double const* data) = delete; - virtual bool hasParticleFlux() const CADET_NOEXCEPT { return true; } - virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT { return true; } - virtual bool hasSolidPhase() const CADET_NOEXCEPT { return _disc.strideBound[_disc.nParType] > 0; } - virtual bool hasVolume() const CADET_NOEXCEPT { return false; } - virtual bool isParticleLumped() const CADET_NOEXCEPT { return true; } - virtual bool hasPrimaryExtent() const CADET_NOEXCEPT { return true; } - - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } - virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT { return _disc.nCol; } - virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numParticleTypes() const CADET_NOEXCEPT { return _disc.nParType; } - virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT { return 1; } - virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT { return _disc.strideBound[parType]; } - virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT { return _disc.nComp * _disc.nCol; } - virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return _disc.nComp * _disc.nCol; } - virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT { return _disc.nComp * _disc.nCol * _disc.nParType; } - virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return _disc.strideBound[parType] * _disc.nCol; } + virtual bool hasParticleFlux() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasSolidPhase() const CADET_NOEXCEPT + { + return _disc.strideBound[_disc.nParType] > 0; + } + virtual bool hasVolume() const CADET_NOEXCEPT + { + return false; + } + virtual bool isParticleLumped() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasPrimaryExtent() const CADET_NOEXCEPT + { + return true; + } + + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } + virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT + { + return _disc.nCol; + } + virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numParticleTypes() const CADET_NOEXCEPT + { + return _disc.nParType; + } + virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.strideBound[parType]; + } + virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nCol; + } + virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nCol; + } + virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nCol * _disc.nParType; + } + virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.strideBound[parType] * _disc.nCol; + } virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT { unsigned int nDofPerParType = 0; @@ -404,8 +592,14 @@ class LumpedRateModelWithPores : public UnitOperationBase nDofPerParType += _disc.strideBound[i]; return _disc.nCol * nDofPerParType; } - virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT { return _disc.nComp * _disc.nCol * _disc.nParType; } - virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT { return 0; } + virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nCol * _disc.nParType; + } + virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT + { + return 0; + } virtual int writeMobilePhase(double* buffer) const; virtual int writeSolidPhase(double* buffer) const; @@ -414,7 +608,10 @@ class LumpedRateModelWithPores : public UnitOperationBase virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const; virtual int writeParticleFlux(double* buffer) const; virtual int writeParticleFlux(unsigned int parType, double* buffer) const; - virtual int writeVolume(double* buffer) const { return 0; } + virtual int writeVolume(double* buffer) const + { + return 0; + } virtual int writeInlet(unsigned int port, double* buffer) const; virtual int writeInlet(double* buffer) const; virtual int writeOutlet(unsigned int port, double* buffer) const; @@ -426,7 +623,10 @@ class LumpedRateModelWithPores : public UnitOperationBase coords[i] = _model._convDispOp.cellCenter(i); return _disc.nCol; } - virtual int writeSecondaryCoordinates(double* coords) const { return 0; } + virtual int writeSecondaryCoordinates(double* coords) const + { + return 0; + } virtual int writeParticleCoordinates(unsigned int parType, double* coords) const { coords[0] = static_cast(_model._parRadius[parType]) * 0.5; @@ -450,4 +650,4 @@ IUnitOperation* createRadialFVLRMP(UnitOpIdx uoId); } // namespace model } // namespace cadet -#endif // LIBCADET_LUMPEDRATEMODELWITHPORES_HPP_ +#endif // LIBCADET_LUMPEDRATEMODELWITHPORES_HPP_ diff --git a/src/libcadet/model/LumpedRateModelWithPoresBuilder.cpp b/src/libcadet/model/LumpedRateModelWithPoresBuilder.cpp index c713f85f9..acca7b010 100644 --- a/src/libcadet/model/LumpedRateModelWithPoresBuilder.cpp +++ b/src/libcadet/model/LumpedRateModelWithPoresBuilder.cpp @@ -6,90 +6,94 @@ #include "LoggingUtils.hpp" #include "Logging.hpp" - namespace cadet { - namespace model - { +namespace model +{ - IUnitOperation* selectAxialFlowDiscretizationLRMP(UnitOpIdx uoId, IParameterProvider& paramProvider) - { - IUnitOperation* model = nullptr; +IUnitOperation* selectAxialFlowDiscretizationLRMP(UnitOpIdx uoId, IParameterProvider& paramProvider) +{ + IUnitOperation* model = nullptr; - paramProvider.pushScope("discretization"); + paramProvider.pushScope("discretization"); - if (paramProvider.exists("SPATIAL_METHOD")) { + if (paramProvider.exists("SPATIAL_METHOD")) + { - const std::string discName = paramProvider.getString("SPATIAL_METHOD"); + const std::string discName = paramProvider.getString("SPATIAL_METHOD"); #ifdef ENABLE_DG - if (discName == "DG") - model = new LumpedRateModelWithPoresDG(uoId); - else if (discName == "FV") + if (discName == "DG") + model = new LumpedRateModelWithPoresDG(uoId); + else if (discName == "FV") #else - if (discName == "FV") + if (discName == "FV") #endif - model = createAxialFVLRMP(uoId); - else - { - LOG(Error) << "Unknown discretization type " << discName << " for unit " << uoId; - } - } - else { - model = createAxialFVLRMP(uoId); - } - - paramProvider.popScope(); - - return model; + model = createAxialFVLRMP(uoId); + else + { + LOG(Error) << "Unknown discretization type " << discName << " for unit " << uoId; } + } + else + { + model = createAxialFVLRMP(uoId); + } - IUnitOperation* selectRadialFlowDiscretizationLRMP(UnitOpIdx uoId, IParameterProvider& paramProvider) - { - IUnitOperation* model = nullptr; + paramProvider.popScope(); - paramProvider.pushScope("discretization"); + return model; +} - if (paramProvider.exists("SPATIAL_METHOD")) { +IUnitOperation* selectRadialFlowDiscretizationLRMP(UnitOpIdx uoId, IParameterProvider& paramProvider) +{ + IUnitOperation* model = nullptr; - const std::string discName = paramProvider.getString("SPATIAL_METHOD"); + paramProvider.pushScope("discretization"); - if (discName == "DG") - { - LOG(Error) << "Radial flow not implemented for DG discretization yet, was called for unit " << uoId; - } - else if (discName == "FV") - model = createRadialFVLRMP(uoId); - else - { - LOG(Error) << "Unknown discretization type " << discName << " for unit " << uoId; - } - } - else { - model = createRadialFVLRMP(uoId); - } + if (paramProvider.exists("SPATIAL_METHOD")) + { - paramProvider.popScope(); + const std::string discName = paramProvider.getString("SPATIAL_METHOD"); - return model; + if (discName == "DG") + { + LOG(Error) << "Radial flow not implemented for DG discretization yet, was called for unit " << uoId; } - - void registerLumpedRateModelWithPores(std::unordered_map>& models) + else if (discName == "FV") + model = createRadialFVLRMP(uoId); + else { - typedef LumpedRateModelWithPores AxialLRMP; - typedef LumpedRateModelWithPores RadialLRMP; + LOG(Error) << "Unknown discretization type " << discName << " for unit " << uoId; + } + } + else + { + model = createRadialFVLRMP(uoId); + } + + paramProvider.popScope(); + + return model; +} + +void registerLumpedRateModelWithPores( + std::unordered_map>& models) +{ + typedef LumpedRateModelWithPores AxialLRMP; + typedef LumpedRateModelWithPores RadialLRMP; #ifdef ENABLE_DG - models[LumpedRateModelWithPoresDG::identifier()] = selectAxialFlowDiscretizationLRMP; - models["LRMP_DG"] = selectAxialFlowDiscretizationLRMP; + models[LumpedRateModelWithPoresDG::identifier()] = selectAxialFlowDiscretizationLRMP; + models["LRMP_DG"] = selectAxialFlowDiscretizationLRMP; #endif - models[AxialLRMP::identifier()] = selectAxialFlowDiscretizationLRMP; - models["LRMP"] = selectAxialFlowDiscretizationLRMP; + models[AxialLRMP::identifier()] = selectAxialFlowDiscretizationLRMP; + models["LRMP"] = selectAxialFlowDiscretizationLRMP; - models[RadialLRMP::identifier()] = selectRadialFlowDiscretizationLRMP; - models["RLRMP"] = selectRadialFlowDiscretizationLRMP; - } + models[RadialLRMP::identifier()] = selectRadialFlowDiscretizationLRMP; + models["RLRMP"] = selectRadialFlowDiscretizationLRMP; +} - } // namespace model +} // namespace model -} // namespace cadet \ No newline at end of file +} // namespace cadet diff --git a/src/libcadet/model/LumpedRateModelWithPoresDG-InitialConditions.cpp b/src/libcadet/model/LumpedRateModelWithPoresDG-InitialConditions.cpp index 5f4512b7a..12515d533 100644 --- a/src/libcadet/model/LumpedRateModelWithPoresDG-InitialConditions.cpp +++ b/src/libcadet/model/LumpedRateModelWithPoresDG-InitialConditions.cpp @@ -33,697 +33,763 @@ namespace cadet { - namespace model - { +namespace model +{ - int LumpedRateModelWithPoresDG::multiplexInitialConditions(const cadet::ParameterId& pId, unsigned int adDirection, double adValue) +int LumpedRateModelWithPoresDG::multiplexInitialConditions(const cadet::ParameterId& pId, unsigned int adDirection, + double adValue) +{ + if (_singleBinding) + { + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { - if (_singleBinding) - { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) - { - _sensParams.insert(&_initCp[pId.component]); - for (unsigned int t = 0; t < _disc.nParType; ++t) - _initCp[t * _disc.nComp + pId.component].setADValue(adDirection, adValue); - } - else if (pId.name == hashString("INIT_CP")) - return -1; - - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) - { - _sensParams.insert(&_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState]); - for (unsigned int t = 0; t < _disc.nParType; ++t) - _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState].setADValue(adDirection, adValue); - } - else if (pId.name == hashString("INIT_Q")) - return -1; - } - else - { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) - { - _sensParams.insert(&_initCp[pId.particleType * _disc.nComp + pId.component]); - _initCp[pId.particleType * _disc.nComp + pId.component].setADValue(adDirection, adValue); - } - else if (pId.name == hashString("INIT_CP")) - return -1; - - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) - { - _sensParams.insert(&_initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState]); - _initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState].setADValue(adDirection, adValue); - } - else if (pId.name == hashString("INIT_Q")) - return -1; - } - return 0; + _sensParams.insert(&_initCp[pId.component]); + for (unsigned int t = 0; t < _disc.nParType; ++t) + _initCp[t * _disc.nComp + pId.component].setADValue(adDirection, adValue); } + else if (pId.name == hashString("INIT_CP")) + return -1; - int LumpedRateModelWithPoresDG::multiplexInitialConditions(const cadet::ParameterId& pId, double val, bool checkSens) + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { - if (_singleBinding) - { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) - { - if (checkSens && !contains(_sensParams, &_initCp[pId.component])) - return -1; - - for (unsigned int t = 0; t < _disc.nParType; ++t) - _initCp[t * _disc.nComp + pId.component].setValue(val); - } - else if (pId.name == hashString("INIT_CP")) - return -1; - - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) - { - if (checkSens && !contains(_sensParams, &_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState])) - return -1; - - for (unsigned int t = 0; t < _disc.nParType; ++t) - _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState].setValue(val); - } - else if (pId.name == hashString("INIT_Q")) - return -1; - } - else - { - if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) - { - if (checkSens && !contains(_sensParams, &_initCp[pId.particleType * _disc.nComp + pId.component])) - return -1; + _sensParams.insert(&_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState]); + for (unsigned int t = 0; t < _disc.nParType; ++t) + _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState] + .setADValue(adDirection, adValue); + } + else if (pId.name == hashString("INIT_Q")) + return -1; + } + else + { + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) + { + _sensParams.insert(&_initCp[pId.particleType * _disc.nComp + pId.component]); + _initCp[pId.particleType * _disc.nComp + pId.component].setADValue(adDirection, adValue); + } + else if (pId.name == hashString("INIT_CP")) + return -1; - _initCp[pId.particleType * _disc.nComp + pId.component].setValue(val); - } - else if (pId.name == hashString("INIT_CP")) - return -1; + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) + { + _sensParams.insert( + &_initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState]); + _initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState] + .setADValue(adDirection, adValue); + } + else if (pId.name == hashString("INIT_Q")) + return -1; + } + return 0; +} - if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && (pId.reaction == ReactionIndep)) - { - if (checkSens && !contains(_sensParams, &_initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState])) - return -1; +int LumpedRateModelWithPoresDG::multiplexInitialConditions(const cadet::ParameterId& pId, double val, bool checkSens) +{ + if (_singleBinding) + { + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) + { + if (checkSens && !contains(_sensParams, &_initCp[pId.component])) + return -1; - _initQ[_disc.nBoundBeforeType[pId.particleType] + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState].setValue(val); - } - else if (pId.name == hashString("INIT_Q")) - return -1; - } - return 0; + for (unsigned int t = 0; t < _disc.nParType; ++t) + _initCp[t * _disc.nComp + pId.component].setValue(val); } + else if (pId.name == hashString("INIT_CP")) + return -1; - void LumpedRateModelWithPoresDG::applyInitialCondition(const SimulationState& simState) const + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) { - Indexer idxr(_disc); + if (checkSens && + !contains(_sensParams, + &_initQ[_disc.nBoundBeforeType[0] + _disc.boundOffset[pId.component] + pId.boundState])) + return -1; + + for (unsigned int t = 0; t < _disc.nParType; ++t) + _initQ[_disc.nBoundBeforeType[t] + _disc.boundOffset[t * _disc.nComp + pId.component] + pId.boundState] + .setValue(val); + } + else if (pId.name == hashString("INIT_Q")) + return -1; + } + else + { + if ((pId.name == hashString("INIT_CP")) && (pId.section == SectionIndep) && + (pId.boundState == BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) + { + if (checkSens && !contains(_sensParams, &_initCp[pId.particleType * _disc.nComp + pId.component])) + return -1; - // Check whether full state vector is available as initial condition - if (!_initState.empty()) - { - std::fill(simState.vecStateY, simState.vecStateY + idxr.offsetC(), 0.0); - std::copy(_initState.data(), _initState.data() + numPureDofs(), simState.vecStateY + idxr.offsetC()); + _initCp[pId.particleType * _disc.nComp + pId.component].setValue(val); + } + else if (pId.name == hashString("INIT_CP")) + return -1; - if (!_initStateDot.empty()) - { - std::fill(simState.vecStateYdot, simState.vecStateYdot + idxr.offsetC(), 0.0); - std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), simState.vecStateYdot + idxr.offsetC()); - } - else - std::fill(simState.vecStateYdot, simState.vecStateYdot + numDofs(), 0.0); + if ((pId.name == hashString("INIT_Q")) && (pId.section == SectionIndep) && + (pId.boundState != BoundStateIndep) && (pId.particleType != ParTypeIndep) && (pId.component != CompIndep) && + (pId.reaction == ReactionIndep)) + { + if (checkSens && + !contains(_sensParams, + &_initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState])) + return -1; + + _initQ[_disc.nBoundBeforeType[pId.particleType] + + _disc.boundOffset[pId.particleType * _disc.nComp + pId.component] + pId.boundState] + .setValue(val); + } + else if (pId.name == hashString("INIT_Q")) + return -1; + } + return 0; +} - return; - } +void LumpedRateModelWithPoresDG::applyInitialCondition(const SimulationState& simState) const +{ + Indexer idxr(_disc); - double* const stateYbulk = simState.vecStateY + idxr.offsetC(); + // Check whether full state vector is available as initial condition + if (!_initState.empty()) + { + std::fill(simState.vecStateY, simState.vecStateY + idxr.offsetC(), 0.0); + std::copy(_initState.data(), _initState.data() + numPureDofs(), simState.vecStateY + idxr.offsetC()); - // Loop over column cells - for (unsigned int point = 0; point < _disc.nPoints; ++point) - { - // Loop over components in cell - for (unsigned comp = 0; comp < _disc.nComp; ++comp) - stateYbulk[point * idxr.strideColNode() + comp * idxr.strideColComp()] = static_cast(_initC[comp]); - } + if (!_initStateDot.empty()) + { + std::fill(simState.vecStateYdot, simState.vecStateYdot + idxr.offsetC(), 0.0); + std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), + simState.vecStateYdot + idxr.offsetC()); + } + else + std::fill(simState.vecStateYdot, simState.vecStateYdot + numDofs(), 0.0); - // Loop over particles - for (unsigned int type = 0; type < _disc.nParType; ++type) - { - for (unsigned int point = 0; point < _disc.nPoints; ++point) - { - const unsigned int offset = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ point }); + return; + } - // Initialize c_p - for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - simState.vecStateY[offset + comp] = static_cast(_initCp[comp + _disc.nComp * type]); + double* const stateYbulk = simState.vecStateY + idxr.offsetC(); - // Initialize q - active const* const iq = _initQ.data() + _disc.nBoundBeforeType[type]; - for (unsigned int bnd = 0; bnd < _disc.strideBound[type]; ++bnd) - simState.vecStateY[offset + idxr.strideParLiquid() + bnd] = static_cast(iq[bnd]); - } - } - } + // Loop over column cells + for (unsigned int point = 0; point < _disc.nPoints; ++point) + { + // Loop over components in cell + for (unsigned comp = 0; comp < _disc.nComp; ++comp) + stateYbulk[point * idxr.strideColNode() + comp * idxr.strideColComp()] = static_cast(_initC[comp]); + } - void LumpedRateModelWithPoresDG::readInitialCondition(IParameterProvider& paramProvider) + // Loop over particles + for (unsigned int type = 0; type < _disc.nParType; ++type) + { + for (unsigned int point = 0; point < _disc.nPoints; ++point) { - _initState.clear(); - _initStateDot.clear(); + const unsigned int offset = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{point}); - // Check if INIT_STATE is present - if (paramProvider.exists("INIT_STATE")) - { - const std::vector initState = paramProvider.getDoubleArray("INIT_STATE"); - _initState = std::vector(initState.begin(), initState.begin() + numPureDofs()); + // Initialize c_p + for (unsigned int comp = 0; comp < _disc.nComp; ++comp) + simState.vecStateY[offset + comp] = static_cast(_initCp[comp + _disc.nComp * type]); - // Check if INIT_STATE contains the full state and its time derivative - if (initState.size() >= 2 * numPureDofs()) - _initStateDot = std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); - return; - } + // Initialize q + active const* const iq = _initQ.data() + _disc.nBoundBeforeType[type]; + for (unsigned int bnd = 0; bnd < _disc.strideBound[type]; ++bnd) + simState.vecStateY[offset + idxr.strideParLiquid() + bnd] = static_cast(iq[bnd]); + } + } +} - const std::vector initC = paramProvider.getDoubleArray("INIT_C"); +void LumpedRateModelWithPoresDG::readInitialCondition(IParameterProvider& paramProvider) +{ + _initState.clear(); + _initStateDot.clear(); - if (initC.size() < _disc.nComp) - throw InvalidParameterException("INIT_C does not contain enough values for all components"); + // Check if INIT_STATE is present + if (paramProvider.exists("INIT_STATE")) + { + const std::vector initState = paramProvider.getDoubleArray("INIT_STATE"); + _initState = std::vector(initState.begin(), initState.begin() + numPureDofs()); - ad::copyToAd(initC.data(), _initC.data(), _disc.nComp); + // Check if INIT_STATE contains the full state and its time derivative + if (initState.size() >= 2 * numPureDofs()) + _initStateDot = + std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); + return; + } - // Check if INIT_CP is present - if (paramProvider.exists("INIT_CP")) - { - const std::vector initCp = paramProvider.getDoubleArray("INIT_CP"); + const std::vector initC = paramProvider.getDoubleArray("INIT_C"); - if (((initCp.size() < _disc.nComp) && _singleBinding) || ((initCp.size() < _disc.nComp * _disc.nParType) && !_singleBinding)) - throw InvalidParameterException("INIT_CP does not contain enough values for all components"); + if (initC.size() < _disc.nComp) + throw InvalidParameterException("INIT_C does not contain enough values for all components"); - if (!_singleBinding) - ad::copyToAd(initCp.data(), _initCp.data(), _disc.nComp * _disc.nParType); - else - { - for (unsigned int t = 0; t < _disc.nParType; ++t) - ad::copyToAd(initCp.data(), _initCp.data() + t * _disc.nComp, _disc.nComp); - } - } - else - { - for (unsigned int t = 0; t < _disc.nParType; ++t) - ad::copyToAd(initC.data(), _initCp.data() + t * _disc.nComp, _disc.nComp); - } - - std::vector initQ; - if (paramProvider.exists("INIT_Q")) - initQ = paramProvider.getDoubleArray("INIT_Q"); + ad::copyToAd(initC.data(), _initC.data(), _disc.nComp); - if (initQ.empty() || (_disc.strideBound[_disc.nParType] == 0)) - return; + // Check if INIT_CP is present + if (paramProvider.exists("INIT_CP")) + { + const std::vector initCp = paramProvider.getDoubleArray("INIT_CP"); - if ((_disc.strideBound[_disc.nParType] > 0) && (((initQ.size() < _disc.strideBound[_disc.nParType]) && !_singleBinding) || ((initQ.size() < _disc.strideBound[0]) && _singleBinding))) - throw InvalidParameterException("INIT_Q does not contain enough values for all bound states"); + if (((initCp.size() < _disc.nComp) && _singleBinding) || + ((initCp.size() < _disc.nComp * _disc.nParType) && !_singleBinding)) + throw InvalidParameterException("INIT_CP does not contain enough values for all components"); - if (!_singleBinding) - ad::copyToAd(initQ.data(), _initQ.data(), _disc.strideBound[_disc.nParType]); - else - { - for (unsigned int t = 0; t < _disc.nParType; ++t) - ad::copyToAd(initQ.data(), _initQ.data() + _disc.nBoundBeforeType[t], _disc.strideBound[t]); - } + if (!_singleBinding) + ad::copyToAd(initCp.data(), _initCp.data(), _disc.nComp * _disc.nParType); + else + { + for (unsigned int t = 0; t < _disc.nParType; ++t) + ad::copyToAd(initCp.data(), _initCp.data() + t * _disc.nComp, _disc.nComp); } + } + else + { + for (unsigned int t = 0; t < _disc.nParType; ++t) + ad::copyToAd(initC.data(), _initCp.data() + t * _disc.nComp, _disc.nComp); + } - /** - * @brief Computes consistent initial values (state variables without their time derivatives) - * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have - * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time - * derivative \f$ \dot{y}_0 \f$ such that they are consistent. - * - * The process works in two steps: - *
                                    - *
                                  1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria). - * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  2. - *
                                  3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0. - * However, because of the algebraic equations, we need additional conditions to fully determine - * @f$ \dot{y}@f$. By differentiating the algebraic equations with respect to time, we get the - * missing linear equations (recall that the state vector @f$ y @f$ is fixed). The resulting system - * has a similar structure as the system Jacobian. - * @f[ \begin{align} - * \left[\begin{array}{c|ccc|c} - * \dot{J}_0 & & & & \\ - * \hline - * & \dot{J}_1 & & & \\ - * & & \ddots & & \\ - * & & & \dot{J}_{N_z} & \\ - * \hline - * J_{f,0} & J_{f,1} & \dots & J_{f,N_z} & I - * \end{array}\right], - * \end{align} @f] - * where @f$ \dot{J}_i @f$ denotes the Jacobian with respect to @f$ \dot{y}@f$. Note that the - * @f$ J_{i,f} @f$ matrices in the right column are missing. - * - * The right hand side of the linear system is given by the negative residual without contribution - * of @f$ \dot{y} @f$ for differential equations and 0 for algebraic equations - * (@f$ -\frac{\partial F}{\partial t}@f$, to be more precise). - * - * The linear system is solved by backsubstitution. First, the diagonal blocks are solved in parallel. - * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG)
                                  4. - *
                                  - * This function performs step 1. See consistentInitialTimeDerivative() for step 2. - * - * This function is to be used with consistentInitialTimeDerivative(). Do not mix normal and lean - * consistent initialization! - * - * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) - * @param [in,out] vecStateY State vector with initial values that are to be updated for consistency - * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) - * @param [in] errorTol Error tolerance for algebraic equations - * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) - */ - void LumpedRateModelWithPoresDG::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) - { - BENCH_SCOPE(_timerConsistentInit); + std::vector initQ; + if (paramProvider.exists("INIT_Q")) + initQ = paramProvider.getDoubleArray("INIT_Q"); - Indexer idxr(_disc); + if (initQ.empty() || (_disc.strideBound[_disc.nParType] == 0)) + return; - // Step 1: Solve algebraic equations + if ((_disc.strideBound[_disc.nParType] > 0) && + (((initQ.size() < _disc.strideBound[_disc.nParType]) && !_singleBinding) || + ((initQ.size() < _disc.strideBound[0]) && _singleBinding))) + throw InvalidParameterException("INIT_Q does not contain enough values for all bound states"); - // Step 1a: Compute quasi-stationary binding model state - for (unsigned int type = 0; type < _disc.nParType; ++type) - { - if (!_binding[type]->hasQuasiStationaryReactions()) - continue; + if (!_singleBinding) + ad::copyToAd(initQ.data(), _initQ.data(), _disc.strideBound[_disc.nParType]); + else + { + for (unsigned int t = 0; t < _disc.nParType; ++t) + ad::copyToAd(initQ.data(), _initQ.data() + _disc.nBoundBeforeType[t], _disc.strideBound[t]); + } +} + +/** + * @brief Computes consistent initial values (state variables without their time derivatives) + * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have + * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time + * derivative \f$ \dot{y}_0 \f$ such that they are consistent. + * + * The process works in two steps: + *
                                    + *
                                  1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria). + * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  2. + *
                                  3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0. + * However, because of the algebraic equations, we need additional conditions to fully determine + * @f$ \dot{y}@f$. By differentiating the algebraic equations with respect to time, we get the + * missing linear equations (recall that the state vector @f$ y @f$ is fixed). The resulting system + * has a similar structure as the system Jacobian. + * @f[ \begin{align} + * \left[\begin{array}{c|ccc|c} + * \dot{J}_0 & & & & \\ + * \hline + * & \dot{J}_1 & & & \\ + * & & \ddots & & \\ + * & & & \dot{J}_{N_z} & \\ + * \hline + * J_{f,0} & J_{f,1} & \dots & J_{f,N_z} & I + * \end{array}\right], + * \end{align} @f] + * where @f$ \dot{J}_i @f$ denotes the Jacobian with respect to @f$ \dot{y}@f$. Note that the + * @f$ J_{i,f} @f$ matrices in the right column are missing. + * + * The right hand side of the linear system is given by the negative residual without contribution + * of @f$ \dot{y} @f$ for differential equations and 0 for algebraic equations + * (@f$ -\frac{\partial F}{\partial t}@f$, to be more precise). + * + * The linear system is solved by backsubstitution. First, the diagonal blocks are solved in parallel. + * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG)
                                  4. + *
                                  + * This function performs step 1. See consistentInitialTimeDerivative() for step 2. + * + * This function is to be used with consistentInitialTimeDerivative(). Do not mix normal and lean + * consistent initialization! + * + * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) + * @param [in,out] vecStateY State vector with initial values that are to be updated for consistency + * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) + * @param [in] errorTol Error tolerance for algebraic equations + * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) + */ +void LumpedRateModelWithPoresDG::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerConsistentInit); - // Copy quasi-stationary binding mask to a local array that also includes the mobile phase - std::vector qsMask(_disc.nComp + _disc.strideBound[type], false); - int const* const qsMaskSrc = _binding[type]->reactionQuasiStationarity(); - std::copy_n(qsMaskSrc, _disc.strideBound[type], qsMask.data() + _disc.nComp); + Indexer idxr(_disc); - // Activate mobile phase components that have at least one active bound state - unsigned int bndStartIdx = 0; - unsigned int numActiveComp = 0; - for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - { - for (unsigned int bnd = 0; bnd < _disc.nBound[_disc.nComp * type + comp]; ++bnd) - { - if (qsMaskSrc[bndStartIdx + bnd]) - { - ++numActiveComp; - qsMask[comp] = true; - break; - } - } + // Step 1: Solve algebraic equations - bndStartIdx += _disc.nBound[_disc.nComp * type + comp]; + // Step 1a: Compute quasi-stationary binding model state + for (unsigned int type = 0; type < _disc.nParType; ++type) + { + if (!_binding[type]->hasQuasiStationaryReactions()) + continue; + + // Copy quasi-stationary binding mask to a local array that also includes the mobile phase + std::vector qsMask(_disc.nComp + _disc.strideBound[type], false); + int const* const qsMaskSrc = _binding[type]->reactionQuasiStationarity(); + std::copy_n(qsMaskSrc, _disc.strideBound[type], qsMask.data() + _disc.nComp); + + // Activate mobile phase components that have at least one active bound state + unsigned int bndStartIdx = 0; + unsigned int numActiveComp = 0; + for (unsigned int comp = 0; comp < _disc.nComp; ++comp) + { + for (unsigned int bnd = 0; bnd < _disc.nBound[_disc.nComp * type + comp]; ++bnd) + { + if (qsMaskSrc[bndStartIdx + bnd]) + { + ++numActiveComp; + qsMask[comp] = true; + break; } + } + + bndStartIdx += _disc.nBound[_disc.nComp * type + comp]; + } - const linalg::ConstMaskArray mask{ qsMask.data(), static_cast(_disc.nComp + _disc.strideBound[type]) }; - const int probSize = linalg::numMaskActive(mask); + const linalg::ConstMaskArray mask{qsMask.data(), static_cast(_disc.nComp + _disc.strideBound[type])}; + const int probSize = linalg::numMaskActive(mask); - //Problem capturing variables here + // Problem capturing variables here #ifdef CADET_PARALLELIZE - BENCH_SCOPE(_timerConsistentInitPar); + BENCH_SCOPE(_timerConsistentInitPar); tbb::parallel_for(std::size_t(0), static_cast(_disc.nPoints), [&](std::size_t pblk) #else - for (unsigned int pblk = 0; pblk < _disc.nPoints; ++pblk) + for (unsigned int pblk = 0; pblk < _disc.nPoints; ++pblk) #endif { - LinearBufferAllocator tlmAlloc = threadLocalMem.get(); - - // Reuse memory of band matrix for dense matrix - linalg::DenseMatrixView fullJacobianMatrix(_globalJacDisc.valuePtr() + _globalJacDisc.outerIndexPtr()[idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ static_cast(pblk) }) - idxr.offsetC()], nullptr, mask.len, mask.len); - - // z coordinate (column length normed to 1) of current node - needed in externally dependent adsorption kinetic - const double z = _convDispOp.relativeCoordinate(pblk); - - // Get workspace memory - BufferedArray nonlinMemBuffer = tlmAlloc.array(_nonlinearSolver->workspaceSize(probSize)); - double* const nonlinMem = static_cast(nonlinMemBuffer); - - BufferedArray solutionBuffer = tlmAlloc.array(probSize); - double* const solution = static_cast(solutionBuffer); - - BufferedArray fullResidualBuffer = tlmAlloc.array(mask.len); - double* const fullResidual = static_cast(fullResidualBuffer); - - BufferedArray fullXBuffer = tlmAlloc.array(mask.len); - double* const fullX = static_cast(fullXBuffer); - - BufferedArray jacobianMemBuffer = tlmAlloc.array(probSize * probSize); - linalg::DenseMatrixView jacobianMatrix(static_cast(jacobianMemBuffer), _globalJacDisc.outerIndexPtr() + pblk * probSize, probSize, probSize); - - BufferedArray conservedQuantsBuffer = tlmAlloc.array(numActiveComp); - double* const conservedQuants = static_cast(conservedQuantsBuffer); - - const parts::cell::CellParameters cellResParams - { - _disc.nComp, - _disc.nBound + _disc.nComp * type, - _disc.boundOffset + _disc.nComp * type, - _disc.strideBound[type], - _binding[type]->reactionQuasiStationarity(), - _parPorosity[type], - _poreAccessFactor.data() + _disc.nComp * type, - _binding[type], - (_dynReaction[type] && (_dynReaction[type]->numReactionsCombined() > 0)) ? _dynReaction[type] : nullptr - }; - - const int localOffsetToParticle = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ static_cast(pblk) }); - const int localOffsetInParticle = idxr.strideParLiquid(); - - // Get pointer to q variables in a shell of particle pblk - double* const qShell = vecStateY + localOffsetToParticle + localOffsetInParticle; - active* const localAdRes = adJac.adRes ? adJac.adRes + localOffsetToParticle : nullptr; - active* const localAdY = adJac.adY ? adJac.adY + localOffsetToParticle : nullptr; - - const ColumnPosition colPos{ z, 0.0, static_cast(_parRadius[type]) * 0.5 }; - - // Determine whether nonlinear solver is required - if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - idxr.strideParLiquid(), tlmAlloc)) - CADET_PAR_CONTINUE; - - // Extract initial values from current state - linalg::selectVectorSubset(qShell - _disc.nComp, mask, solution); - - // Save values of conserved moieties - const double epsQ = 1.0 - static_cast(_parPorosity[type]); - linalg::conservedMoietiesFromPartitionedMask(mask, _disc.nBound + type * _disc.nComp, _disc.nComp, qShell - _disc.nComp, conservedQuants, static_cast(_parPorosity[type]), epsQ); - - std::function jacFunc; - if (localAdY && localAdRes) // todo fix AD consistent initialization with req. binding + LinearBufferAllocator tlmAlloc = threadLocalMem.get(); + + // Reuse memory of band matrix for dense matrix + linalg::DenseMatrixView fullJacobianMatrix( + _globalJacDisc.valuePtr() + + _globalJacDisc.outerIndexPtr()[idxr.offsetCp(ParticleTypeIndex{type}, + ParticleIndex{static_cast(pblk)}) - + idxr.offsetC()], + nullptr, mask.len, mask.len); + + // z coordinate (column length normed to 1) of current node - needed in externally dependent adsorption + // kinetic + const double z = _convDispOp.relativeCoordinate(pblk); + + // Get workspace memory + BufferedArray nonlinMemBuffer = tlmAlloc.array(_nonlinearSolver->workspaceSize(probSize)); + double* const nonlinMem = static_cast(nonlinMemBuffer); + + BufferedArray solutionBuffer = tlmAlloc.array(probSize); + double* const solution = static_cast(solutionBuffer); + + BufferedArray fullResidualBuffer = tlmAlloc.array(mask.len); + double* const fullResidual = static_cast(fullResidualBuffer); + + BufferedArray fullXBuffer = tlmAlloc.array(mask.len); + double* const fullX = static_cast(fullXBuffer); + + BufferedArray jacobianMemBuffer = tlmAlloc.array(probSize * probSize); + linalg::DenseMatrixView jacobianMatrix(static_cast(jacobianMemBuffer), + _globalJacDisc.outerIndexPtr() + pblk * probSize, probSize, + probSize); + + BufferedArray conservedQuantsBuffer = tlmAlloc.array(numActiveComp); + double* const conservedQuants = static_cast(conservedQuantsBuffer); + + const parts::cell::CellParameters cellResParams{ + _disc.nComp, + _disc.nBound + _disc.nComp * type, + _disc.boundOffset + _disc.nComp * type, + _disc.strideBound[type], + _binding[type]->reactionQuasiStationarity(), + _parPorosity[type], + _poreAccessFactor.data() + _disc.nComp * type, + _binding[type], + (_dynReaction[type] && (_dynReaction[type]->numReactionsCombined() > 0)) ? _dynReaction[type] + : nullptr}; + + const int localOffsetToParticle = + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); + const int localOffsetInParticle = idxr.strideParLiquid(); + + // Get pointer to q variables in a shell of particle pblk + double* const qShell = vecStateY + localOffsetToParticle + localOffsetInParticle; + active* const localAdRes = adJac.adRes ? adJac.adRes + localOffsetToParticle : nullptr; + active* const localAdY = adJac.adY ? adJac.adY + localOffsetToParticle : nullptr; + + const ColumnPosition colPos{z, 0.0, static_cast(_parRadius[type]) * 0.5}; + + // Determine whether nonlinear solver is required + if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - idxr.strideParLiquid(), tlmAlloc)) + CADET_PAR_CONTINUE; + + // Extract initial values from current state + linalg::selectVectorSubset(qShell - _disc.nComp, mask, solution); + + // Save values of conserved moieties + const double epsQ = 1.0 - static_cast(_parPorosity[type]); + linalg::conservedMoietiesFromPartitionedMask(mask, _disc.nBound + type * _disc.nComp, _disc.nComp, + qShell - _disc.nComp, conservedQuants, + static_cast(_parPorosity[type]), epsQ); + + std::function jacFunc; + if (localAdY && localAdRes) // todo fix AD consistent initialization with req. binding + { + jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) { + // Copy over state vector to AD state vector (without changing directional values to keep seed + // vectors) and initialize residuals with zero (also resetting directional values) + ad::copyToAd(qShell - _disc.nComp, localAdY, mask.len); + // @todo Check if this is necessary + ad::resetAd(localAdRes, mask.len); + + // Prepare input vector by overwriting masked items + linalg::applyVectorSubset(x, mask, localAdY); + + // Call residual function + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, localAdY, nullptr, localAdRes, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); + + // // todo check analytical jacobian + // #ifdef CADET_CHECK_ANALYTIC_JACOBIAN + // std::copy_n(qShell - _disc.nComp, mask.len, fullX); + // linalg::applyVectorSubset(x, mask, fullX); + // + // // Compute analytic Jacobian + // parts::cell::residualKernel( + // simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + // cellResParams, tlmAlloc + // ); + // + // // Compare + // const double diff = ad::compareDenseJacobianWithBandedAd( + // adJac.adRes + idxr.offsetCp(ParticleTypeIndex{ type }), pblk * + // idxr.strideParBlock(type), adJac.adDirOffset, _jacP[type].lowerBandwidth(), + // _jacP[type].lowerBandwidth(), _jacP[type].upperBandwidth(), + // fullJacobianMatrix + // ); + // LOG(Debug) << "MaxDiff: " << diff; + // #endif + // Extract Jacobian from AD + // Read particle Jacobian entries from dedicated AD directions + int offsetParticleTypeDirs = adJac.adDirOffset + _convDispOp.requiredADdirs(); + const active* const adRes = adJac.adRes; + + for (unsigned int type = 0; type < _disc.nParType; type++) { - jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) + for (unsigned int par = 0; par < _disc.nPoints; par++) { - // Copy over state vector to AD state vector (without changing directional values to keep seed vectors) - // and initialize residuals with zero (also resetting directional values) - ad::copyToAd(qShell - _disc.nComp, localAdY, mask.len); - // @todo Check if this is necessary - ad::resetAd(localAdRes, mask.len); - - // Prepare input vector by overwriting masked items - linalg::applyVectorSubset(x, mask, localAdY); - - // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, localAdY, nullptr, localAdRes, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); - -// // todo check analytical jacobian -//#ifdef CADET_CHECK_ANALYTIC_JACOBIAN -// std::copy_n(qShell - _disc.nComp, mask.len, fullX); -// linalg::applyVectorSubset(x, mask, fullX); -// -// // Compute analytic Jacobian -// parts::cell::residualKernel( -// simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc -// ); -// -// // Compare -// const double diff = ad::compareDenseJacobianWithBandedAd( -// adJac.adRes + idxr.offsetCp(ParticleTypeIndex{ type }), pblk * idxr.strideParBlock(type), adJac.adDirOffset, _jacP[type].lowerBandwidth(), -// _jacP[type].lowerBandwidth(), _jacP[type].upperBandwidth(), fullJacobianMatrix -// ); -// LOG(Debug) << "MaxDiff: " << diff; -//#endif - // Extract Jacobian from AD - // Read particle Jacobian entries from dedicated AD directions - int offsetParticleTypeDirs = adJac.adDirOffset + _convDispOp.requiredADdirs(); - const active* const adRes = adJac.adRes; - - for (unsigned int type = 0; type < _disc.nParType; type++) + const int eqOffset_res = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); + const int eqOffset_mat = + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}) - idxr.offsetC(); + for (unsigned int phase = 0; phase < idxr.strideParBlock(type); phase++) { - for (unsigned int par = 0; par < _disc.nPoints; par++) + for (unsigned int phaseTo = 0; phaseTo < idxr.strideParBlock(type); phaseTo++) { - const int eqOffset_res = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ par }); - const int eqOffset_mat = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ par }) - idxr.offsetC(); - for (unsigned int phase = 0; phase < idxr.strideParBlock(type); phase++) - { - for (unsigned int phaseTo = 0; phaseTo < idxr.strideParBlock(type); phaseTo++) - { - _globalJac.coeffRef(eqOffset_mat + phase, eqOffset_mat + phaseTo) = adRes[eqOffset_res + phase].getADValue(offsetParticleTypeDirs + phaseTo); - } - } + _globalJac.coeffRef(eqOffset_mat + phase, eqOffset_mat + phaseTo) = + adRes[eqOffset_res + phase].getADValue(offsetParticleTypeDirs + phaseTo); } - offsetParticleTypeDirs += idxr.strideParBlock(type); } + } + offsetParticleTypeDirs += idxr.strideParBlock(type); + } - // Extract Jacobian from full Jacobian - mat.setAll(0.0); - linalg::copyMatrixSubset(fullJacobianMatrix, mask, mask, mat); + // Extract Jacobian from full Jacobian + mat.setAll(0.0); + linalg::copyMatrixSubset(fullJacobianMatrix, mask, mask, mat); - // Replace upper part with conservation relations - mat.submatrixSetAll(0.0, 0, 0, numActiveComp, probSize); + // Replace upper part with conservation relations + mat.submatrixSetAll(0.0, 0, 0, numActiveComp, probSize); - unsigned int bndIdx = 0; - unsigned int rIdx = 0; - unsigned int bIdx = 0; - for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - { - if (!mask.mask[comp]) - { - bndIdx += _disc.nBound[_disc.nComp * type + comp]; - continue; - } + unsigned int bndIdx = 0; + unsigned int rIdx = 0; + unsigned int bIdx = 0; + for (unsigned int comp = 0; comp < _disc.nComp; ++comp) + { + if (!mask.mask[comp]) + { + bndIdx += _disc.nBound[_disc.nComp * type + comp]; + continue; + } - mat.native(rIdx, rIdx) = static_cast(_parPorosity[type]); + mat.native(rIdx, rIdx) = static_cast(_parPorosity[type]); - for (unsigned int bnd = 0; bnd < _disc.nBound[_disc.nComp * type + comp]; ++bnd, ++bndIdx) - { - if (mask.mask[bndIdx]) - { - mat.native(rIdx, bIdx + numActiveComp) = epsQ; - ++bIdx; - } - } - - ++rIdx; + for (unsigned int bnd = 0; bnd < _disc.nBound[_disc.nComp * type + comp]; ++bnd, ++bndIdx) + { + if (mask.mask[bndIdx]) + { + mat.native(rIdx, bIdx + numActiveComp) = epsQ; + ++bIdx; } + } - return true; - }; + ++rIdx; } - else + + return true; + }; + } + else + { + jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) { + // Prepare input vector by overwriting masked items + std::copy_n(qShell - _disc.nComp, mask.len, fullX); + linalg::applyVectorSubset(x, mask, fullX); + + // Call residual function + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); + + // Extract Jacobian from full Jacobian + mat.setAll(0.0); + linalg::copyMatrixSubset(fullJacobianMatrix, mask, mask, mat); + + // Replace upper part with conservation relations + mat.submatrixSetAll(0.0, 0, 0, numActiveComp, probSize); + + unsigned int bndIdx = 0; + unsigned int rIdx = 0; + unsigned int bIdx = 0; + for (unsigned int comp = 0; comp < _disc.nComp; ++comp) { - jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) + if (!mask.mask[comp]) { - // Prepare input vector by overwriting masked items - std::copy_n(qShell - _disc.nComp, mask.len, fullX); - linalg::applyVectorSubset(x, mask, fullX); - - // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); - - // Extract Jacobian from full Jacobian - mat.setAll(0.0); - linalg::copyMatrixSubset(fullJacobianMatrix, mask, mask, mat); - - // Replace upper part with conservation relations - mat.submatrixSetAll(0.0, 0, 0, numActiveComp, probSize); - - unsigned int bndIdx = 0; - unsigned int rIdx = 0; - unsigned int bIdx = 0; - for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - { - if (!mask.mask[comp]) - { - bndIdx += _disc.nBound[_disc.nComp * type + comp]; - continue; - } - - mat.native(rIdx, rIdx) = static_cast(_parPorosity[type]); + bndIdx += _disc.nBound[_disc.nComp * type + comp]; + continue; + } - for (unsigned int bnd = 0; bnd < _disc.nBound[_disc.nComp * type + comp]; ++bnd, ++bndIdx) - { - if (mask.mask[bndIdx]) - { - mat.native(rIdx, bIdx + numActiveComp) = epsQ; - ++bIdx; - } - } + mat.native(rIdx, rIdx) = static_cast(_parPorosity[type]); - ++rIdx; + for (unsigned int bnd = 0; bnd < _disc.nBound[_disc.nComp * type + comp]; ++bnd, ++bndIdx) + { + if (mask.mask[bndIdx]) + { + mat.native(rIdx, bIdx + numActiveComp) = epsQ; + ++bIdx; } + } - return true; - }; + ++rIdx; } - // Apply nonlinear solver - _nonlinearSolver->solve( - [&](double const* const x, double* const r) - { - // Prepare input vector by overwriting masked items - std::copy_n(qShell - _disc.nComp, mask.len, fullX); - linalg::applyVectorSubset(x, mask, fullX); - - // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); - - // Extract values from residual - linalg::selectVectorSubset(fullResidual, mask, r); - - // Calculate residual of conserved moieties - std::fill_n(r, numActiveComp, 0.0); - unsigned int bndIdx = _disc.nComp; - unsigned int rIdx = 0; - unsigned int bIdx = 0; - for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - { - if (!mask.mask[comp]) - { - bndIdx += _disc.nBound[_disc.nComp * type + comp]; - continue; - } + return true; + }; + } - r[rIdx] = static_cast(_parPorosity[type]) * x[rIdx] - conservedQuants[rIdx]; + // Apply nonlinear solver + _nonlinearSolver->solve( + [&](double const* const x, double* const r) { + // Prepare input vector by overwriting masked items + std::copy_n(qShell - _disc.nComp, mask.len, fullX); + linalg::applyVectorSubset(x, mask, fullX); + + // Call residual function + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); + + // Extract values from residual + linalg::selectVectorSubset(fullResidual, mask, r); + + // Calculate residual of conserved moieties + std::fill_n(r, numActiveComp, 0.0); + unsigned int bndIdx = _disc.nComp; + unsigned int rIdx = 0; + unsigned int bIdx = 0; + for (unsigned int comp = 0; comp < _disc.nComp; ++comp) + { + if (!mask.mask[comp]) + { + bndIdx += _disc.nBound[_disc.nComp * type + comp]; + continue; + } - for (unsigned int bnd = 0; bnd < _disc.nBound[_disc.nComp * type + comp]; ++bnd, ++bndIdx) - { - if (mask.mask[bndIdx]) - { - r[rIdx] += epsQ * x[bIdx + numActiveComp]; - ++bIdx; - } - } + r[rIdx] = static_cast(_parPorosity[type]) * x[rIdx] - conservedQuants[rIdx]; - ++rIdx; + for (unsigned int bnd = 0; bnd < _disc.nBound[_disc.nComp * type + comp]; ++bnd, ++bndIdx) + { + if (mask.mask[bndIdx]) + { + r[rIdx] += epsQ * x[bIdx + numActiveComp]; + ++bIdx; } + } - return true; - }, - jacFunc, errorTol, solution, nonlinMem, jacobianMatrix, probSize); + ++rIdx; + } - // Apply solution - linalg::applyVectorSubset(solution, mask, qShell - idxr.strideParLiquid()); + return true; + }, + jacFunc, errorTol, solution, nonlinMem, jacobianMatrix, probSize); - // Refine / correct solution - _binding[type]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - idxr.strideParLiquid(), tlmAlloc); + // Apply solution + linalg::applyVectorSubset(solution, mask, qShell - idxr.strideParLiquid()); - } CADET_PARFOR_END; - } - - // reset jacobian pattern //@todo can this be avoided? - setGlobalJacPattern(_globalJacDisc, _dynReactionBulk); - } + // Refine / correct solution + _binding[type]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - idxr.strideParLiquid(), tlmAlloc); - /** - * @brief Computes consistent initial time derivatives - * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have - * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time - * derivative \f$ \dot{y}_0 \f$ such that they are consistent. - * - * The process works in two steps: - *
                                    - *
                                  1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria). - * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  2. - *
                                  3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0. - * However, because of the algebraic equations, we need additional conditions to fully determine - * @f$ \dot{y}@f$. By differentiating the algebraic equations with respect to time, we get the - * missing linear equations (recall that the state vector @f$ y @f$ is fixed). The resulting system - * has a similar structure as the system Jacobian. - * @f[ \begin{align} - * \left[\begin{array}{c|ccc|c} - * \dot{J}_0 & & & & \\ - * \hline - * & \dot{J}_1 & & & \\ - * & & \ddots & & \\ - * & & & \dot{J}_{N_z} & \\ - * \hline - * J_{f,0} & J_{f,1} & \dots & J_{f,N_z} & I - * \end{array}\right], - * \end{align} @f] - * where @f$ \dot{J}_i @f$ denotes the Jacobian with respect to @f$ \dot{y}@f$. Note that the - * @f$ J_{i,f} @f$ matrices in the right column are missing. - * - * The right hand side of the linear system is given by the negative residual without contribution - * of @f$ \dot{y} @f$ for differential equations and 0 for algebraic equations - * (@f$ -\frac{\partial F}{\partial t}@f$, to be more precise). - * - * The linear system is solved by backsubstitution. First, the diagonal blocks are solved in parallel. - * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  4. - *
                                  - * This function performs step 2. See consistentInitialState() for step 1. - * - * This function is to be used with consistentInitialState(). Do not mix normal and lean - * consistent initialization! - * - * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) - * @param [in] vecStateY Consistently initialized state vector - * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent state time derivatives. - */ - void LumpedRateModelWithPoresDG::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem) - { - BENCH_SCOPE(_timerConsistentInit); + } CADET_PARFOR_END; + } + + // reset jacobian pattern //@todo can this be avoided? + setGlobalJacPattern(_globalJacDisc, _dynReactionBulk); +} + +/** + * @brief Computes consistent initial time derivatives + * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have + * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time + * derivative \f$ \dot{y}_0 \f$ such that they are consistent. + * + * The process works in two steps: + *
                                    + *
                                  1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria). + * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  2. + *
                                  3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0. + * However, because of the algebraic equations, we need additional conditions to fully determine + * @f$ \dot{y}@f$. By differentiating the algebraic equations with respect to time, we get the + * missing linear equations (recall that the state vector @f$ y @f$ is fixed). The resulting system + * has a similar structure as the system Jacobian. + * @f[ \begin{align} + * \left[\begin{array}{c|ccc|c} + * \dot{J}_0 & & & & \\ + * \hline + * & \dot{J}_1 & & & \\ + * & & \ddots & & \\ + * & & & \dot{J}_{N_z} & \\ + * \hline + * J_{f,0} & J_{f,1} & \dots & J_{f,N_z} & I + * \end{array}\right], + * \end{align} @f] + * where @f$ \dot{J}_i @f$ denotes the Jacobian with respect to @f$ \dot{y}@f$. Note that the + * @f$ J_{i,f} @f$ matrices in the right column are missing. + * + * The right hand side of the linear system is given by the negative residual without contribution + * of @f$ \dot{y} @f$ for differential equations and 0 for algebraic equations + * (@f$ -\frac{\partial F}{\partial t}@f$, to be more precise). + * + * The linear system is solved by backsubstitution. First, the diagonal blocks are solved in parallel. + * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  4. + *
                                  + * This function performs step 2. See consistentInitialState() for step 1. + * + * This function is to be used with consistentInitialState(). Do not mix normal and lean + * consistent initialization! + * + * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) + * @param [in] vecStateY Consistently initialized state vector + * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent + * state time derivatives. + */ +void LumpedRateModelWithPoresDG::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, + util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerConsistentInit); - Eigen::Map y(vecStateY, numDofs()); - Eigen::Map yDot(vecStateYdot, numDofs()); + Eigen::Map y(vecStateY, numDofs()); + Eigen::Map yDot(vecStateYdot, numDofs()); - Indexer idxr(_disc); + Indexer idxr(_disc); - // Step 2: Compute the correct time derivative of the state vector + // Step 2: Compute the correct time derivative of the state vector - // Step 2a: Assemble, factorize, and solve diagonal blocks of linear system + // Step 2a: Assemble, factorize, and solve diagonal blocks of linear system - // Note that the residual has not been negated, yet. We will do that now. - for (unsigned int i = 0; i < numDofs(); ++i) - vecStateYdot[i] = -vecStateYdot[i]; + // Note that the residual has not been negated, yet. We will do that now. + for (unsigned int i = 0; i < numDofs(); ++i) + vecStateYdot[i] = -vecStateYdot[i]; - // Handle bulk column block - // Assemble - double* vPtr = _globalJacDisc.valuePtr(); - for (int k = 0; k < _globalJacDisc.nonZeros(); k++) { - vPtr[k] = 0.0; - } - _convDispOp.addTimeDerivativeToJacobian(1.0, _globalJacDisc); + // Handle bulk column block + // Assemble + double* vPtr = _globalJacDisc.valuePtr(); + for (int k = 0; k < _globalJacDisc.nonZeros(); k++) + { + vPtr[k] = 0.0; + } + _convDispOp.addTimeDerivativeToJacobian(1.0, _globalJacDisc); - // Process the particle blocks + // Process the particle blocks #ifdef CADET_PARALLELIZE - BENCH_START(_timerConsistentInitPar); + BENCH_START(_timerConsistentInitPar); tbb::parallel_for(std::size_t(0), static_cast(_disc.nParType), [&](std::size_t type) #else - for (unsigned int type = 0; type < _disc.nParType; ++type) + for (unsigned int type = 0; type < _disc.nParType; ++type) #endif { - LinearBufferAllocator tlmAlloc = threadLocalMem.get(); - double* const dFluxDt = _tempState + idxr.offsetCp(ParticleTypeIndex{ static_cast(type) }); - - for (unsigned int pblk = 0; pblk < _disc.nPoints; ++pblk) - { - // z coordinate (column length normed to 1) of current node - needed in externally dependent adsorption kinetic - const double z = _convDispOp.relativeCoordinate(pblk); - - // Assemble - linalg::BandedEigenSparseRowIterator jacPar(_globalJacDisc, idxr.offsetCp(ParticleTypeIndex{ static_cast(type) }, ParticleIndex{ pblk }) - idxr.offsetC()); - - // Mobile and stationary phase (advances jac accordingly) - addTimeDerivativeToJacobianParticleBlock(jacPar, idxr, 1.0, type); - - if (!_binding[type]->hasQuasiStationaryReactions()) - continue; - - // Get iterators to beginning of solid phase - linalg::BandedEigenSparseRowIterator jacSolidOrig(_globalJac, idxr.offsetCp(ParticleTypeIndex{ static_cast(type) }, ParticleIndex{ pblk }) - idxr.offsetC() + idxr.strideParLiquid()); - linalg::BandedEigenSparseRowIterator jacSolid = jacPar - idxr.strideParBound(type); + LinearBufferAllocator tlmAlloc = threadLocalMem.get(); + double* const dFluxDt = _tempState + idxr.offsetCp(ParticleTypeIndex{static_cast(type)}); - int const* const mask = _binding[type]->reactionQuasiStationarity(); - double* const qShellDot = vecStateYdot + idxr.offsetCp(ParticleTypeIndex{ static_cast(type) }, ParticleIndex{ pblk }) + idxr.strideParLiquid(); + for (unsigned int pblk = 0; pblk < _disc.nPoints; ++pblk) + { + // z coordinate (column length normed to 1) of current node - needed in externally dependent adsorption + // kinetic + const double z = _convDispOp.relativeCoordinate(pblk); - // Obtain derivative of fluxes wrt. time - std::fill_n(dFluxDt, _disc.strideBound[type], 0.0); - if (_binding[type]->dependsOnTime()) - { - _binding[type]->timeDerivativeQuasiStationaryFluxes(simTime.t, simTime.secIdx, - ColumnPosition{ z, 0.0, static_cast(_parRadius[type]) * 0.5 }, - qShellDot - _disc.nComp, qShellDot, dFluxDt, tlmAlloc); - } + // Assemble + linalg::BandedEigenSparseRowIterator jacPar( + _globalJacDisc, idxr.offsetCp(ParticleTypeIndex{static_cast(type)}, ParticleIndex{pblk}) - + idxr.offsetC()); + + // Mobile and stationary phase (advances jac accordingly) + addTimeDerivativeToJacobianParticleBlock(jacPar, idxr, 1.0, type); + + if (!_binding[type]->hasQuasiStationaryReactions()) + continue; + + // Get iterators to beginning of solid phase + linalg::BandedEigenSparseRowIterator jacSolidOrig( + _globalJac, idxr.offsetCp(ParticleTypeIndex{static_cast(type)}, ParticleIndex{pblk}) - + idxr.offsetC() + idxr.strideParLiquid()); + linalg::BandedEigenSparseRowIterator jacSolid = jacPar - idxr.strideParBound(type); + + int const* const mask = _binding[type]->reactionQuasiStationarity(); + double* const qShellDot = + vecStateYdot + idxr.offsetCp(ParticleTypeIndex{static_cast(type)}, ParticleIndex{pblk}) + + idxr.strideParLiquid(); + + // Obtain derivative of fluxes wrt. time + std::fill_n(dFluxDt, _disc.strideBound[type], 0.0); + if (_binding[type]->dependsOnTime()) + { + _binding[type]->timeDerivativeQuasiStationaryFluxes( + simTime.t, simTime.secIdx, ColumnPosition{z, 0.0, static_cast(_parRadius[type]) * 0.5}, + qShellDot - _disc.nComp, qShellDot, dFluxDt, tlmAlloc); + } - // Copy row from original Jacobian and set right hand side - for (int i = 0; i < idxr.strideParBound(type); ++i, ++jacSolid, ++jacSolidOrig) - { - if (!mask[i]) - continue; + // Copy row from original Jacobian and set right hand side + for (int i = 0; i < idxr.strideParBound(type); ++i, ++jacSolid, ++jacSolidOrig) + { + if (!mask[i]) + continue; - jacSolid.copyRowFrom(jacSolidOrig); - qShellDot[i] = -dFluxDt[i]; - } - } + jacSolid.copyRowFrom(jacSolidOrig); + qShellDot[i] = -dFluxDt[i]; + } + } } CADET_PARFOR_END; @@ -736,400 +802,439 @@ namespace cadet if (cadet_unlikely(_linearSolver->info() != Eigen::Success)) { - LOG(Error) << "Factorize() failed"; + LOG(Error) << "Factorize() failed"; } // Solve yDot.segment(idxr.offsetC(), numPureDofs()) = _linearSolver->solve(yDot.segment(idxr.offsetC(), numPureDofs())); if (cadet_unlikely(_linearSolver->info() != Eigen::Success)) { - LOG(Error) << "Solve() failed"; + LOG(Error) << "Solve() failed"; } - } +} + +/** + * @brief Computes approximately / partially consistent initial values (state variables without their time derivatives) + * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have + * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time + * derivative \f$ \dot{y}_0 \f$ such that they are consistent. + * + * This function performs a relaxed consistent initialization: Only parts of the vectors are updated + * and, hence, consistency is not guaranteed. Since there is less work to do, it is probably faster than + * the standard process represented by consistentInitialState(). + * + * The process works in two steps: + *
                                    + *
                                  1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations). + * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  2. + *
                                  3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0 in the column + * bulk and flux blocks. The resulting equations are stated below: + * @f[ \begin{align} + * \left[\begin{array}{c|ccc|c} + * \dot{J}_0 & & & & \\ + * \hline + * J_{f,0} & J_{f,1} & \dots & J_{f,N_z} & I + * \end{array}\right], + * \end{align} @f] + * where @f$ \dot{J}_0 @f$ denotes the bulk block Jacobian with respect to @f$ \dot{y}@f$. + * + * The right hand side of the linear system is given by the negative residual without contribution + * of @f$ \dot{y} @f$ for the bulk block and 0 for the flux block. + * + * The linear system is solved by backsubstitution. First, the bulk block is solved. + * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  4. + *
                                  + * This function performs step 1. See leanConsistentInitialTimeDerivative() for step 2. + * + * This function is to be used with leanConsistentInitialTimeDerivative(). Do not mix normal and lean + * consistent initialization! + * + * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) + * @param [in,out] vecStateY State vector with initial values that are to be updated for consistency + * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) + * @param [in] errorTol Error tolerance for algebraic equations + */ +void LumpedRateModelWithPoresDG::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerConsistentInit); + // Nothing to do here since there are no dedicated flux state variables for the DG LRMP discretization. +} + +/** + * @brief Computes approximately / partially consistent initial time derivatives + * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have + * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time + * derivative \f$ \dot{y}_0 \f$ such that they are consistent. + * + * This function performs a relaxed consistent initialization: Only parts of the vectors are updated + * and, hence, consistency is not guaranteed. Since there is less work to do, it is probably faster than + * the standard process represented by consistentInitialTimeDerivative(). + * + * The process works in two steps: + *
                                    + *
                                  1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations). + * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  2. + *
                                  3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0 in the column + * bulk and flux blocks. The resulting equations are stated below: + * @f[ \begin{align} + * \left[\begin{array}{c|ccc|c} + * \dot{J}_0 & & & & \\ + * \hline + * J_{f,0} & J_{f,1} & \dots & J_{f,N_z} & I + * \end{array}\right], + * \end{align} @f] + * where @f$ \dot{J}_0 @f$ denotes the bulk block Jacobian with respect to @f$ \dot{y}@f$. + * + * The right hand side of the linear system is given by the negative residual without contribution + * of @f$ \dot{y} @f$ for the bulk block and 0 for the flux block. + * + * The linear system is solved by backsubstitution. First, the bulk block is solved. + * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  4. + *
                                  + * This function performs step 2. See leanConsistentInitialState() for step 1. + * + * This function is to be used with leanConsistentInitialState(). Do not mix normal and lean + * consistent initialization! + * + * @param [in] t Current time point + * @param [in] vecStateY (Lean) consistently initialized state vector + * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time + * derivatives. + * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during + * execution of the function. + */ +void LumpedRateModelWithPoresDG::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerConsistentInit); + Indexer idxr(_disc); + double* const resSlice = res + idxr.offsetC(); - /** - * @brief Computes approximately / partially consistent initial values (state variables without their time derivatives) - * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have - * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time - * derivative \f$ \dot{y}_0 \f$ such that they are consistent. - * - * This function performs a relaxed consistent initialization: Only parts of the vectors are updated - * and, hence, consistency is not guaranteed. Since there is less work to do, it is probably faster than - * the standard process represented by consistentInitialState(). - * - * The process works in two steps: - *
                                    - *
                                  1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations). - * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  2. - *
                                  3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0 in the column - * bulk and flux blocks. The resulting equations are stated below: - * @f[ \begin{align} - * \left[\begin{array}{c|ccc|c} - * \dot{J}_0 & & & & \\ - * \hline - * J_{f,0} & J_{f,1} & \dots & J_{f,N_z} & I - * \end{array}\right], - * \end{align} @f] - * where @f$ \dot{J}_0 @f$ denotes the bulk block Jacobian with respect to @f$ \dot{y}@f$. - * - * The right hand side of the linear system is given by the negative residual without contribution - * of @f$ \dot{y} @f$ for the bulk block and 0 for the flux block. - * - * The linear system is solved by backsubstitution. First, the bulk block is solved. - * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  4. - *
                                  - * This function performs step 1. See leanConsistentInitialTimeDerivative() for step 2. - * - * This function is to be used with leanConsistentInitialTimeDerivative(). Do not mix normal and lean - * consistent initialization! - * - * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) - * @param [in,out] vecStateY State vector with initial values that are to be updated for consistency - * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) - * @param [in] errorTol Error tolerance for algebraic equations - */ - void LumpedRateModelWithPoresDG::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) - { - BENCH_SCOPE(_timerConsistentInit); - // Nothing to do here since there are no dedicated flux state variables for the DG LRMP discretization. - } + // Step 2: Compute the correct time derivative of the state vector, i.e. + // assemble, factorize, and solve column bulk block of linear system - /** - * @brief Computes approximately / partially consistent initial time derivatives - * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have - * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time - * derivative \f$ \dot{y}_0 \f$ such that they are consistent. - * - * This function performs a relaxed consistent initialization: Only parts of the vectors are updated - * and, hence, consistency is not guaranteed. Since there is less work to do, it is probably faster than - * the standard process represented by consistentInitialTimeDerivative(). - * - * The process works in two steps: - *
                                    - *
                                  1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations). - * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  2. - *
                                  3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0 in the column - * bulk and flux blocks. The resulting equations are stated below: - * @f[ \begin{align} - * \left[\begin{array}{c|ccc|c} - * \dot{J}_0 & & & & \\ - * \hline - * J_{f,0} & J_{f,1} & \dots & J_{f,N_z} & I - * \end{array}\right], - * \end{align} @f] - * where @f$ \dot{J}_0 @f$ denotes the bulk block Jacobian with respect to @f$ \dot{y}@f$. - * - * The right hand side of the linear system is given by the negative residual without contribution - * of @f$ \dot{y} @f$ for the bulk block and 0 for the flux block. - * - * The linear system is solved by backsubstitution. First, the bulk block is solved. - * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  4. - *
                                  - * This function performs step 2. See leanConsistentInitialState() for step 1. - * - * This function is to be used with leanConsistentInitialState(). Do not mix normal and lean - * consistent initialization! - * - * @param [in] t Current time point - * @param [in] vecStateY (Lean) consistently initialized state vector - * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time derivatives. - * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during execution of the function. - */ - void LumpedRateModelWithPoresDG::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem) - { - BENCH_SCOPE(_timerConsistentInit); + // Note that the residual is not negated as required at this point. We will fix that later. + + solveBulkTimeDerivativeSystem(SimulationTime{t, 0u}, resSlice); - Indexer idxr(_disc); + // Note that we have solved with the *positive* residual as right hand side + // instead of the *negative* one. Fortunately, we are dealing with linear systems, + // which means that we can just negate the solution. + double* const yDotSlice = vecStateYdot + idxr.offsetC(); + for (unsigned int i = 0; i < _disc.nCol * _disc.nComp; ++i) + yDotSlice[i] = -resSlice[i]; +} - double* const resSlice = res + idxr.offsetC(); +void LumpedRateModelWithPoresDG::solveBulkTimeDerivativeSystem(const SimulationTime& simTime, double* const rhs) +{ - // Step 2: Compute the correct time derivative of the state vector, i.e. - // assemble, factorize, and solve column bulk block of linear system + Indexer idxr(_disc); - // Note that the residual is not negated as required at this point. We will fix that later. + // Assemble + double* vals = _globalJacDisc.valuePtr(); + for (int entry = 0; entry < _globalJacDisc.nonZeros(); entry++) + vals[entry] = 0.0; - solveBulkTimeDerivativeSystem(SimulationTime{ t, 0u }, resSlice); + double alpha = 1.0; + const int gapCell = idxr.strideColNode() - static_cast(_disc.nComp) * idxr.strideColComp(); + linalg::BandedEigenSparseRowIterator jac(_globalJacDisc, 0); - // Note that we have solved with the *positive* residual as right hand side - // instead of the *negative* one. Fortunately, we are dealing with linear systems, - // which means that we can just negate the solution. - double* const yDotSlice = vecStateYdot + idxr.offsetC(); - for (unsigned int i = 0; i < _disc.nCol * _disc.nComp; ++i) - yDotSlice[i] = -resSlice[i]; + for (unsigned int point = 0; point < _disc.nPoints; ++point, jac += gapCell) + { + for (unsigned int comp = 0; comp < _disc.nComp; ++comp, ++jac) + { + // dc_b / dt in transport equation + jac[0] += alpha; } + } - void LumpedRateModelWithPoresDG::solveBulkTimeDerivativeSystem(const SimulationTime& simTime, double* const rhs) { - - Indexer idxr(_disc); + const int bulkRows = idxr.offsetCp() - idxr.offsetC(); + _linearSolver->analyzePattern(_globalJacDisc.block(0, 0, bulkRows, bulkRows)); + _linearSolver->factorize(_globalJacDisc.block(0, 0, bulkRows, bulkRows)); - // Assemble - double* vals = _globalJacDisc.valuePtr(); - for (int entry = 0; entry < _globalJacDisc.nonZeros(); entry++) - vals[entry] = 0.0; - - double alpha = 1.0; - const int gapCell = idxr.strideColNode() - static_cast(_disc.nComp) * idxr.strideColComp(); - linalg::BandedEigenSparseRowIterator jac(_globalJacDisc, 0); - - for (unsigned int point = 0; point < _disc.nPoints; ++point, jac += gapCell) { - for (unsigned int comp = 0; comp < _disc.nComp; ++comp, ++jac) { - // dc_b / dt in transport equation - jac[0] += alpha; - } - } + if (_linearSolver->info() != Success) + { + LOG(Error) << "factorization failed in sensitivity initialization"; + } - const int bulkRows = idxr.offsetCp() - idxr.offsetC(); - _linearSolver->analyzePattern(_globalJacDisc.block(0, 0, bulkRows, bulkRows)); - _linearSolver->factorize(_globalJacDisc.block(0, 0, bulkRows, bulkRows)); + Eigen::Map ret_vec(rhs, bulkRows); + ret_vec = _linearSolver->solve(ret_vec); - if (_linearSolver->info() != Success) { - LOG(Error) << "factorization failed in sensitivity initialization"; - } + // Use the factors to solve the linear system + if (_linearSolver->info() != Success) + { + LOG(Error) << "solve failed in sensitivity initialization"; + } - Eigen::Map ret_vec(rhs, bulkRows); - ret_vec = _linearSolver->solve(ret_vec); + // reset linear solver to global Jacobian + _linearSolver->analyzePattern(_globalJacDisc); +} - // Use the factors to solve the linear system - if (_linearSolver->info() != Success) { - LOG(Error) << "solve failed in sensitivity initialization"; - } +void LumpedRateModelWithPoresDG::initializeSensitivityStates(const std::vector& vecSensY) const +{ + Indexer idxr(_disc); + for (std::size_t param = 0; param < vecSensY.size(); ++param) + { + double* const stateYbulk = vecSensY[param] + idxr.offsetC(); - // reset linear solver to global Jacobian - _linearSolver->analyzePattern(_globalJacDisc); + // Loop over column cells + for (unsigned int point = 0; point < _disc.nPoints; ++point) + { + // Loop over components in cell + for (unsigned comp = 0; comp < _disc.nComp; ++comp) + stateYbulk[point * idxr.strideColNode() + comp * idxr.strideColComp()] = _initC[comp].getADValue(param); } - void LumpedRateModelWithPoresDG::initializeSensitivityStates(const std::vector& vecSensY) const + // Loop over particles + for (unsigned int type = 0; type < _disc.nParType; ++type) { - Indexer idxr(_disc); - for (std::size_t param = 0; param < vecSensY.size(); ++param) + for (unsigned int point = 0; point < _disc.nPoints; ++point) { - double* const stateYbulk = vecSensY[param] + idxr.offsetC(); - - // Loop over column cells - for (unsigned int point = 0; point < _disc.nPoints; ++point) - { - // Loop over components in cell - for (unsigned comp = 0; comp < _disc.nComp; ++comp) - stateYbulk[point * idxr.strideColNode() + comp * idxr.strideColComp()] = _initC[comp].getADValue(param); - } + const unsigned int offset = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{point}); + double* const stateYparticle = vecSensY[param] + offset; + double* const stateYparticleSolid = stateYparticle + idxr.strideParLiquid(); - // Loop over particles - for (unsigned int type = 0; type < _disc.nParType; ++type) - { - for (unsigned int point = 0; point < _disc.nPoints; ++point) - { - const unsigned int offset = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ point }); - double* const stateYparticle = vecSensY[param] + offset; - double* const stateYparticleSolid = stateYparticle + idxr.strideParLiquid(); - - // Initialize c_p - for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - stateYparticle[comp] = _initCp[comp + _disc.nComp * type].getADValue(param); + // Initialize c_p + for (unsigned int comp = 0; comp < _disc.nComp; ++comp) + stateYparticle[comp] = _initCp[comp + _disc.nComp * type].getADValue(param); - // Initialize q - for (unsigned int bnd = 0; bnd < _disc.strideBound[type]; ++bnd) - stateYparticleSolid[bnd] = _initQ[bnd + _disc.nBoundBeforeType[type]].getADValue(param); - } - } + // Initialize q + for (unsigned int bnd = 0; bnd < _disc.strideBound[type]; ++bnd) + stateYparticleSolid[bnd] = _initQ[bnd + _disc.nBoundBeforeType[type]].getADValue(param); } } + } +} + +/** + * @brief Computes consistent initial values and time derivatives of sensitivity subsystems + * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, + * the sensitivity system for a parameter @f$ p @f$ reads + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of this linear DAE, @f$ s_0 = + * \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ have to be + * consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the + * time derivative \f$ \dot{s}_0 \f$ such that they are consistent. + * + * The process follows closely the one of consistentInitialConditions() and, in fact, is a linearized version + * of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ + * are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them + * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial + * \dot{y}_0}{\partial p}. @f$
                                  1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, + * reaction equilibria). No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG). Let + * @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at this point, we have \f[ \left( \frac{\partial + * F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, y_0, \dot{y}_0) \right)_{\mathcal{I}_a} = 0. + * \f]
                                  2. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations + * hold. However, because of the algebraic equations, we need additional conditions to fully determine + * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the + * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting + * system has a similar structure as the system Jacobian. + * @f[ \begin{align} + * \left[\begin{array}{c|ccc|c} + * \dot{J}_0 & & & & \\ + * \hline + * & \dot{J}_1 & & & \\ + * & & \ddots & & \\ + * & & & \dot{J}_{N_z} & \\ + * \hline + * J_{f,0} & J_{f,1} & \dots & J_{f,N_z} & I + * \end{array}\right], + * \end{align} @f] + * where @f$ \dot{J}_i @f$ denotes the Jacobian with respect to @f$ \dot{y}@f$. Note that the + * @f$ J_{i,f} @f$ matrices in the right column are missing. + * + * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. + * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - + * \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] which is 0 for algebraic equations (@f$ -\frac{\partial^2 + * F}{\partial t \partial p}@f$, to be more precise). + * + * The linear system is solved by backsubstitution. First, the diagonal blocks are solved in parallel. + * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  3. + *
                                  + * This function requires the parameter sensitivities to be computed beforehand and up-to-date Jacobians. + * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) + * @param [in] simState Consistent state of the simulation (state vector and its time derivative) + * @param [in,out] vecSensY Sensitivity subsystem state vectors + * @param [in,out] vecSensYdot Time derivative state vectors of the sensitivity subsystems to be initialized + * @param [in] adRes Pointer to residual vector of AD datatypes with parameter sensitivities + * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) + */ +void LumpedRateModelWithPoresDG::consistentInitialSensitivity( + const SimulationTime& simTime, const ConstSimulationState& simState, std::vector& vecSensY, + std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerConsistentInit); - /** - * @brief Computes consistent initial values and time derivatives of sensitivity subsystems - * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, - * the sensitivity system for a parameter @f$ p @f$ reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of this linear DAE, @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ - * have to be consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the time - * derivative \f$ \dot{s}_0 \f$ such that they are consistent. - * - * The process follows closely the one of consistentInitialConditions() and, in fact, is a linearized version of it. - * This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ are - * related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them - * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p}. @f$ - *
                                    - *
                                  1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria). - * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG). Let @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at this point, we have - * \f[ \left( \frac{\partial F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, y_0, \dot{y}_0) \right)_{\mathcal{I}_a} = 0. \f]
                                  2. - *
                                  3. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. - * However, because of the algebraic equations, we need additional conditions to fully determine - * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the - * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting system - * has a similar structure as the system Jacobian. - * @f[ \begin{align} - * \left[\begin{array}{c|ccc|c} - * \dot{J}_0 & & & & \\ - * \hline - * & \dot{J}_1 & & & \\ - * & & \ddots & & \\ - * & & & \dot{J}_{N_z} & \\ - * \hline - * J_{f,0} & J_{f,1} & \dots & J_{f,N_z} & I - * \end{array}\right], - * \end{align} @f] - * where @f$ \dot{J}_i @f$ denotes the Jacobian with respect to @f$ \dot{y}@f$. Note that the - * @f$ J_{i,f} @f$ matrices in the right column are missing. - * - * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. - * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] - * which is 0 for algebraic equations (@f$ -\frac{\partial^2 F}{\partial t \partial p}@f$, to be more precise). - * - * The linear system is solved by backsubstitution. First, the diagonal blocks are solved in parallel. - * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  4. - *
                                  - * This function requires the parameter sensitivities to be computed beforehand and up-to-date Jacobians. - * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) - * @param [in] simState Consistent state of the simulation (state vector and its time derivative) - * @param [in,out] vecSensY Sensitivity subsystem state vectors - * @param [in,out] vecSensYdot Time derivative state vectors of the sensitivity subsystems to be initialized - * @param [in] adRes Pointer to residual vector of AD datatypes with parameter sensitivities - * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) - */ - void LumpedRateModelWithPoresDG::consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) + Indexer idxr(_disc); + + for (std::size_t param = 0; param < vecSensY.size(); ++param) + { + double* const sensY = vecSensY[param]; + double* const sensYdot = vecSensYdot[param]; + + // Copy parameter derivative dF / dp from AD and negate it + for (unsigned int i = _disc.nComp; i < numDofs(); ++i) + sensYdot[i] = -adRes[i].getADValue(param); + + // Step 1: Compute quasi-stationary binding model state + for (unsigned int type = 0; type < _disc.nParType; ++type) { - BENCH_SCOPE(_timerConsistentInit); - - Indexer idxr(_disc); - - for (std::size_t param = 0; param < vecSensY.size(); ++param) - { - double* const sensY = vecSensY[param]; - double* const sensYdot = vecSensYdot[param]; - - // Copy parameter derivative dF / dp from AD and negate it - for (unsigned int i = _disc.nComp; i < numDofs(); ++i) - sensYdot[i] = -adRes[i].getADValue(param); - - // Step 1: Compute quasi-stationary binding model state - for (unsigned int type = 0; type < _disc.nParType; ++type) - { - if (!_binding[type]->hasQuasiStationaryReactions()) - continue; - - int const* const qsMask = _binding[type]->reactionQuasiStationarity(); - const linalg::ConstMaskArray mask{ qsMask, static_cast(_disc.strideBound[type]) }; - const int probSize = linalg::numMaskActive(mask); - - #ifdef CADET_PARALLELIZE - BENCH_SCOPE(_timerConsistentInitPar); + if (!_binding[type]->hasQuasiStationaryReactions()) + continue; + + int const* const qsMask = _binding[type]->reactionQuasiStationarity(); + const linalg::ConstMaskArray mask{qsMask, static_cast(_disc.strideBound[type])}; + const int probSize = linalg::numMaskActive(mask); + +#ifdef CADET_PARALLELIZE + BENCH_SCOPE(_timerConsistentInitPar); tbb::parallel_for(std::size_t(0), static_cast(_disc.nPoints), [&](std::size_t pblk) - #else - for (unsigned int pblk = 0; pblk < _disc.nPoints; ++pblk) - #endif +#else + for (unsigned int pblk = 0; pblk < _disc.nPoints; ++pblk) +#endif { - // Reuse memory of band matrix for dense matrix - linalg::DenseMatrixView jacobianMatrix(_globalJacDisc.valuePtr() + _globalJacDisc.outerIndexPtr()[idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ static_cast(pblk) }) - idxr.offsetC()] + pblk * probSize * probSize, nullptr, probSize, probSize); - - //linalg::DenseMatrixView jacobianMatrix(_jacPdisc[type].data() + pblk * probSize * probSize, _jacPdisc[type].pivot() + pblk * probSize, probSize, probSize); - - // Get workspace memory - LinearBufferAllocator tlmAlloc = threadLocalMem.get(); - - BufferedArray rhsBuffer = tlmAlloc.array(probSize); - double* const rhs = static_cast(rhsBuffer); - - BufferedArray rhsUnmaskedBuffer = tlmAlloc.array(idxr.strideParBound(type)); - double* const rhsUnmasked = static_cast(rhsUnmaskedBuffer); - - double* const maskedMultiplier = _tempState + idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ static_cast(pblk) }); - double* const scaleFactors = _tempState + idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ static_cast(pblk) }); - - const int jacRowOffset = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ static_cast(pblk) }) - idxr.offsetC(); - const int jacColOffset = jacRowOffset; - const int localQOffset = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ static_cast(pblk) }) + idxr.strideParLiquid(); - - // Extract subproblem Jacobian from full Jacobian - jacobianMatrix.setAll(0.0); - linalg::copyMatrixSubset(_globalJac, mask, mask, jacRowOffset, jacColOffset, jacobianMatrix); - - // Construct right hand side - linalg::selectVectorSubset(sensYdot + localQOffset, mask, rhs); - - // Zero out masked elements - std::copy_n(sensY + localQOffset - idxr.strideParLiquid(), _disc.nComp + _disc.strideBound[type], maskedMultiplier); - linalg::fillVectorSubset(maskedMultiplier + _disc.nComp, mask, 0.0); - - // Assemble right hand side - Eigen::Map maskedMultiplier_eigen(maskedMultiplier, idxr.strideParBlock(type)); - Eigen::Map rhsUnmasked_eigen(rhsUnmasked, idxr.strideParBlock(type)); - rhsUnmasked_eigen = _globalJac.block(jacRowOffset, jacColOffset, idxr.strideParBlock(type), idxr.strideParBlock(type)) * maskedMultiplier_eigen; - linalg::vectorSubsetAdd(rhsUnmasked, mask, -1.0, 1.0, rhs); - - // Precondition - jacobianMatrix.rowScaleFactors(scaleFactors); - jacobianMatrix.scaleRows(scaleFactors); - - // Solve - jacobianMatrix.factorize(); - jacobianMatrix.solve(scaleFactors, rhs); - - // Write back - linalg::applyVectorSubset(rhs, mask, sensY + localQOffset); + // Reuse memory of band matrix for dense matrix + linalg::DenseMatrixView jacobianMatrix( + _globalJacDisc.valuePtr() + + _globalJacDisc.outerIndexPtr()[idxr.offsetCp(ParticleTypeIndex{type}, + ParticleIndex{static_cast(pblk)}) - + idxr.offsetC()] + + pblk * probSize * probSize, + nullptr, probSize, probSize); + + // linalg::DenseMatrixView jacobianMatrix(_jacPdisc[type].data() + pblk * probSize * probSize, + // _jacPdisc[type].pivot() + pblk * probSize, probSize, probSize); + + // Get workspace memory + LinearBufferAllocator tlmAlloc = threadLocalMem.get(); + + BufferedArray rhsBuffer = tlmAlloc.array(probSize); + double* const rhs = static_cast(rhsBuffer); + + BufferedArray rhsUnmaskedBuffer = tlmAlloc.array(idxr.strideParBound(type)); + double* const rhsUnmasked = static_cast(rhsUnmaskedBuffer); + + double* const maskedMultiplier = + _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); + double* const scaleFactors = + _tempState + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}); + + const int jacRowOffset = + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}) - + idxr.offsetC(); + const int jacColOffset = jacRowOffset; + const int localQOffset = + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{static_cast(pblk)}) + + idxr.strideParLiquid(); + + // Extract subproblem Jacobian from full Jacobian + jacobianMatrix.setAll(0.0); + linalg::copyMatrixSubset(_globalJac, mask, mask, jacRowOffset, jacColOffset, jacobianMatrix); + + // Construct right hand side + linalg::selectVectorSubset(sensYdot + localQOffset, mask, rhs); + + // Zero out masked elements + std::copy_n(sensY + localQOffset - idxr.strideParLiquid(), _disc.nComp + _disc.strideBound[type], + maskedMultiplier); + linalg::fillVectorSubset(maskedMultiplier + _disc.nComp, mask, 0.0); + + // Assemble right hand side + Eigen::Map maskedMultiplier_eigen(maskedMultiplier, idxr.strideParBlock(type)); + Eigen::Map rhsUnmasked_eigen(rhsUnmasked, idxr.strideParBlock(type)); + rhsUnmasked_eigen = + _globalJac.block(jacRowOffset, jacColOffset, idxr.strideParBlock(type), idxr.strideParBlock(type)) * + maskedMultiplier_eigen; + linalg::vectorSubsetAdd(rhsUnmasked, mask, -1.0, 1.0, rhs); + + // Precondition + jacobianMatrix.rowScaleFactors(scaleFactors); + jacobianMatrix.scaleRows(scaleFactors); + + // Solve + jacobianMatrix.factorize(); + jacobianMatrix.solve(scaleFactors, rhs); + + // Write back + linalg::applyVectorSubset(rhs, mask, sensY + localQOffset); } CADET_PARFOR_END; - } - - // Step 2: Compute the correct time derivative of the state vector: Assemble, factorize, and solve diagonal blocks of linear system - - // Compute right hand side by adding -dF / dy * s = -J * s to -dF / dp which is already stored in sensYdot - multiplyWithJacobian(simTime, simState, sensY, -1.0, 1.0, sensYdot); - - // Note that we have correctly negated the right hand side - - // Assemble bulk block - double* vPtr = _globalJacDisc.valuePtr(); - for (int k = 0; k < _globalJacDisc.nonZeros(); k++) { - vPtr[k] = 0.0; - } - _convDispOp.addTimeDerivativeToJacobian(1.0, _globalJacDisc); + } + + // Step 2: Compute the correct time derivative of the state vector: Assemble, factorize, and solve diagonal + // blocks of linear system + + // Compute right hand side by adding -dF / dy * s = -J * s to -dF / dp which is already stored in sensYdot + multiplyWithJacobian(simTime, simState, sensY, -1.0, 1.0, sensYdot); + + // Note that we have correctly negated the right hand side + + // Assemble bulk block + double* vPtr = _globalJacDisc.valuePtr(); + for (int k = 0; k < _globalJacDisc.nonZeros(); k++) + { + vPtr[k] = 0.0; + } + _convDispOp.addTimeDerivativeToJacobian(1.0, _globalJacDisc); - // Process the particle blocks - #ifdef CADET_PARALLELIZE - BENCH_START(_timerConsistentInitPar); + // Process the particle blocks +#ifdef CADET_PARALLELIZE + BENCH_START(_timerConsistentInitPar); tbb::parallel_for(std::size_t(0), static_cast(_disc.nParType), [&](std::size_t type) - #else - for (unsigned int type = 0; type < _disc.nParType; ++type) - #endif +#else + for (unsigned int type = 0; type < _disc.nParType; ++type) +#endif + { + for (unsigned int pblk = 0; pblk < _disc.nPoints; ++pblk) + { + // Assemble + linalg::BandedEigenSparseRowIterator jacPar( + _globalJacDisc, + idxr.offsetCp(ParticleTypeIndex{static_cast(type)}, ParticleIndex{pblk}) - + idxr.offsetC()); + + // Mobile and solid phase + addTimeDerivativeToJacobianParticleBlock(jacPar, idxr, 1.0, type); + // Iterator jac has already been advanced to next shell + + // Overwrite rows corresponding to algebraic equations with the Jacobian and set right hand side to 0 + if (_binding[type]->hasQuasiStationaryReactions()) { - for (unsigned int pblk = 0; pblk < _disc.nPoints; ++pblk) + // Get iterators to beginning of solid phase + linalg::BandedEigenSparseRowIterator jacSolidOrig( + _globalJac, + idxr.offsetCp(ParticleTypeIndex{static_cast(type)}, ParticleIndex{pblk}) - + idxr.offsetC() + idxr.strideParLiquid()); + linalg::BandedEigenSparseRowIterator jacSolid = jacPar - idxr.strideParBound(type); + + int const* const mask = _binding[type]->reactionQuasiStationarity(); + double* const qShellDot = + sensYdot + + idxr.offsetCp(ParticleTypeIndex{static_cast(type)}, ParticleIndex{pblk}) + + idxr.strideParLiquid(); + + // Copy row from original Jacobian and set right hand side + for (int i = 0; i < idxr.strideParBound(type); ++i, ++jacSolid, ++jacSolidOrig) { - // Assemble - linalg::BandedEigenSparseRowIterator jacPar(_globalJacDisc, idxr.offsetCp(ParticleTypeIndex{ static_cast(type) }, ParticleIndex{ pblk }) - idxr.offsetC()); - - // Mobile and solid phase - addTimeDerivativeToJacobianParticleBlock(jacPar, idxr, 1.0, type); - // Iterator jac has already been advanced to next shell - - // Overwrite rows corresponding to algebraic equations with the Jacobian and set right hand side to 0 - if (_binding[type]->hasQuasiStationaryReactions()) - { - // Get iterators to beginning of solid phase - linalg::BandedEigenSparseRowIterator jacSolidOrig(_globalJac, idxr.offsetCp(ParticleTypeIndex{ static_cast(type) }, ParticleIndex{ pblk }) - idxr.offsetC() + idxr.strideParLiquid()); - linalg::BandedEigenSparseRowIterator jacSolid = jacPar - idxr.strideParBound(type); - - int const* const mask = _binding[type]->reactionQuasiStationarity(); - double* const qShellDot = sensYdot + idxr.offsetCp(ParticleTypeIndex{ static_cast(type) }, ParticleIndex{ pblk }) + idxr.strideParLiquid(); - - // Copy row from original Jacobian and set right hand side - for (int i = 0; i < idxr.strideParBound(type); ++i, ++jacSolid, ++jacSolidOrig) - { - if (!mask[i]) - continue; - - jacSolid.copyRowFrom(jacSolidOrig); - - // Right hand side is -\frac{\partial^2 res(t, y, \dot{y})}{\partial p \partial t} - // If the residual is not explicitly depending on time, this expression is 0 - // @todo This is wrong if external functions are used. Take that into account! - qShellDot[i] = 0.0; - } - } + if (!mask[i]) + continue; + + jacSolid.copyRowFrom(jacSolidOrig); + + // Right hand side is -\frac{\partial^2 res(t, y, \dot{y})}{\partial p \partial t} + // If the residual is not explicitly depending on time, this expression is 0 + // @todo This is wrong if external functions are used. Take that into account! + qShellDot[i] = 0.0; } - + } + } + } CADET_PARFOR_END; - + Eigen::Map yDot(sensYdot, numPureDofs()); // Factorize @@ -1137,99 +1242,100 @@ namespace cadet if (cadet_unlikely(_linearSolver->info() != Eigen::Success)) { - LOG(Error) << "Factorize() failed"; + LOG(Error) << "Factorize() failed"; } // Solve yDot.segment(0, numPureDofs()) = _linearSolver->solve(yDot.segment(0, numPureDofs())); if (cadet_unlikely(_linearSolver->info() != Eigen::Success)) { - LOG(Error) << "Solve() failed"; + LOG(Error) << "Solve() failed"; } - #ifdef CADET_PARALLELIZE +#ifdef CADET_PARALLELIZE BENCH_STOP(_timerConsistentInitPar); - #endif - - } - } - - /** - * @brief Computes approximately / partially consistent initial values and time derivatives of sensitivity subsystems - * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, - * the sensitivity system for a parameter @f$ p @f$ reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of this linear DAE, @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ - * have to be consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the time - * derivative \f$ \dot{s}_0 \f$ such that they are consistent. - * - * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized version of it. - * This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ are - * related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them - * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p}. @f$ - *
                                    - *
                                  1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations). - * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  2. - *
                                  3. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. - * However, because of the algebraic equations, we need additional conditions to fully determine - * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the - * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting - * equations are stated below: - * @f[ \begin{align} - * \left[\begin{array}{c|ccc|c} - * \dot{J}_0 & & & & \\ - * \hline - * J_{f,0} & J_{f,1} & \dots & J_{f,N_z} & I - * \end{array}\right], - * \end{align} @f] - * where @f$ \dot{J}_0 @f$ denotes the bulk block Jacobian with respect to @f$ \dot{y}@f$. - * - * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. - * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] - * which is 0 for algebraic equations (@f$ -\frac{\partial^2 F}{\partial t \partial p}@f$, to be more precise). - * - * The linear system is solved by backsubstitution. First, the bulk block is solved. - * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  4. - *
                                  - * This function requires the parameter sensitivities to be computed beforehand and up-to-date Jacobians. - * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) - * @param [in] simState Consistent state of the simulation (state vector and its time derivative) - * @param [in,out] vecSensY Sensitivity subsystem state vectors - * @param [in,out] vecSensYdot Time derivative state vectors of the sensitivity subsystems to be initialized - * @param [in] adRes Pointer to residual vector of AD datatypes with parameter sensitivities - * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) - */ - void LumpedRateModelWithPoresDG::leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) - { - BENCH_SCOPE(_timerConsistentInit); +#endif + } +} + +/** + * @brief Computes approximately / partially consistent initial values and time derivatives of sensitivity subsystems + * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, + * the sensitivity system for a parameter @f$ p @f$ reads + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of this linear DAE, @f$ s_0 = + * \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ have to be + * consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the + * time derivative \f$ \dot{s}_0 \f$ such that they are consistent. + * + * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized + * version of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ + * \dot{s}_0 \f$ are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by + * differentiating them with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = + * \frac{\partial \dot{y}_0}{\partial p}. @f$
                                  1. Keep state and time derivative vectors as they are (i.e., do not + * solve algebraic equations). No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for + * DG).
                                  2. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations + * hold. However, because of the algebraic equations, we need additional conditions to fully determine + * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the + * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting + * equations are stated below: + * @f[ \begin{align} + * \left[\begin{array}{c|ccc|c} + * \dot{J}_0 & & & & \\ + * \hline + * J_{f,0} & J_{f,1} & \dots & J_{f,N_z} & I + * \end{array}\right], + * \end{align} @f] + * where @f$ \dot{J}_0 @f$ denotes the bulk block Jacobian with respect to @f$ \dot{y}@f$. + * + * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. + * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - + * \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] which is 0 for algebraic equations (@f$ -\frac{\partial^2 + * F}{\partial t \partial p}@f$, to be more precise). + * + * The linear system is solved by backsubstitution. First, the bulk block is solved. + * No need to solve for fluxes @f$ j_{f,i} @f$ (since they are not part of the state for DG).
                                  3. + *
                                  + * This function requires the parameter sensitivities to be computed beforehand and up-to-date Jacobians. + * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) + * @param [in] simState Consistent state of the simulation (state vector and its time derivative) + * @param [in,out] vecSensY Sensitivity subsystem state vectors + * @param [in,out] vecSensYdot Time derivative state vectors of the sensitivity subsystems to be initialized + * @param [in] adRes Pointer to residual vector of AD datatypes with parameter sensitivities + * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) + */ +void LumpedRateModelWithPoresDG::leanConsistentInitialSensitivity( + const SimulationTime& simTime, const ConstSimulationState& simState, std::vector& vecSensY, + std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerConsistentInit); - Indexer idxr(_disc); + Indexer idxr(_disc); - for (std::size_t param = 0; param < vecSensY.size(); ++param) - { - double* const sensY = vecSensY[param]; - double* const sensYdot = vecSensYdot[param]; + for (std::size_t param = 0; param < vecSensY.size(); ++param) + { + double* const sensY = vecSensY[param]; + double* const sensYdot = vecSensYdot[param]; - // Copy parameter derivative from AD to tempState and negate it - // We need to use _tempState in order to keep sensYdot unchanged at this point - for (int i = 0; i < idxr.offsetCp(); ++i) - _tempState[i] = -adRes[i].getADValue(param); + // Copy parameter derivative from AD to tempState and negate it + // We need to use _tempState in order to keep sensYdot unchanged at this point + for (int i = 0; i < idxr.offsetCp(); ++i) + _tempState[i] = -adRes[i].getADValue(param); - // Compute the correct time derivative of the state vector, i.e. - // assemble, factorize, and solve diagonal blocks of linear system + // Compute the correct time derivative of the state vector, i.e. + // assemble, factorize, and solve diagonal blocks of linear system - // Compute right hand side by adding -dF / dy * s = -J * s to -dF / dp which is already stored in _tempState - multiplyWithJacobian(simTime, simState, sensY, -1.0, 1.0, _tempState); + // Compute right hand side by adding -dF / dy * s = -J * s to -dF / dp which is already stored in _tempState + multiplyWithJacobian(simTime, simState, sensY, -1.0, 1.0, _tempState); - // Copy relevant parts to sensYdot for use as right hand sides - std::copy(_tempState + idxr.offsetC(), _tempState + idxr.offsetCp(), sensYdot + idxr.offsetC()); + // Copy relevant parts to sensYdot for use as right hand sides + std::copy(_tempState + idxr.offsetC(), _tempState + idxr.offsetCp(), sensYdot + idxr.offsetC()); - // Handle bulk block - solveBulkTimeDerivativeSystem(simTime, sensYdot + idxr.offsetC()); - } - } + // Handle bulk block + solveBulkTimeDerivativeSystem(simTime, sensYdot + idxr.offsetC()); + } +} - } // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/LumpedRateModelWithPoresDG-LinearSolver.cpp b/src/libcadet/model/LumpedRateModelWithPoresDG-LinearSolver.cpp index d515c366c..4ac20b7aa 100644 --- a/src/libcadet/model/LumpedRateModelWithPoresDG-LinearSolver.cpp +++ b/src/libcadet/model/LumpedRateModelWithPoresDG-LinearSolver.cpp @@ -23,11 +23,11 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include - #include +#include +#include - typedef tbb::flow::continue_node< tbb::flow::continue_msg > node_t; - typedef const tbb::flow::continue_msg & msg_t; +typedef tbb::flow::continue_node node_t; +typedef const tbb::flow::continue_msg& msg_t; #endif namespace cadet @@ -38,12 +38,16 @@ namespace model /** * @brief Computes the solution of the linear system involving the system Jacobian - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + b \f] * has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the - * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) \f$, - * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p rhs. + * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) + \f$, + * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p + rhs. * - * The full Jacobian @f$ J = \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) @f$ is given by + * The full Jacobian @f$ J = \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} + \right) @f$ is given by * @f[ \begin{align} J = \left[\begin{array}{c|ccc|c} @@ -91,11 +95,12 @@ namespace model * @param [in] outerTol Error tolerance for the solution of the linear system from outer Newton iteration * @param [in,out] rhs On entry the right hand side of the linear equation system, on exit the solution * @param [in] weight Vector with error weights - * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is evaluated + * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is + evaluated * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ -int LumpedRateModelWithPoresDG::linearSolve(double t, double alpha, double outerTol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) +int LumpedRateModelWithPoresDG::linearSolve(double t, double alpha, double outerTol, double* const rhs, + double const* const weight, const ConstSimulationState& simState) { BENCH_SCOPE(_timerLinearSolve); @@ -114,7 +119,8 @@ int LumpedRateModelWithPoresDG::linearSolve(double t, double alpha, double outer _linearSolver->factorize(_globalJacDisc); - if (cadet_unlikely(_linearSolver->info() != Eigen::Success)) { + if (cadet_unlikely(_linearSolver->info() != Eigen::Success)) + { LOG(Error) << "Factorize() failed"; } @@ -124,15 +130,19 @@ int LumpedRateModelWithPoresDG::linearSolve(double t, double alpha, double outer // ====== Step 1.5: Solve J c_uo = b_uo - A * c_in = b_uo - A*b_in - // rhs is passed twice but due to the values in jacA the writes happen to a different area of the rhs than the reads. + // rhs is passed twice but due to the values in jacA the writes happen to a different area of the rhs than the + // reads. // Handle inlet DOFs: // Inlet at z = 0 for forward flow, at z = L for backward flow. unsigned int offInlet = (_convDispOp.forwardFlow()) ? 0 : (_disc.nCol - 1u) * idxr.strideColCell(); - for (int comp = 0; comp < _disc.nComp; comp++) { - for (int node = 0; node < (_disc.exactInt ? _disc.nNodes : 1); node++) { - r[idxr.offsetC() + offInlet + comp * idxr.strideColComp() + node * idxr.strideColNode()] += _jacInlet(node, 0) * r[comp]; + for (int comp = 0; comp < _disc.nComp; comp++) + { + for (int node = 0; node < (_disc.exactInt ? _disc.nNodes : 1); node++) + { + r[idxr.offsetC() + offInlet + comp * idxr.strideColComp() + node * idxr.strideColNode()] += + _jacInlet(node, 0) * r[comp]; } } @@ -152,20 +162,18 @@ int LumpedRateModelWithPoresDG::linearSolve(double t, double alpha, double outer /** * @brief Assembles bulk Jacobian @f$ J_i @f$ (@f$ i > 0 @f$) of the time-discretized equations - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The system Jacobian of the original equations, - * \f[ \frac{\partial F}{\partial y}, \f] - * is already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible - * for adding - * \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] - * to the system Jacobian, which yields the Jacobian of the time-discretized equations - * \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] - * when a BDF method is used. The time integrator needs to solve this equation for @f$ y_0 @f$, which requires - * the solution of the linear system mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + * b \f] has to be solved. The system Jacobian of the original equations, \f[ \frac{\partial F}{\partial y}, \f] is + * already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible for + * adding \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] to the system Jacobian, which yields the Jacobian of the + * time-discretized equations \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] when a BDF method is used. + * The time integrator needs to solve this equation for @f$ y_0 @f$, which requires the solution of the linear system + * mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). * * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) */ -void LumpedRateModelWithPoresDG::assembleDiscretizedGlobalJacobian(double alpha, Indexer idxr) { +void LumpedRateModelWithPoresDG::assembleDiscretizedGlobalJacobian(double alpha, Indexer idxr) +{ // set to static (per section) jacobian _globalJacDisc = _globalJac; @@ -174,8 +182,10 @@ void LumpedRateModelWithPoresDG::assembleDiscretizedGlobalJacobian(double alpha, _convDispOp.addTimeDerivativeToJacobian(alpha, _globalJacDisc); // Add time derivatives to particle shells - for (unsigned int parType = 0; parType < _disc.nParType; parType++) { - linalg::BandedEigenSparseRowIterator jac(_globalJacDisc, idxr.offsetCp(ParticleTypeIndex{ parType }) - idxr.offsetC()); + for (unsigned int parType = 0; parType < _disc.nParType; parType++) + { + linalg::BandedEigenSparseRowIterator jac(_globalJacDisc, + idxr.offsetCp(ParticleTypeIndex{parType}) - idxr.offsetC()); for (unsigned int j = 0; j < _disc.nPoints; ++j) { // Mobile and solid phase (advances jac accordingly) @@ -194,7 +204,9 @@ void LumpedRateModelWithPoresDG::assembleDiscretizedGlobalJacobian(double alpha, * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) * @param [in] parType Index of the particle type */ -void LumpedRateModelWithPoresDG::addTimeDerivativeToJacobianParticleBlock(linalg::BandedEigenSparseRowIterator& jac, const Indexer& idxr, double alpha, unsigned int parType) +void LumpedRateModelWithPoresDG::addTimeDerivativeToJacobianParticleBlock(linalg::BandedEigenSparseRowIterator& jac, + const Indexer& idxr, double alpha, + unsigned int parType) { // Mobile phase for (int comp = 0; comp < static_cast(_disc.nComp); ++comp, ++jac) @@ -202,7 +214,9 @@ void LumpedRateModelWithPoresDG::addTimeDerivativeToJacobianParticleBlock(linalg // Add derivative with respect to dc_p / dt to Jacobian jac[0] += alpha; - const double invBetaP = (1.0 - static_cast(_parPorosity[parType])) / (static_cast(_poreAccessFactor[parType * _disc.nComp + comp]) * static_cast(_parPorosity[parType])); + const double invBetaP = (1.0 - static_cast(_parPorosity[parType])) / + (static_cast(_poreAccessFactor[parType * _disc.nComp + comp]) * + static_cast(_parPorosity[parType])); // Add derivative with respect to dq / dt to Jacobian const int nBound = static_cast(_disc.nBound[parType * _disc.nComp + comp]); @@ -213,7 +227,9 @@ void LumpedRateModelWithPoresDG::addTimeDerivativeToJacobianParticleBlock(linalg // + strideParLiquid() skip to solid phase // + offsetBoundComp() jump to component (skips all bound states of previous components) // + i go to current bound state - jac[idxr.strideParLiquid() - comp + idxr.offsetBoundComp(ParticleTypeIndex{ parType }, ComponentIndex{ static_cast(comp) }) + i] += alpha * invBetaP; + jac[idxr.strideParLiquid() - comp + + idxr.offsetBoundComp(ParticleTypeIndex{parType}, ComponentIndex{static_cast(comp)}) + + i] += alpha * invBetaP; } } @@ -230,6 +246,6 @@ void LumpedRateModelWithPoresDG::addTimeDerivativeToJacobianParticleBlock(linalg } } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/LumpedRateModelWithPoresDG.cpp b/src/libcadet/model/LumpedRateModelWithPoresDG.cpp index d0e56273d..94653d265 100644 --- a/src/libcadet/model/LumpedRateModelWithPoresDG.cpp +++ b/src/libcadet/model/LumpedRateModelWithPoresDG.cpp @@ -48,11 +48,10 @@ constexpr double SurfVolRatioSphere = 3.0; constexpr double SurfVolRatioCylinder = 2.0; constexpr double SurfVolRatioSlab = 1.0; - -LumpedRateModelWithPoresDG::LumpedRateModelWithPoresDG(UnitOpIdx unitOpIdx) : UnitOperationBase(unitOpIdx), -_dynReactionBulk(nullptr), _globalJac(), _jacInlet(), _analyticJac(true), -_jacobianAdDirs(0), _factorizeJacobian(false), _tempState(nullptr), _initC(0), _initCp(0), _initQ(0), -_initState(0), _initStateDot(0) +LumpedRateModelWithPoresDG::LumpedRateModelWithPoresDG(UnitOpIdx unitOpIdx) + : UnitOperationBase(unitOpIdx), _dynReactionBulk(nullptr), _globalJac(), _jacInlet(), _analyticJac(true), + _jacobianAdDirs(0), _factorizeJacobian(false), _tempState(nullptr), _initC(0), _initCp(0), _initQ(0), + _initState(0), _initStateDot(0) { } @@ -86,7 +85,6 @@ unsigned int LumpedRateModelWithPoresDG::numPureDofs() const CADET_NOEXCEPT return _disc.nComp * _disc.nPoints + _disc.parTypeOffset[_disc.nParType]; } - bool LumpedRateModelWithPoresDG::usesAD() const CADET_NOEXCEPT { #ifdef CADET_CHECK_ANALYTIC_JACOBIAN @@ -98,7 +96,8 @@ bool LumpedRateModelWithPoresDG::usesAD() const CADET_NOEXCEPT #endif } -bool LumpedRateModelWithPoresDG::configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper) +bool LumpedRateModelWithPoresDG::configureModelDiscretization(IParameterProvider& paramProvider, + const IConfigHelper& helper) { // ==== Read discretization _disc.nComp = paramProvider.getInt("NCOMP"); @@ -109,9 +108,11 @@ bool LumpedRateModelWithPoresDG::configureModelDiscretization(IParameterProvider paramProvider.pushScope("discretization"); - _linearSolver = cadet::linalg::setLinearSolver(paramProvider.exists("LINEAR_SOLVER") ? paramProvider.getString("LINEAR_SOLVER") : "SparseLU"); + _linearSolver = cadet::linalg::setLinearSolver( + paramProvider.exists("LINEAR_SOLVER") ? paramProvider.getString("LINEAR_SOLVER") : "SparseLU"); - if (!newNBoundInterface && paramProvider.exists("NBOUND")) // done here and in this order for backwards compatibility + if (!newNBoundInterface && + paramProvider.exists("NBOUND")) // done here and in this order for backwards compatibility nBound = paramProvider.getIntArray("NBOUND"); else { @@ -120,12 +121,15 @@ bool LumpedRateModelWithPoresDG::configureModelDiscretization(IParameterProvider paramProvider.pushScope("discretization"); } if (nBound.size() < _disc.nComp) - throw InvalidParameterException("Field NBOUND contains too few elements (NCOMP = " + std::to_string(_disc.nComp) + " required)"); + throw InvalidParameterException( + "Field NBOUND contains too few elements (NCOMP = " + std::to_string(_disc.nComp) + " required)"); if (nBound.size() % _disc.nComp != 0) - throw InvalidParameterException("Field NBOUND must have a size divisible by NCOMP (" + std::to_string(_disc.nComp) + ")"); + throw InvalidParameterException("Field NBOUND must have a size divisible by NCOMP (" + + std::to_string(_disc.nComp) + ")"); - if (!newNPartypeInterface && paramProvider.exists("NPARTYPE")) // done here and in this order for backwards compatibility + if (!newNPartypeInterface && + paramProvider.exists("NPARTYPE")) // done here and in this order for backwards compatibility { _disc.nParType = paramProvider.getInt("NPARTYPE"); _disc.nBound = new unsigned int[_disc.nComp * _disc.nParType]; @@ -168,7 +172,8 @@ bool LumpedRateModelWithPoresDG::configureModelDiscretization(IParameterProvider if (paramProvider.getInt("POLYDEG") < 1) throw InvalidParameterException("Polynomial degree must be at least 1!"); else if (_disc.polyDeg < 3) - LOG(Warning) << "Polynomial degree > 2 in bulk discretization (cf. POLYDEG) is always recommended for performance reasons."; + LOG(Warning) << "Polynomial degree > 2 in bulk discretization (cf. POLYDEG) is always recommended for " + "performance reasons."; _disc.nNodes = _disc.polyDeg + 1; @@ -187,7 +192,8 @@ bool LumpedRateModelWithPoresDG::configureModelDiscretization(IParameterProvider int polynomial_integration_mode = 0; if (paramProvider.exists("EXACT_INTEGRATION")) polynomial_integration_mode = paramProvider.getInt("EXACT_INTEGRATION"); - _disc.exactInt = static_cast(polynomial_integration_mode); // only integration mode 0 applies the inexact collocated diagonal LGL mass matrix + _disc.exactInt = static_cast( + polynomial_integration_mode); // only integration mode 0 applies the inexact collocated diagonal LGL mass matrix if (paramProvider.exists("NPARTYPE")) { @@ -270,7 +276,8 @@ bool LumpedRateModelWithPoresDG::configureModelDiscretization(IParameterProvider pg.resize(_disc.nParType, pg[0]); } else if (pg.size() < _disc.nParType) - throw InvalidParameterException("Field PAR_GEOM contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field PAR_GEOM contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); for (unsigned int i = 0; i < _disc.nParType; ++i) { @@ -281,14 +288,16 @@ bool LumpedRateModelWithPoresDG::configureModelDiscretization(IParameterProvider else if (pg[i] == "SLAB") _parGeomSurfToVol[i] = SurfVolRatioSlab; else - throw InvalidParameterException("Unknown particle geometry type \"" + pg[i] + "\" at index " + std::to_string(i) + " of field PAR_GEOM"); + throw InvalidParameterException("Unknown particle geometry type \"" + pg[i] + "\" at index " + + std::to_string(i) + " of field PAR_GEOM"); } } paramProvider.popScope(); const unsigned int strideNode = _disc.nComp; - const bool transportSuccess = _convDispOp.configureModelDiscretization(paramProvider, helper, _disc.nComp, polynomial_integration_mode, _disc.nCol, _disc.polyDeg, strideNode); + const bool transportSuccess = _convDispOp.configureModelDiscretization( + paramProvider, helper, _disc.nComp, polynomial_integration_mode, _disc.nCol, _disc.polyDeg, strideNode); _disc.curSection = -1; @@ -303,7 +312,7 @@ bool LumpedRateModelWithPoresDG::configureModelDiscretization(IParameterProvider clearBindingModels(); _binding = std::vector(_disc.nParType, nullptr); - std::vector bindModelNames = { "NONE" }; + std::vector bindModelNames = {"NONE"}; if (paramProvider.exists("ADSORPTION_MODEL")) bindModelNames = paramProvider.getStringArray("ADSORPTION_MODEL"); @@ -316,7 +325,8 @@ bool LumpedRateModelWithPoresDG::configureModelDiscretization(IParameterProvider } if (!_singleBinding && (bindModelNames.size() < _disc.nParType)) - throw InvalidParameterException("Field ADSORPTION_MODEL contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field ADSORPTION_MODEL contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); else if (_singleBinding && (bindModelNames.size() != 1)) throw InvalidParameterException("Field ADSORPTION_MODEL requires (only) 1 element"); @@ -334,8 +344,12 @@ bool LumpedRateModelWithPoresDG::configureModelDiscretization(IParameterProvider if (!_binding[i]) throw InvalidParameterException("Unknown binding model " + bindModelNames[i]); - MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", _singleBinding, i, _disc.nParType == 1, _binding[i]->usesParamProviderInDiscretizationConfig()); - bindingConfSuccess = _binding[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, _disc.boundOffset + i * _disc.nComp) && bindingConfSuccess; + MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", _singleBinding, i, _disc.nParType == 1, + _binding[i]->usesParamProviderInDiscretizationConfig()); + bindingConfSuccess = + _binding[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, + _disc.boundOffset + i * _disc.nComp) && + bindingConfSuccess; } } @@ -353,7 +367,8 @@ bool LumpedRateModelWithPoresDG::configureModelDiscretization(IParameterProvider if (_dynReactionBulk->usesParamProviderInDiscretizationConfig()) paramProvider.pushScope("reaction_bulk"); - reactionConfSuccess = _dynReactionBulk->configureModelDiscretization(paramProvider, _disc.nComp, nullptr, nullptr); + reactionConfSuccess = + _dynReactionBulk->configureModelDiscretization(paramProvider, _disc.nComp, nullptr, nullptr); if (_dynReactionBulk->usesParamProviderInDiscretizationConfig()) paramProvider.popScope(); @@ -375,7 +390,8 @@ bool LumpedRateModelWithPoresDG::configureModelDiscretization(IParameterProvider } if (!_singleDynReaction && (dynReactModelNames.size() < _disc.nParType)) - throw InvalidParameterException("Field REACTION_MODEL_PARTICLES contains too few elements (" + std::to_string(_disc.nParType) + " required)"); + throw InvalidParameterException("Field REACTION_MODEL_PARTICLES contains too few elements (" + + std::to_string(_disc.nParType) + " required)"); else if (_singleDynReaction && (dynReactModelNames.size() != 1)) throw InvalidParameterException("Field REACTION_MODEL_PARTICLES requires (only) 1 element"); @@ -392,8 +408,13 @@ bool LumpedRateModelWithPoresDG::configureModelDiscretization(IParameterProvider if (!_dynReaction[i]) throw InvalidParameterException("Unknown dynamic reaction model " + dynReactModelNames[i]); - MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", _singleDynReaction, i, _disc.nParType == 1, _dynReaction[i]->usesParamProviderInDiscretizationConfig()); - reactionConfSuccess = _dynReaction[i]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, _disc.boundOffset + i * _disc.nComp) && reactionConfSuccess; + MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", _singleDynReaction, i, + _disc.nParType == 1, + _dynReaction[i]->usesParamProviderInDiscretizationConfig()); + reactionConfSuccess = _dynReaction[i]->configureModelDiscretization( + paramProvider, _disc.nComp, _disc.nBound + i * _disc.nComp, + _disc.boundOffset + i * _disc.nComp) && + reactionConfSuccess; } } } @@ -410,8 +431,9 @@ bool LumpedRateModelWithPoresDG::configureModelDiscretization(IParameterProvider _globalJacDisc.resize(numPureDofs(), numPureDofs()); setGlobalJacPattern(_globalJac, _dynReactionBulk); _globalJacDisc = _globalJac; - // the solver repetitively solves the linear system with a static pattern of the jacobian (set above). - // The goal of analyzePattern() is to reorder the nonzero elements of the matrix, such that the factorization step creates less fill-in + // the solver repetitively solves the linear system with a static pattern of the jacobian (set above). + // The goal of analyzePattern() is to reorder the nonzero elements of the matrix, such that the factorization step + // creates less fill-in _linearSolver->analyzePattern(_globalJacDisc); return transportSuccess && bindingConfSuccess && reactionConfSuccess; @@ -425,14 +447,19 @@ bool LumpedRateModelWithPoresDG::configure(IParameterProvider& paramProvider) // Read geometry parameters _colPorosity = paramProvider.getDouble("COL_POROSITY"); - _singleParRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parRadius, "PAR_RADIUS", _disc.nParType, _unitOpIdx); - _singleParPorosity = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parPorosity, "PAR_POROSITY", _disc.nParType, _unitOpIdx); + _singleParRadius = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parRadius, "PAR_RADIUS", + _disc.nParType, _unitOpIdx); + _singleParPorosity = readAndRegisterMultiplexTypeParam(paramProvider, _parameters, _parPorosity, "PAR_POROSITY", + _disc.nParType, _unitOpIdx); // Read vectorial parameters (which may also be section dependent; transport) - _filmDiffusionMode = readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _filmDiffusion, "FILM_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); + _filmDiffusionMode = readAndRegisterMultiplexCompTypeSecParam( + paramProvider, _parameters, _filmDiffusion, "FILM_DIFFUSION", _disc.nParType, _disc.nComp, _unitOpIdx); if (paramProvider.exists("PORE_ACCESSIBILITY")) - _poreAccessFactorMode = readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _poreAccessFactor, "PORE_ACCESSIBILITY", _disc.nParType, _disc.nComp, _unitOpIdx); + _poreAccessFactorMode = + readAndRegisterMultiplexCompTypeSecParam(paramProvider, _parameters, _poreAccessFactor, + "PORE_ACCESSIBILITY", _disc.nParType, _disc.nComp, _unitOpIdx); else { _poreAccessFactorMode = MultiplexMode::ComponentType; @@ -454,7 +481,8 @@ bool LumpedRateModelWithPoresDG::configure(IParameterProvider& paramProvider) // Expand to all axial cells _parTypeVolFrac.resize(_disc.nPoints * _disc.nParType, 1.0); for (unsigned int i = 1; i < _disc.nPoints; ++i) - std::copy(_parTypeVolFrac.begin(), _parTypeVolFrac.begin() + _disc.nParType, _parTypeVolFrac.begin() + _disc.nParType * i); + std::copy(_parTypeVolFrac.begin(), _parTypeVolFrac.begin() + _disc.nParType, + _parTypeVolFrac.begin() + _disc.nParType * i); } else _axiallyConstantParTypeVolFrac = false; @@ -467,49 +495,76 @@ bool LumpedRateModelWithPoresDG::configure(IParameterProvider& paramProvider) // Check whether all sizes are matched if (_disc.nParType != _parRadius.size()) - throw InvalidParameterException("Number of elements in field PAR_RADIUS does not match number of particle types"); + throw InvalidParameterException( + "Number of elements in field PAR_RADIUS does not match number of particle types"); if (_disc.nParType * _disc.nPoints != _parTypeVolFrac.size()) - throw InvalidParameterException("Number of elements in field PAR_TYPE_VOLFRAC does not match number of particle types"); + throw InvalidParameterException( + "Number of elements in field PAR_TYPE_VOLFRAC does not match number of particle types"); if (_disc.nParType != _parPorosity.size()) - throw InvalidParameterException("Number of elements in field PAR_POROSITY does not match number of particle types"); - - if ((_filmDiffusion.size() < _disc.nComp * _disc.nParType) || (_filmDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) - throw InvalidParameterException("Number of elements in field FILM_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + ")"); + throw InvalidParameterException( + "Number of elements in field PAR_POROSITY does not match number of particle types"); + + if ((_filmDiffusion.size() < _disc.nComp * _disc.nParType) || + (_filmDiffusion.size() % (_disc.nComp * _disc.nParType) != 0)) + throw InvalidParameterException( + "Number of elements in field FILM_DIFFUSION is not a positive multiple of NCOMP * NPARTYPE (" + + std::to_string(_disc.nComp * _disc.nParType) + ")"); if (_disc.nComp * _disc.nParType != _poreAccessFactor.size()) - throw InvalidParameterException("Number of elements in field PORE_ACCESSIBILITY differs from NCOMP * NPARTYPE (" + std::to_string(_disc.nComp * _disc.nParType) + ")"); + throw InvalidParameterException( + "Number of elements in field PORE_ACCESSIBILITY differs from NCOMP * NPARTYPE (" + + std::to_string(_disc.nComp * _disc.nParType) + ")"); // Check that particle volume fractions sum to 1.0 for (unsigned int i = 0; i < _disc.nPoints; ++i) { - const double volFracSum = std::accumulate(_parTypeVolFrac.begin() + i * _disc.nParType, _parTypeVolFrac.begin() + (i + 1) * _disc.nParType, 0.0, + const double volFracSum = std::accumulate( + _parTypeVolFrac.begin() + i * _disc.nParType, _parTypeVolFrac.begin() + (i + 1) * _disc.nParType, 0.0, [](double a, const active& b) -> double { return a + static_cast(b); }); if (std::abs(1.0 - volFracSum) > 1e-10) - throw InvalidParameterException("Sum of field PAR_TYPE_VOLFRAC differs from 1.0 (is " + std::to_string(volFracSum) + ") in axial cell " + std::to_string(i)); + throw InvalidParameterException("Sum of field PAR_TYPE_VOLFRAC differs from 1.0 (is " + + std::to_string(volFracSum) + ") in axial cell " + std::to_string(i)); } // Add parameters to map - _parameters[makeParamId(hashString("COL_POROSITY"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_colPorosity; + _parameters[makeParamId(hashString("COL_POROSITY"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_colPorosity; if (_axiallyConstantParTypeVolFrac) { // Register only the first nParType items for (unsigned int i = 0; i < _disc.nParType; ++i) - _parameters[makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, i, BoundStateIndep, ReactionIndep, SectionIndep)] = &_parTypeVolFrac[i]; + _parameters[makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, i, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_parTypeVolFrac[i]; } else - registerParam2DArray(_parameters, _parTypeVolFrac, [=](bool multi, unsigned cell, unsigned int type) { return makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, type, BoundStateIndep, ReactionIndep, cell); }, _disc.nParType); + registerParam2DArray( + _parameters, _parTypeVolFrac, + [=](bool multi, unsigned cell, unsigned int type) { + return makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, type, BoundStateIndep, + ReactionIndep, cell); + }, + _disc.nParType); // Register initial conditions parameters - registerParam1DArray(_parameters, _initC, [=](bool multi, unsigned int comp) { return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep); }); + registerParam1DArray(_parameters, _initC, [=](bool multi, unsigned int comp) { + return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep); + }); if (_singleBinding) { for (unsigned int c = 0; c < _disc.nComp; ++c) - _parameters[makeParamId(hashString("INIT_CP"), _unitOpIdx, c, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_initCp[c]; + _parameters[makeParamId(hashString("INIT_CP"), _unitOpIdx, c, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep)] = &_initCp[c]; } else - registerParam2DArray(_parameters, _initCp, [=](bool multi, unsigned int type, unsigned int comp) { return makeParamId(hashString("INIT_CP"), _unitOpIdx, comp, type, BoundStateIndep, ReactionIndep, SectionIndep); }, _disc.nComp); - + registerParam2DArray( + _parameters, _initCp, + [=](bool multi, unsigned int type, unsigned int comp) { + return makeParamId(hashString("INIT_CP"), _unitOpIdx, comp, type, BoundStateIndep, ReactionIndep, + SectionIndep); + }, + _disc.nComp); if (!_binding.empty()) { @@ -579,7 +634,8 @@ bool LumpedRateModelWithPoresDG::configure(IParameterProvider& paramProvider) if (_dynReaction[0] && _dynReaction[0]->requiresConfiguration()) { MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", true); - dynReactionConfSuccess = _dynReaction[0]->configure(paramProvider, _unitOpIdx, ParTypeIndep) && dynReactionConfSuccess; + dynReactionConfSuccess = + _dynReaction[0]->configure(paramProvider, _unitOpIdx, ParTypeIndep) && dynReactionConfSuccess; } } else @@ -590,7 +646,8 @@ bool LumpedRateModelWithPoresDG::configure(IParameterProvider& paramProvider) continue; MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", type, _disc.nParType == 1, true); - dynReactionConfSuccess = _dynReaction[type]->configure(paramProvider, _unitOpIdx, type) && dynReactionConfSuccess; + dynReactionConfSuccess = + _dynReaction[type]->configure(paramProvider, _unitOpIdx, type) && dynReactionConfSuccess; } } @@ -608,7 +665,8 @@ unsigned int LumpedRateModelWithPoresDG::threadLocalMemorySize() const CADET_NOE lms.fitBlock(_binding[i]->workspaceSize(_disc.nComp, _disc.strideBound[i], _disc.nBound + i * _disc.nComp)); if (_dynReaction[i] && _dynReaction[i]->requiresWorkspace()) - lms.fitBlock(_dynReaction[i]->workspaceSize(_disc.nComp, _disc.strideBound[i], _disc.nBound + i * _disc.nComp)); + lms.fitBlock( + _dynReaction[i]->workspaceSize(_disc.nComp, _disc.strideBound[i], _disc.nBound + i * _disc.nComp)); } if (_dynReactionBulk && _dynReactionBulk->requiresWorkspace()) @@ -642,8 +700,9 @@ unsigned int LumpedRateModelWithPoresDG::threadLocalMemorySize() const CADET_NOE unsigned int LumpedRateModelWithPoresDG::numAdDirsForJacobian() const CADET_NOEXCEPT { - // The global DG Jacobian is banded around the main diagonal and has additional (also banded) entries for film diffusion. - // To feasibly seed and reconstruct the Jacobian, we create dedicated active directions for the bulk and each particle type (see @ todo) + // The global DG Jacobian is banded around the main diagonal and has additional (also banded) entries for film + // diffusion. To feasibly seed and reconstruct the Jacobian, we create dedicated active directions for the bulk and + // each particle type (see @ todo) int sumParBandwidth = 0; for (unsigned int type = 0; type < _disc.nParType; ++type) @@ -669,7 +728,9 @@ void LumpedRateModelWithPoresDG::useAnalyticJacobian(const bool analyticJac) #endif } -void LumpedRateModelWithPoresDG::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) +void LumpedRateModelWithPoresDG::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac) { _disc.curSection = secIdx; _disc.newStaticJac = true; @@ -695,7 +756,6 @@ void LumpedRateModelWithPoresDG::reportSolutionStructure(ISolutionRecorder& reco recorder.unitOperationStructure(_unitOpIdx, *this, expr); } - unsigned int LumpedRateModelWithPoresDG::requiredADdirs() const CADET_NOEXCEPT { const unsigned int numDirsBinding = maxBindingAdDirs(); @@ -715,21 +775,26 @@ void LumpedRateModelWithPoresDG::prepareADvectors(const AdJacobianParams& adJac) Indexer idxr(_disc); - // The global DG Jacobian is banded around the main diagonal and has additional (also banded, but offset) entries for film diffusion, - // i.e. banded AD vector seeding is not sufficient (as it is for the FV Jacobians, see @puttmann2016 and the DG LRM Jacobian). - // The compressed vectorial AD seeding and Jacobian construction is described in the following (as in @todo?). - // The global DG Jacobian is banded around the main diagonal and has additional (also banded) entries for film diffusion. - // To feasibly seed and reconstruct the Jacobian (we need information for decompression), we create dedicated active directions for - // the bulk and each particle type (see @ todo). + // The global DG Jacobian is banded around the main diagonal and has additional (also banded, but offset) entries + // for film diffusion, i.e. banded AD vector seeding is not sufficient (as it is for the FV Jacobians, see + // @puttmann2016 and the DG LRM Jacobian). The compressed vectorial AD seeding and Jacobian construction is + // described in the following (as in @todo?). The global DG Jacobian is banded around the main diagonal and has + // additional (also banded) entries for film diffusion. To feasibly seed and reconstruct the Jacobian (we need + // information for decompression), we create dedicated active directions for the bulk and each particle type (see @ + // todo). // We begin by seeding the (banded around main diagonal) bulk Jacobian block - // We have differing Jacobian structures for exact integration and collocation DG scheme, i.e. we need different seed vectors - // collocation DG: 2 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend on the next and last N_n liquid phase entries of same component) - // ex. int. DG: 4 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend on the next and last 2*N_n liquid phase entries of same component) - const int lowerBandwidth = (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); + // We have differing Jacobian structures for exact integration and collocation DG scheme, i.e. we need different + // seed vectors collocation DG: 2 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend + // on the next and last N_n liquid phase entries of same component) + // ex. int. DG: 4 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend on the next + // and last 2*N_n liquid phase entries of same component) + const int lowerBandwidth = + (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); const int upperBandwidth = lowerBandwidth; const int bulkRows = idxr.offsetCp() - idxr.offsetC(); - ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + _disc.nComp, adJac.adDirOffset, bulkRows, lowerBandwidth, upperBandwidth, lowerBandwidth); + ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + _disc.nComp, adJac.adDirOffset, bulkRows, lowerBandwidth, + upperBandwidth, lowerBandwidth); // We now seed the particle Jacobian blocks using the individual AD directions for each particle type. unsigned int adDirOffset = adJac.adDirOffset + _convDispOp.requiredADdirs(); @@ -738,8 +803,8 @@ void LumpedRateModelWithPoresDG::prepareADvectors(const AdJacobianParams& adJac) { for (unsigned int parBlock = 0; parBlock < _disc.nPoints; parBlock++) { - // move adVec pointer to start of current particle block - active* _adVec = adJac.adY + idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ parBlock }); + // move adVec pointer to start of current particle block + active* _adVec = adJac.adY + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{parBlock}); for (int eq = 0; eq < idxr.strideParBlock(type); ++eq) { @@ -747,7 +812,6 @@ void LumpedRateModelWithPoresDG::prepareADvectors(const AdJacobianParams& adJac) _adVec[eq].fillADValue(adJac.adDirOffset, 0.0); // Set direction _adVec[eq].setADValue(adDirOffset + eq, 1.0); - } } if (type < _disc.nParType - 1u) // move to dedicated DoFs of next particle type @@ -767,14 +831,16 @@ void LumpedRateModelWithPoresDG::extractJacobianFromAD(active const* const adRes const active* const adVec = adRes + idxr.offsetC(); /* Extract bulk phase equations entries */ - const int lowerBandwidth = (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); + const int lowerBandwidth = + (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); const int upperBandwidth = lowerBandwidth; const int stride = lowerBandwidth + 1 + upperBandwidth; int diagDir = lowerBandwidth; const int bulkDoFs = idxr.offsetCp() - idxr.offsetC(); int eqOffset = 0; - ad::extractBandedBlockEigenJacobianFromAd(adVec, adDirOffset, diagDir, lowerBandwidth, upperBandwidth, eqOffset, bulkDoFs, _globalJac); - + ad::extractBandedBlockEigenJacobianFromAd(adVec, adDirOffset, diagDir, lowerBandwidth, upperBandwidth, eqOffset, + bulkDoFs, _globalJac); + /* Handle particle liquid and solid phase equations entries */ // Read particle Jacobian enries from dedicated AD directions int offsetParticleTypeDirs = adDirOffset + _convDispOp.requiredADdirs(); @@ -783,13 +849,14 @@ void LumpedRateModelWithPoresDG::extractJacobianFromAD(active const* const adRes { for (unsigned int par = 0; par < _disc.nPoints; par++) { - const int eqOffset_res = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ par }); - const int eqOffset_mat = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ par }) - idxr.offsetC(); + const int eqOffset_res = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); + const int eqOffset_mat = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}) - idxr.offsetC(); for (unsigned int phase = 0; phase < idxr.strideParBlock(type); phase++) { for (unsigned int phaseTo = 0; phaseTo < idxr.strideParBlock(type); phaseTo++) { - _globalJac.coeffRef(eqOffset_mat + phase, eqOffset_mat + phaseTo) = adRes[eqOffset_res + phase].getADValue(offsetParticleTypeDirs + phaseTo); + _globalJac.coeffRef(eqOffset_mat + phase, eqOffset_mat + phaseTo) = + adRes[eqOffset_res + phase].getADValue(offsetParticleTypeDirs + phaseTo); } } } @@ -798,7 +865,6 @@ void LumpedRateModelWithPoresDG::extractJacobianFromAD(active const* const adRes /* Film diffusion flux entries are handled analytically (only cross dependent entries) */ calcFluxJacobians(_disc.curSection, true); - } #ifdef CADET_CHECK_ANALYTIC_JACOBIAN @@ -809,15 +875,19 @@ void LumpedRateModelWithPoresDG::extractJacobianFromAD(active const* const adRes * @param [in] adRes Residual vector of AD datatypes with band compressed seed vectors * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) */ -void LumpedRateModelWithPoresDG::checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const +void LumpedRateModelWithPoresDG::checkAnalyticJacobianAgainstAd(active const* const adRes, + unsigned int adDirOffset) const { Indexer idxr(_disc); - const int lowerBandwidth = (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); + const int lowerBandwidth = + (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); const int upperBandwidth = lowerBandwidth; const int stride = lowerBandwidth + 1 + upperBandwidth; - const double maxDiff = ad::compareBandedEigenJacobianWithAd(adRes + idxr.offsetC(), adDirOffset, lowerBandwidth, lowerBandwidth, upperBandwidth, 0, numPureDofs(), _globalJac, 0); + const double maxDiff = + ad::compareBandedEigenJacobianWithAd(adRes + idxr.offsetC(), adDirOffset, lowerBandwidth, lowerBandwidth, + upperBandwidth, 0, numPureDofs(), _globalJac, 0); if (maxDiff > 1e-6) int jojo = 0; @@ -826,12 +896,12 @@ void LumpedRateModelWithPoresDG::checkAnalyticJacobianAgainstAd(active const* co for (unsigned int type = 0; type < _disc.nParType; type++) { - int offsetParticleTypeDirs = adDirOffset + idxr.offsetCp(ParticleTypeIndex{ type }) - idxr.offsetC(); + int offsetParticleTypeDirs = adDirOffset + idxr.offsetCp(ParticleTypeIndex{type}) - idxr.offsetC(); for (unsigned int par = 0; par < _disc.nPoints; par++) { - const int eqOffset_res = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ par }); - const int eqOffset_mat = idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ par }) - idxr.offsetC(); + const int eqOffset_res = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}); + const int eqOffset_mat = idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{par}) - idxr.offsetC(); for (unsigned int phase = 0; phase < idxr.strideParBlock(type); phase++) { for (unsigned int phaseTo = 0; phaseTo < idxr.strideParBlock(type); phaseTo++) @@ -850,7 +920,6 @@ void LumpedRateModelWithPoresDG::checkAnalyticJacobianAgainstAd(active const* co maxDiffPar = std::max(maxDiffPar, diff / baseVal); else maxDiffPar = std::max(maxDiffPar, diff); - } } } @@ -860,32 +929,42 @@ void LumpedRateModelWithPoresDG::checkAnalyticJacobianAgainstAd(active const* co if (maxDiffPar > 1e-6) int jojo2 = 0; - LOG(Debug) << "AD dir offset: " << adDirOffset << " DiagBlockSize: " << stride << " MaxDiffBulk: " << maxDiff << " MaxDiffParticle: " << maxDiffPar; + LOG(Debug) << "AD dir offset: " << adDirOffset << " DiagBlockSize: " << stride << " MaxDiffBulk: " << maxDiff + << " MaxDiffParticle: " << maxDiffPar; } #endif -int LumpedRateModelWithPoresDG::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPoresDG::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); _factorizeJacobian = true; if (_analyticJac) - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); else - return residualWithJacobian(simTime, ConstSimulationState{ simState.vecStateY, nullptr }, nullptr, adJac, threadLocalMem); + return residualWithJacobian(simTime, ConstSimulationState{simState.vecStateY, nullptr}, nullptr, adJac, + threadLocalMem); } -int LumpedRateModelWithPoresDG::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPoresDG::residual(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); // Evaluate residual do not compute Jacobian or parameter sensitivities - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } -int LumpedRateModelWithPoresDG::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPoresDG::residualWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); @@ -895,8 +974,10 @@ int LumpedRateModelWithPoresDG::residualWithJacobian(const SimulationTime& simTi return residual(simTime, simState, res, adJac, threadLocalMem, true, false); } -int LumpedRateModelWithPoresDG::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, - const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity) +int LumpedRateModelWithPoresDG::residual(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, + bool paramSensitivity) { if (updateJacobian) { @@ -907,7 +988,8 @@ int LumpedRateModelWithPoresDG::residual(const SimulationTime& simTime, const Co { if (paramSensitivity) { - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -916,7 +998,8 @@ int LumpedRateModelWithPoresDG::residual(const SimulationTime& simTime, const Co return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } else { @@ -931,9 +1014,11 @@ int LumpedRateModelWithPoresDG::residual(const SimulationTime& simTime, const Co // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -956,15 +1041,18 @@ int LumpedRateModelWithPoresDG::residual(const SimulationTime& simTime, const Co // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); // Only do comparison if we have a residuals vector (which is not always the case) if (res) { // Evaluate with analytical Jacobian which is stored in the band matrices - retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); // Compare AD with anaytic Jacobian checkAnalyticJacobianAgainstAd(adJac.adRes, adJac.adDirOffset); @@ -984,7 +1072,8 @@ int LumpedRateModelWithPoresDG::residual(const SimulationTime& simTime, const Co // @todo Check if this is necessary ad::resetAd(adJac.adRes, numDofs()); - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -993,12 +1082,15 @@ int LumpedRateModelWithPoresDG::residual(const SimulationTime& simTime, const Co return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } } template -int LumpedRateModelWithPoresDG::residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPoresDG::residualImpl(double t, unsigned int secIdx, StateType const* const y, + double const* const yDot, ResidualType* const res, + util::ThreadLocalStorage& threadLocalMem) { bool success = 1; @@ -1019,9 +1111,10 @@ int LumpedRateModelWithPoresDG::residualImpl(double t, unsigned int secIdx, Stat for (unsigned int pblk = 0; pblk < _disc.nPoints * _disc.nParType; ++pblk) { - const unsigned int type = (pblk) / _disc.nPoints; - const unsigned int par = (pblk) % _disc.nPoints; - residualParticle(t, type, par, secIdx, y, yDot, res, threadLocalMem); + const unsigned int type = (pblk) / _disc.nPoints; + const unsigned int par = (pblk) % _disc.nPoints; + residualParticle(t, type, par, secIdx, y, yDot, res, + threadLocalMem); } BENCH_STOP(_timerResidualPar); @@ -1041,10 +1134,13 @@ int LumpedRateModelWithPoresDG::residualImpl(double t, unsigned int secIdx, Stat } template -int LumpedRateModelWithPoresDG::residualBulk(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPoresDG::residualBulk(double t, unsigned int secIdx, StateType const* yBase, + double const* yDotBase, ResidualType* resBase, + util::ThreadLocalStorage& threadLocalMem) { if (wantRes) - _convDispOp.residual(*this, t, secIdx, yBase, yDotBase, resBase, typename cadet::ParamSens::enabled()); + _convDispOp.residual(*this, t, secIdx, yBase, yDotBase, resBase, + typename cadet::ParamSens::enabled()); if (!_dynReactionBulk || (_dynReactionBulk->numReactionsLiquid() == 0)) return 0; @@ -1057,11 +1153,12 @@ int LumpedRateModelWithPoresDG::residualBulk(double t, unsigned int secIdx, Stat { for (unsigned int col = 0; col < _disc.nPoints; ++col, y += idxr.strideColNode()) { - const ColumnPosition colPos{ (0.5 + static_cast(col)) / static_cast(_disc.nCol), 0.0, 0.0 }; + const ColumnPosition colPos{(0.5 + static_cast(col)) / static_cast(_disc.nCol), 0.0, 0.0}; linalg::BandedEigenSparseRowIterator jac(_globalJacDisc, col * idxr.strideColNode()); // static_cast should be sufficient here, but this statement is also analyzed when wantJac = false - _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(y), -1.0, jac, tlmAlloc); + _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(y), -1.0, + jac, tlmAlloc); } return 0; @@ -1071,14 +1168,15 @@ int LumpedRateModelWithPoresDG::residualBulk(double t, unsigned int secIdx, Stat for (unsigned int col = 0; col < _disc.nPoints; ++col, y += idxr.strideColNode(), res += idxr.strideColNode()) { - const ColumnPosition colPos{ (0.5 + static_cast(col)) / static_cast(_disc.nCol), 0.0, 0.0 }; + const ColumnPosition colPos{(0.5 + static_cast(col)) / static_cast(_disc.nCol), 0.0, 0.0}; _dynReactionBulk->residualLiquidAdd(t, secIdx, colPos, y, res, -1.0, tlmAlloc); if (wantJac) { linalg::BandedEigenSparseRowIterator jac(_globalJacDisc, col * idxr.strideColNode()); // static_cast should be sufficient here, but this statement is also analyzed when wantJac = false - _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(y), -1.0, jac, tlmAlloc); + _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(y), -1.0, + jac, tlmAlloc); } } @@ -1086,12 +1184,14 @@ int LumpedRateModelWithPoresDG::residualBulk(double t, unsigned int secIdx, Stat } template -int LumpedRateModelWithPoresDG::residualParticle(double t, unsigned int parType, unsigned int colNode, unsigned int secIdx, StateType const* yBase, double const* yDotBase, ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPoresDG::residualParticle(double t, unsigned int parType, unsigned int colNode, + unsigned int secIdx, StateType const* yBase, double const* yDotBase, + ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem) { Indexer idxr(_disc); // Go to the particle block of the given type and column cell - StateType const* y = yBase + idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }); + StateType const* y = yBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}); // Prepare parameters const ParamType radius = static_cast(_parRadius[parType]); @@ -1099,8 +1199,7 @@ int LumpedRateModelWithPoresDG::residualParticle(double t, unsigned int parType, // z coordinate (column length normed to 1) of current node - needed in externally dependent adsorption kinetic const double z = _convDispOp.relativeCoordinate(colNode); - const parts::cell::CellParameters cellResParams - { + const parts::cell::CellParameters cellResParams{ _disc.nComp, _disc.nBound + _disc.nComp * parType, _disc.boundOffset + _disc.nComp * parType, @@ -1109,35 +1208,38 @@ int LumpedRateModelWithPoresDG::residualParticle(double t, unsigned int parType, _parPorosity[parType], _poreAccessFactor.data() + _disc.nComp * parType, _binding[parType], - (_dynReaction[parType] && (_dynReaction[parType]->numReactionsCombined() > 0)) ? _dynReaction[parType] : nullptr - }; + (_dynReaction[parType] && (_dynReaction[parType]->numReactionsCombined() > 0)) ? _dynReaction[parType] + : nullptr}; - linalg::BandedEigenSparseRowIterator jac(_globalJac, idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }) - idxr.offsetC()); + linalg::BandedEigenSparseRowIterator jac( + _globalJac, idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}) - idxr.offsetC()); // Handle time derivatives, binding, dynamic reactions if (wantRes) { - double const* yDot = yDotBase ? yDotBase + idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }) : nullptr; - ResidualType* res = resBase + idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ colNode }); - - parts::cell::residualKernel( - t, secIdx, ColumnPosition{ z, 0.0, static_cast(radius) * 0.5 }, y, yDot, res, - jac, cellResParams, threadLocalMem.get() - ); + double const* yDot = + yDotBase ? yDotBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}) : nullptr; + ResidualType* res = resBase + idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{colNode}); + + parts::cell::residualKernel( + t, secIdx, ColumnPosition{z, 0.0, static_cast(radius) * 0.5}, y, yDot, res, jac, cellResParams, + threadLocalMem.get()); } else { - parts::cell::residualKernel( - t, secIdx, ColumnPosition{ z, 0.0, static_cast(radius) * 0.5 }, y, nullptr, nullptr, - jac, cellResParams, threadLocalMem.get() - ); + parts::cell::residualKernel( + t, secIdx, ColumnPosition{z, 0.0, static_cast(radius) * 0.5}, y, nullptr, nullptr, jac, + cellResParams, threadLocalMem.get()); } return 0; } template -int LumpedRateModelWithPoresDG::residualFlux(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, ResidualType* resBase) +int LumpedRateModelWithPoresDG::residualFlux(double t, unsigned int secIdx, StateType const* yBase, + double const* yDotBase, ResidualType* resBase) { Indexer idxr(_disc); @@ -1149,12 +1251,13 @@ int LumpedRateModelWithPoresDG::residualFlux(double t, unsigned int secIdx, Stat for (unsigned int type = 0; type < _disc.nParType; ++type) { - ResidualType* const resParType = resBase + idxr.offsetCp(ParticleTypeIndex{ type }); - StateType const* const yParType = yBase + idxr.offsetCp(ParticleTypeIndex{ type }); + ResidualType* const resParType = resBase + idxr.offsetCp(ParticleTypeIndex{type}); + StateType const* const yParType = yBase + idxr.offsetCp(ParticleTypeIndex{type}); const ParamType epsP = static_cast(_parPorosity[type]); const ParamType radius = static_cast(_parRadius[type]); - active const* const filmDiff = getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const filmDiff = + getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; active const* const poreAccFactor = _poreAccessFactor.data() + type * _disc.nComp; const ParamType jacCF_val = invBetaC * _parGeomSurfToVol[type] / radius; @@ -1165,7 +1268,9 @@ int LumpedRateModelWithPoresDG::residualFlux(double t, unsigned int secIdx, Stat { const unsigned int colNode = i / _disc.nComp; const unsigned int comp = i % _disc.nComp; - resCol[i] += jacCF_val * static_cast(filmDiff[comp]) * static_cast(_parTypeVolFrac[type + _disc.nParType * colNode]) * (yCol[i] - yParType[colNode * idxr.strideParBlock(type) + comp]); + resCol[i] += jacCF_val * static_cast(filmDiff[comp]) * + static_cast(_parTypeVolFrac[type + _disc.nParType * colNode]) * + (yCol[i] - yParType[colNode * idxr.strideParBlock(type) + comp]); } // Add flux to particle / bead volume equations @@ -1174,7 +1279,9 @@ int LumpedRateModelWithPoresDG::residualFlux(double t, unsigned int secIdx, Stat for (unsigned int comp = 0; comp < _disc.nComp; ++comp) { const unsigned int eq = pblk * idxr.strideColNode() + comp * idxr.strideColComp(); - resParType[pblk * idxr.strideParBlock(type) + comp] += jacPF_val / static_cast(poreAccFactor[comp]) * static_cast(filmDiff[comp]) * (yCol[eq] - yParType[pblk * idxr.strideParBlock(type) + comp]); + resParType[pblk * idxr.strideParBlock(type) + comp] += + jacPF_val / static_cast(poreAccFactor[comp]) * static_cast(filmDiff[comp]) * + (yCol[eq] - yParType[pblk * idxr.strideParBlock(type) + comp]); } } } @@ -1187,8 +1294,10 @@ void LumpedRateModelWithPoresDG::assembleFluxJacobian(double t, unsigned int sec calcFluxJacobians(secIdx); } -int LumpedRateModelWithPoresDG::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, - const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPoresDG::residualSensFwdWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidualSens); @@ -1197,17 +1306,23 @@ int LumpedRateModelWithPoresDG::residualSensFwdWithJacobian(const SimulationTime return residual(simTime, simState, nullptr, adJac, threadLocalMem, true, true); } -int LumpedRateModelWithPoresDG::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithPoresDG::residualSensFwdAdOnly(const SimulationTime& simTime, + const ConstSimulationState& simState, active* const adRes, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidualSens); // Evaluate residual for all parameters using AD in vector mode - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adRes, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, adRes, threadLocalMem); } -int LumpedRateModelWithPoresDG::residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3) +int LumpedRateModelWithPoresDG::residualSensFwdCombine(const SimulationTime& simTime, + const ConstSimulationState& simState, + const std::vector& yS, + const std::vector& ySdot, + const std::vector& resS, active const* adRes, + double* const tmp1, double* const tmp2, double* const tmp3) { BENCH_SCOPE(_timerResidualSens); @@ -1217,10 +1332,12 @@ int LumpedRateModelWithPoresDG::residualSensFwdCombine(const SimulationTime& sim for (std::size_t param = 0; param < yS.size(); ++param) { // Directional derivative (dF / dy) * s - multiplyWithJacobian(SimulationTime{ 0.0, 0u }, ConstSimulationState{ nullptr, nullptr }, yS[param], 1.0, 0.0, tmp1); + multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, yS[param], 1.0, 0.0, + tmp1); // Directional derivative (dF / dyDot) * sDot - multiplyWithDerivativeJacobian(SimulationTime{ 0.0, 0u }, ConstSimulationState{ nullptr, nullptr }, ySdot[param], tmp2); + multiplyWithDerivativeJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, ySdot[param], + tmp2); double* const ptrResS = resS[param]; @@ -1244,11 +1361,12 @@ int LumpedRateModelWithPoresDG::residualSensFwdCombine(const SimulationTime& sim } /** - * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, \dot{y}\right) @f$) + * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, + * \dot{y}\right) @f$) * @details Actually, the operation @f$ z = \alpha \frac{\partial F}{\partial y} x + \beta z @f$ is performed. * - * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ once - * before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. + * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ + * once before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. * @param [in] simTime Current simulation time point * @param [in] simState Simulation state vectors * @param [in] yS Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial y} @f$ @@ -1256,7 +1374,9 @@ int LumpedRateModelWithPoresDG::residualSensFwdCombine(const SimulationTime& sim * @param [in] beta Factor @f$ \beta @f$ in front of @f$ z @f$ * @param [in,out] ret Vector @f$ z @f$ which stores the result of the operation */ -void LumpedRateModelWithPoresDG::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret) +void LumpedRateModelWithPoresDG::multiplyWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, double const* yS, + double alpha, double beta, double* ret) { Indexer idxr(_disc); @@ -1275,15 +1395,19 @@ void LumpedRateModelWithPoresDG::multiplyWithJacobian(const SimulationTime& simT // Inlet at z = 0 for forward flow, at z = L for backward flow. unsigned int offInlet = _convDispOp.forwardFlow() ? 0 : (_disc.nCol - 1u) * idxr.strideColCell(); - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int node = 0; node < (_disc.exactInt ? _disc.nNodes : 1); node++) { - ret[idxr.offsetC() + offInlet + comp * idxr.strideColComp() + node * idxr.strideColNode()] += alpha * _jacInlet(node, 0) * yS[comp]; + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int node = 0; node < (_disc.exactInt ? _disc.nNodes : 1); node++) + { + ret[idxr.offsetC() + offInlet + comp * idxr.strideColComp() + node * idxr.strideColNode()] += + alpha * _jacInlet(node, 0) * yS[comp]; } } } /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ + * with a given vector * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). * @param [in] simTime Current simulation time point @@ -1291,7 +1415,9 @@ void LumpedRateModelWithPoresDG::multiplyWithJacobian(const SimulationTime& simT * @param [in] sDot Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ * @param [out] ret Vector @f$ z @f$ which stores the result of the operation */ -void LumpedRateModelWithPoresDG::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret) +void LumpedRateModelWithPoresDG::multiplyWithDerivativeJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + double const* sDot, double* ret) { Indexer idxr(_disc); @@ -1312,8 +1438,8 @@ void LumpedRateModelWithPoresDG::multiplyWithDerivativeJacobian(const Simulation const unsigned int type = idxParLoop / _disc.nPoints; // Particle - double const* const localSdot = sDot + idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ pblk }); - double* const localRet = ret + idxr.offsetCp(ParticleTypeIndex{ type }, ParticleIndex{ pblk }); + double const* const localSdot = sDot + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}); + double* const localRet = ret + idxr.offsetCp(ParticleTypeIndex{type}, ParticleIndex{pblk}); unsigned int const* const nBound = _disc.nBound + type * _disc.nComp; unsigned int const* const boundOffset = _disc.boundOffset + type * _disc.nComp; @@ -1324,7 +1450,9 @@ void LumpedRateModelWithPoresDG::multiplyWithDerivativeJacobian(const Simulation // Add derivative with respect to dc_p / dt to Jacobian localRet[comp] = localSdot[comp]; - const double invBetaP = (1.0 - static_cast(_parPorosity[type])) / (static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * static_cast(_parPorosity[type])); + const double invBetaP = (1.0 - static_cast(_parPorosity[type])) / + (static_cast(_poreAccessFactor[type * _disc.nComp + comp]) * + static_cast(_parPorosity[type])); // Add derivative with respect to dq / dt to Jacobian (normal equations) for (unsigned int i = 0; i < nBound[comp]; ++i) @@ -1404,7 +1532,8 @@ bool LumpedRateModelWithPoresDG::setParameter(const ParameterId& pId, double val // Intercept changes to PAR_TYPE_VOLFRAC when not specified per axial cell (but once globally) if (_axiallyConstantParTypeVolFrac && (pId.name == hashString("PAR_TYPE_VOLFRAC"))) { - if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep)) + if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep)) return false; if (pId.particleType >= _disc.nParType) return false; @@ -1417,12 +1546,15 @@ bool LumpedRateModelWithPoresDG::setParameter(const ParameterId& pId, double val if (multiplexTypeParameterValue(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, value, nullptr)) return true; - if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, nullptr)) + if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, + nullptr)) return true; - if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, _disc.nParType, _disc.nComp, value, nullptr)) + if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, + _disc.nParType, _disc.nComp, value, nullptr)) return true; - if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, _poreAccessFactor, _disc.nParType, _disc.nComp, value, nullptr)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, + _poreAccessFactor, _disc.nParType, _disc.nComp, value, nullptr)) return true; const int mpIc = multiplexInitialConditions(pId, value, false); @@ -1445,7 +1577,8 @@ void LumpedRateModelWithPoresDG::setSensitiveParameterValue(const ParameterId& p // Intercept changes to PAR_TYPE_VOLFRAC when not specified per axial cell (but once globally) if (_axiallyConstantParTypeVolFrac && (pId.name == hashString("PAR_TYPE_VOLFRAC"))) { - if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep)) + if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep)) return; if (pId.particleType >= _disc.nParType) return; @@ -1459,14 +1592,18 @@ void LumpedRateModelWithPoresDG::setSensitiveParameterValue(const ParameterId& p return; } - if (multiplexTypeParameterValue(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, value, &_sensParams)) + if (multiplexTypeParameterValue(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, value, + &_sensParams)) return; - if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, &_sensParams)) + if (multiplexTypeParameterValue(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, value, + &_sensParams)) return; - if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, _disc.nParType, _disc.nComp, value, &_sensParams)) + if (multiplexCompTypeSecParameterValue(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, + _disc.nParType, _disc.nComp, value, &_sensParams)) return; - if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, _poreAccessFactor, _disc.nParType, _disc.nComp, value, &_sensParams)) + if (multiplexCompTypeSecParameterValue(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, + _poreAccessFactor, _disc.nParType, _disc.nComp, value, &_sensParams)) return; if (multiplexInitialConditions(pId, value, true) != 0) return; @@ -1485,7 +1622,8 @@ bool LumpedRateModelWithPoresDG::setSensitiveParameter(const ParameterId& pId, u // Intercept changes to PAR_TYPE_VOLFRAC when not specified per axial cell (but once globally) if (_axiallyConstantParTypeVolFrac && (pId.name == hashString("PAR_TYPE_VOLFRAC"))) { - if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep)) + if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep)) return false; if (pId.particleType >= _disc.nParType) return false; @@ -1500,25 +1638,30 @@ bool LumpedRateModelWithPoresDG::setSensitiveParameter(const ParameterId& pId, u return true; } - if (multiplexTypeParameterAD(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, adDirection, adValue, _sensParams)) + if (multiplexTypeParameterAD(pId, hashString("PAR_RADIUS"), _singleParRadius, _parRadius, adDirection, adValue, + _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexTypeParameterAD(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, adDirection, adValue, _sensParams)) + if (multiplexTypeParameterAD(pId, hashString("PAR_POROSITY"), _singleParPorosity, _parPorosity, adDirection, + adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexCompTypeSecParameterAD(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) + if (multiplexCompTypeSecParameterAD(pId, hashString("FILM_DIFFUSION"), _filmDiffusionMode, _filmDiffusion, + _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; } - if (multiplexCompTypeSecParameterAD(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, _poreAccessFactor, _disc.nParType, _disc.nComp, adDirection, adValue, _sensParams)) + if (multiplexCompTypeSecParameterAD(pId, hashString("PORE_ACCESSIBILITY"), _poreAccessFactorMode, + _poreAccessFactor, _disc.nParType, _disc.nComp, adDirection, adValue, + _sensParams)) { LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; return true; @@ -1579,7 +1722,7 @@ int LumpedRateModelWithPoresDG::Exporter::writeSolidPhase(unsigned int parType, cadet_assert(parType < _disc.nParType); const unsigned int stride = _disc.nComp + _disc.strideBound[parType]; - double const* ptr = _data + _idx.offsetCp(ParticleTypeIndex{ parType }) + _idx.strideParLiquid(); + double const* ptr = _data + _idx.offsetCp(ParticleTypeIndex{parType}) + _idx.strideParLiquid(); for (unsigned int i = 0; i < _disc.nPoints; ++i) { std::copy_n(ptr, _disc.strideBound[parType], buffer); @@ -1594,7 +1737,7 @@ int LumpedRateModelWithPoresDG::Exporter::writeParticleMobilePhase(unsigned int cadet_assert(parType < _disc.nParType); const unsigned int stride = _disc.nComp + _disc.strideBound[parType]; - double const* ptr = _data + _idx.offsetCp(ParticleTypeIndex{ parType }); + double const* ptr = _data + _idx.offsetCp(ParticleTypeIndex{parType}); for (unsigned int i = 0; i < _disc.nPoints; ++i) { std::copy_n(ptr, _disc.nComp, buffer); @@ -1639,9 +1782,9 @@ int LumpedRateModelWithPoresDG::Exporter::writeOutlet(double* buffer) const return _disc.nComp; } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet #include "model/LumpedRateModelWithPoresDG-InitialConditions.cpp" #include "model/LumpedRateModelWithPoresDG-LinearSolver.cpp" diff --git a/src/libcadet/model/LumpedRateModelWithPoresDG.hpp b/src/libcadet/model/LumpedRateModelWithPoresDG.hpp index 1fdccda8c..90488a1d0 100644 --- a/src/libcadet/model/LumpedRateModelWithPoresDG.hpp +++ b/src/libcadet/model/LumpedRateModelWithPoresDG.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -54,10 +54,11 @@ class IDynamicReactionModel; * @details See @cite Guiochon2006, @cite Gu1995, @cite Felinger2004 * * @f[\begin{align} - \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 c_i}{\partial z^2} - \frac{1 - \varepsilon_c}{\varepsilon_c} \frac{3 k_{f,i}}{r_p} j_{f,i} \\ - \frac{\partial c_{p,i}}{\partial t} + \frac{1 - \varepsilon_p}{\varepsilon_p} \frac{\partial q_{i}}{\partial t} &= \frac{3 k_{f,i}}{\varepsilon_p r_p} j_{f,i} \\ - a \frac{\partial q_i}{\partial t} &= f_{\text{iso}}(c_p, q) -\end{align} @f] + \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 +c_i}{\partial z^2} - \frac{1 - \varepsilon_c}{\varepsilon_c} \frac{3 k_{f,i}}{r_p} j_{f,i} \\ + \frac{\partial c_{p,i}}{\partial t} + \frac{1 - \varepsilon_p}{\varepsilon_p} \frac{\partial q_{i}}{\partial t} &= +\frac{3 k_{f,i}}{\varepsilon_p r_p} j_{f,i} \\ a \frac{\partial q_i}{\partial t} &= f_{\text{iso}}(c_p, q) \end{align} +@f] @f[ \begin{align} j_{f,i} = c_i - c_{p,i} \end{align} @f] @@ -66,12 +67,12 @@ class IDynamicReactionModel; u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partial z}(t,0) \\ \frac{\partial c_i}{\partial z}(t,L) &= 0 \end{align} @f] - * Methods are described in @cite Breuer2023 (DGSEM discretization), @cite Puttmann2013 @cite Puttmann2016 (forward sensitivities, AD, band compression) + * Methods are described in @cite Breuer2023 (DGSEM discretization), @cite Puttmann2013 @cite Puttmann2016 (forward +sensitivities, AD, band compression) */ class LumpedRateModelWithPoresDG : public UnitOperationBase { public: - LumpedRateModelWithPoresDG(UnitOpIdx unitOpIdx); virtual ~LumpedRateModelWithPoresDG() CADET_NOEXCEPT; @@ -80,60 +81,105 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase virtual bool usesAD() const CADET_NOEXCEPT; virtual unsigned int requiredADdirs() const CADET_NOEXCEPT; - virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT { return _unitOpIdx; } - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } + virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT + { + return _unitOpIdx; + } + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } virtual void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT; - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual bool canAccumulate() const CADET_NOEXCEPT { return false; } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual bool canAccumulate() const CADET_NOEXCEPT + { + return false; + } - static const char* identifier() { return "LUMPED_RATE_MODEL_WITH_PORES_DG"; } - virtual const char* unitOperationName() const CADET_NOEXCEPT { return identifier(); } + static const char* identifier() + { + return "LUMPED_RATE_MODEL_WITH_PORES_DG"; + } + virtual const char* unitOperationName() const CADET_NOEXCEPT + { + return identifier(); + } virtual bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper); virtual bool configure(IParameterProvider& paramProvider); - virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac); + virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac); virtual void useAnalyticJacobian(const bool analyticJac); virtual void reportSolution(ISolutionRecorder& recorder, double const* const solution) const; virtual void reportSolutionStructure(ISolutionRecorder& recorder) const; - virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem); + virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem); - virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3); + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3); virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState); + const ConstSimulationState& simState); virtual void prepareADvectors(const AdJacobianParams& adJac) const; virtual void applyInitialCondition(const SimulationState& simState) const; virtual void readInitialCondition(IParameterProvider& paramProvider); - virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); void solveBulkTimeDerivativeSystem(const SimulationTime& simTime, double* const rhs); virtual void initializeSensitivityStates(const std::vector& vecSensY) const; virtual void consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem); virtual void leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual bool hasInlet() const CADET_NOEXCEPT { return true; } - virtual bool hasOutlet() const CADET_NOEXCEPT { return true; } + virtual bool hasInlet() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasOutlet() const CADET_NOEXCEPT + { + return true; + } virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT; virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT; @@ -141,14 +187,19 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT; virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size); - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { } + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) + { + } virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut); - virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret); - virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret); + virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret); + virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* sDot, double* ret); - inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double* ret) + inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double* ret) { multiplyWithJacobian(simTime, simState, yS, 1.0, 0.0, ret); } @@ -175,43 +226,37 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase _timerFactorize.totalElapsedTime(), _timerFactorizePar.totalElapsedTime(), _timerMatVec.totalElapsedTime(), - }); + }); } virtual char const* const* benchmarkDescriptions() const { static const char* const desc[] = { - "DOFs", - "Residual", - "ResidualPar", - "ResidualSens", - "ResidualSensPar", - "JacobianPar", - "ConsistentInit", - "ConsistentInitPar", - "LinearSolve", - "Factorize", - "FactorizePar", - "MatVec", + "DOFs", "Residual", "ResidualPar", "ResidualSens", "ResidualSensPar", "JacobianPar", + "ConsistentInit", "ConsistentInitPar", "LinearSolve", "Factorize", "FactorizePar", "MatVec", }; return desc; } #endif protected: - class Indexer; - int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity); + int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, + bool paramSensitivity); template - int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); + int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, + ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); template - int residualBulk(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem); + int residualBulk(double t, unsigned int secIdx, StateType const* yBase, double const* yDotBase, + ResidualType* resBase, util::ThreadLocalStorage& threadLocalMem); template - int residualParticle(double t, unsigned int parType, unsigned int colCell, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, util::ThreadLocalStorage& threadLocalMem); + int residualParticle(double t, unsigned int parType, unsigned int colCell, unsigned int secIdx, StateType const* y, + double const* yDot, ResidualType* res, util::ThreadLocalStorage& threadLocalMem); template int residualFlux(double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res); @@ -221,7 +266,8 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase void assembleDiscretizedGlobalJacobian(double alpha, Indexer idxr); - void addTimeDerivativeToJacobianParticleBlock(linalg::BandedEigenSparseRowIterator& jac, const Indexer& idxr, double alpha, unsigned int parType); + void addTimeDerivativeToJacobianParticleBlock(linalg::BandedEigenSparseRowIterator& jac, const Indexer& idxr, + double alpha, unsigned int parType); unsigned int numAdDirsForJacobian() const CADET_NOEXCEPT; @@ -235,29 +281,34 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase class Discretization { public: - unsigned int nParType; //!< Number of particle types - unsigned int* parTypeOffset; //!< Array with offsets (in particle block) to particle type, additional last element contains total number of particle DOFs - unsigned int* nBound; //!< Array with number of bound states for each component and particle type (particle type major ordering) - unsigned int* boundOffset; //!< Array with offset to the first bound state of each component in the solid phase (particle type major ordering) - unsigned int* strideBound; //!< Total number of bound states for each particle type, additional last element contains total number of bound states for all types - unsigned int* nBoundBeforeType; //!< Array with number of bound states before a particle type (cumulative sum of strideBound) - unsigned int nComp; //!< Number of components - - bool exactInt; //!< 1 for exact integration, 0 for LGL quadrature - unsigned int nCol; //!< Number of column cells + unsigned int nParType; //!< Number of particle types + unsigned int* parTypeOffset; //!< Array with offsets (in particle block) to particle type, additional last + //!< element contains total number of particle DOFs + unsigned int* nBound; //!< Array with number of bound states for each component and particle type (particle type + //!< major ordering) + unsigned int* boundOffset; //!< Array with offset to the first bound state of each component in the solid phase + //!< (particle type major ordering) + unsigned int* strideBound; //!< Total number of bound states for each particle type, additional last element + //!< contains total number of bound states for all types + unsigned int* nBoundBeforeType; //!< Array with number of bound states before a particle type (cumulative sum of + //!< strideBound) + unsigned int nComp; //!< Number of components + + bool exactInt; //!< 1 for exact integration, 0 for LGL quadrature + unsigned int nCol; //!< Number of column cells unsigned int polyDeg; //!< polynomial degree - unsigned int nNodes; //!< Number of nodes per cell + unsigned int nNodes; //!< Number of nodes per cell unsigned int nPoints; //!< Number of discrete Points - bool curSection; //!< current section index + bool curSection; //!< current section index bool newStaticJac; //!< determines wether static analytical jacobian needs to be computed (every section) - }; Discretization _disc; //!< Discretization info -// IExternalFunction* _extFun; //!< External function (owned by library user) + // IExternalFunction* _extFun; //!< External function (owned by library user) - parts::AxialConvectionDispersionOperatorBaseDG _convDispOp; //!< Convection dispersion operator for interstitial volume transport + parts::AxialConvectionDispersionOperatorBaseDG + _convDispOp; //!< Convection dispersion operator for interstitial volume transport IDynamicReactionModel* _dynReactionBulk; //!< Dynamic reactions in the bulk volume cadet::linalg::EigenSolverBase* _linearSolver; //!< Linear solver @@ -265,13 +316,14 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase Eigen::MatrixXd _jacInlet; //!< Jacobian inlet DOF block matrix connects inlet DOFs to first bulk cells // for FV the bulk jacobians are defined in the ConvDisp operator. - Eigen::SparseMatrix _globalJac; //!< global Jacobian + Eigen::SparseMatrix _globalJac; //!< global Jacobian Eigen::SparseMatrix _globalJacDisc; //!< global Jacobian with time derivatove from BDF method - //Eigen::MatrixXd _FDjac; //!< test purpose FD jacobian + // Eigen::MatrixXd _FDjac; //!< test purpose FD jacobian - active _colPorosity; //!< Column porosity (external porosity) \f$ \varepsilon_c \f$ - std::vector _parGeomSurfToVol; //!< Particle surface to volume ratio factor (i.e., 3.0 for spherical, 2.0 for cylindrical, 1.0 for hexahedral) - std::vector _parRadius; //!< Particle radius \f$ r_p \f$ + active _colPorosity; //!< Column porosity (external porosity) \f$ \varepsilon_c \f$ + std::vector _parGeomSurfToVol; //!< Particle surface to volume ratio factor (i.e., 3.0 for spherical, 2.0 + //!< for cylindrical, 1.0 for hexahedral) + std::vector _parRadius; //!< Particle radius \f$ r_p \f$ bool _singleParRadius; std::vector _parPorosity; //!< Particle porosity (internal porosity) \f$ \varepsilon_p \f$ bool _singleParPorosity; @@ -283,66 +335,130 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase std::vector _poreAccessFactor; //!< Pore accessibility factor \f$ F_{\text{acc}} \f$ MultiplexMode _poreAccessFactorMode; - bool _axiallyConstantParTypeVolFrac; //!< Determines whether particle type volume fraction is homogeneous across axial coordinate - bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used - unsigned int _jacobianAdDirs; //!< Number of AD seed vectors required for Jacobian computation + bool _axiallyConstantParTypeVolFrac; //!< Determines whether particle type volume fraction is homogeneous across + //!< axial coordinate + bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used + unsigned int _jacobianAdDirs; //!< Number of AD seed vectors required for Jacobian computation bool _factorizeJacobian; //!< Determines whether the Jacobian needs to be factorized double* _tempState; //!< Temporary storage with the size of the state vector or larger if binding models require it - std::vector _initC; //!< Liquid bulk phase initial conditions - std::vector _initCp; //!< Liquid particle phase initial conditions - std::vector _initQ; //!< Solid phase initial conditions - std::vector _initState; //!< Initial conditions for state vector if given + std::vector _initC; //!< Liquid bulk phase initial conditions + std::vector _initCp; //!< Liquid particle phase initial conditions + std::vector _initQ; //!< Solid phase initial conditions + std::vector _initState; //!< Initial conditions for state vector if given std::vector _initStateDot; //!< Initial conditions for time derivative BENCH_TIMER(_timerResidual) - BENCH_TIMER(_timerResidualPar) - BENCH_TIMER(_timerResidualSens) - BENCH_TIMER(_timerResidualSensPar) - BENCH_TIMER(_timerJacobianPar) - BENCH_TIMER(_timerConsistentInit) - BENCH_TIMER(_timerConsistentInitPar) - BENCH_TIMER(_timerLinearSolve) - BENCH_TIMER(_timerFactorize) - BENCH_TIMER(_timerFactorizePar) - BENCH_TIMER(_timerMatVec) + BENCH_TIMER(_timerResidualPar) + BENCH_TIMER(_timerResidualSens) + BENCH_TIMER(_timerResidualSensPar) + BENCH_TIMER(_timerJacobianPar) + BENCH_TIMER(_timerConsistentInit) + BENCH_TIMER(_timerConsistentInitPar) + BENCH_TIMER(_timerLinearSolve) + BENCH_TIMER(_timerFactorize) + BENCH_TIMER(_timerFactorizePar) + BENCH_TIMER(_timerMatVec) class Indexer { public: - Indexer(const Discretization& disc) : _disc(disc) { } + Indexer(const Discretization& disc) : _disc(disc) + { + } // Strides - inline int strideColNode() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideColCell() const CADET_NOEXCEPT { return static_cast(_disc.nNodes * strideColNode()); } - inline int strideColComp() const CADET_NOEXCEPT { return 1; } + inline int strideColNode() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideColCell() const CADET_NOEXCEPT + { + return static_cast(_disc.nNodes * strideColNode()); + } + inline int strideColComp() const CADET_NOEXCEPT + { + return 1; + } - inline int strideParComp() const CADET_NOEXCEPT { return 1; } - inline int strideParLiquid() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideParBound(int parType) const CADET_NOEXCEPT { return static_cast(_disc.strideBound[parType]); } - inline int strideParBlock(int parType) const CADET_NOEXCEPT { return strideParLiquid() + strideParBound(parType); } + inline int strideParComp() const CADET_NOEXCEPT + { + return 1; + } + inline int strideParLiquid() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideParBound(int parType) const CADET_NOEXCEPT + { + return static_cast(_disc.strideBound[parType]); + } + inline int strideParBlock(int parType) const CADET_NOEXCEPT + { + return strideParLiquid() + strideParBound(parType); + } // Offsets - inline int offsetC() const CADET_NOEXCEPT { return _disc.nComp; } - inline int offsetCp() const CADET_NOEXCEPT { return _disc.nComp * _disc.nPoints + offsetC(); } - inline int offsetCp(ParticleTypeIndex pti) const CADET_NOEXCEPT { return offsetCp() + _disc.parTypeOffset[pti.value]; } - inline int offsetCp(ParticleTypeIndex pti, ParticleIndex pi) const CADET_NOEXCEPT { return offsetCp(pti) + strideParBlock(pti.value) * pi.value; } - inline int offsetBoundComp(ParticleTypeIndex pti, ComponentIndex comp) const CADET_NOEXCEPT { return _disc.boundOffset[pti.value * _disc.nComp + comp.value]; } + inline int offsetC() const CADET_NOEXCEPT + { + return _disc.nComp; + } + inline int offsetCp() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nPoints + offsetC(); + } + inline int offsetCp(ParticleTypeIndex pti) const CADET_NOEXCEPT + { + return offsetCp() + _disc.parTypeOffset[pti.value]; + } + inline int offsetCp(ParticleTypeIndex pti, ParticleIndex pi) const CADET_NOEXCEPT + { + return offsetCp(pti) + strideParBlock(pti.value) * pi.value; + } + inline int offsetBoundComp(ParticleTypeIndex pti, ComponentIndex comp) const CADET_NOEXCEPT + { + return _disc.boundOffset[pti.value * _disc.nComp + comp.value]; + } // Return pointer to first element of state variable in state vector - template inline real_t* c(real_t* const data) const { return data + offsetC(); } - template inline real_t const* c(real_t const* const data) const { return data + offsetC(); } + template inline real_t* c(real_t* const data) const + { + return data + offsetC(); + } + template inline real_t const* c(real_t const* const data) const + { + return data + offsetC(); + } - template inline real_t* cp(real_t* const data) const { return data + offsetCp(); } - template inline real_t const* cp(real_t const* const data) const { return data + offsetCp(); } + template inline real_t* cp(real_t* const data) const + { + return data + offsetCp(); + } + template inline real_t const* cp(real_t const* const data) const + { + return data + offsetCp(); + } - template inline real_t* q(real_t* const data) const { return data + offsetCp() + strideParLiquid(); } - template inline real_t const* q(real_t const* const data) const { return data + offsetCp() + strideParLiquid(); } + template inline real_t* q(real_t* const data) const + { + return data + offsetCp() + strideParLiquid(); + } + template inline real_t const* q(real_t const* const data) const + { + return data + offsetCp() + strideParLiquid(); + } // Return specific variable in state vector - template inline real_t& c(real_t* const data, unsigned int point, unsigned int comp) const { return data[offsetC() + comp + point * strideColNode()]; } - template inline const real_t& c(real_t const* const data, unsigned int point, unsigned int comp) const { return data[offsetC() + comp + point * strideColNode()]; } + template inline real_t& c(real_t* const data, unsigned int point, unsigned int comp) const + { + return data[offsetC() + comp + point * strideColNode()]; + } + template + inline const real_t& c(real_t const* const data, unsigned int point, unsigned int comp) const + { + return data[offsetC() + comp + point * strideColNode()]; + } protected: const Discretization& _disc; @@ -351,64 +467,146 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase class Exporter : public ISolutionExporter { public: - - Exporter(const Discretization& disc, const LumpedRateModelWithPoresDG& model, double const* data) : _disc(disc), _idx(disc), _model(model), _data(data) { } + Exporter(const Discretization& disc, const LumpedRateModelWithPoresDG& model, double const* data) + : _disc(disc), _idx(disc), _model(model), _data(data) + { + } Exporter(const Discretization&& disc, const LumpedRateModelWithPoresDG& model, double const* data) = delete; - virtual bool hasParticleFlux() const CADET_NOEXCEPT { return false; } - virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT { return true; } - virtual bool hasSolidPhase() const CADET_NOEXCEPT { return _disc.strideBound[_disc.nParType] > 0; } - virtual bool hasVolume() const CADET_NOEXCEPT { return false; } - virtual bool isParticleLumped() const CADET_NOEXCEPT { return true; } - virtual bool hasPrimaryExtent() const CADET_NOEXCEPT { return true; } - - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } - virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT { return _disc.nPoints; } - virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numParticleTypes() const CADET_NOEXCEPT { return _disc.nParType; } - virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT { return 1; } - virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT { return _disc.strideBound[parType]; } - virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT { return _disc.nComp * _disc.nPoints; } - virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return _disc.nComp * _disc.nPoints; } - virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT { return _disc.nComp * _disc.nPoints * _disc.nParType; } - virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return _disc.strideBound[parType] * _disc.nPoints; } - virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT { + virtual bool hasParticleFlux() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasSolidPhase() const CADET_NOEXCEPT + { + return _disc.strideBound[_disc.nParType] > 0; + } + virtual bool hasVolume() const CADET_NOEXCEPT + { + return false; + } + virtual bool isParticleLumped() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasPrimaryExtent() const CADET_NOEXCEPT + { + return true; + } + + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } + virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT + { + return _disc.nPoints; + } + virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numParticleTypes() const CADET_NOEXCEPT + { + return _disc.nParType; + } + virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.strideBound[parType]; + } + virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nPoints; + } + virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nPoints; + } + virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nPoints * _disc.nParType; + } + virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.strideBound[parType] * _disc.nPoints; + } + virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT + { unsigned int nDofPerParType = 0; for (unsigned int i = 0; i < _disc.nParType; ++i) nDofPerParType += _disc.strideBound[i]; return _disc.nPoints * nDofPerParType; } - virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT { return 0; } + virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT + { + return 0; + } virtual int writeMobilePhase(double* buffer) const; virtual int writeSolidPhase(double* buffer) const; virtual int writeParticleMobilePhase(double* buffer) const; virtual int writeSolidPhase(unsigned int parType, double* buffer) const; virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const; - virtual int writeParticleFlux(double* buffer) const { return 0; } - virtual int writeParticleFlux(unsigned int parType, double* buffer) const { return 0; } - virtual int writeVolume(double* buffer) const { return 0; } + virtual int writeParticleFlux(double* buffer) const + { + return 0; + } + virtual int writeParticleFlux(unsigned int parType, double* buffer) const + { + return 0; + } + virtual int writeVolume(double* buffer) const + { + return 0; + } virtual int writeInlet(unsigned int port, double* buffer) const; virtual int writeInlet(double* buffer) const; virtual int writeOutlet(unsigned int port, double* buffer) const; virtual int writeOutlet(double* buffer) const; /** - * @brief calculates and writes the physical node coordinates of the DG discretization with double! interface nodes - */ + * @brief calculates and writes the physical node coordinates of the DG discretization with double! interface + * nodes + */ virtual int writePrimaryCoordinates(double* coords) const { - for (unsigned int i = 0; i < _disc.nCol; i++) { - for (unsigned int j = 0; j < _disc.nNodes; j++) { - // mapping - coords[i * _disc.nNodes + j] = _model._convDispOp.cellLeftBound(i) + 0.5 * (static_cast(_model._convDispOp.columnLength()) / static_cast(_disc.nCol)) * (1.0 + _model._convDispOp.LGLnodes()[j]); + for (unsigned int i = 0; i < _disc.nCol; i++) + { + for (unsigned int j = 0; j < _disc.nNodes; j++) + { + // mapping + coords[i * _disc.nNodes + j] = + _model._convDispOp.cellLeftBound(i) + + 0.5 * + (static_cast(_model._convDispOp.columnLength()) / static_cast(_disc.nCol)) * + (1.0 + _model._convDispOp.LGLnodes()[j]); } } return _disc.nPoints; } - virtual int writeSecondaryCoordinates(double* coords) const { return 0; } + virtual int writeSecondaryCoordinates(double* coords) const + { + return 0; + } virtual int writeParticleCoordinates(unsigned int parType, double* coords) const { coords[0] = static_cast(_model._parRadius[parType]) * 0.5; @@ -422,16 +620,20 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase double const* const _data; }; - // ========================================================================================================================================================== // - // ======================================== DG Jacobian ========================================================= // - // ========================================================================================================================================================== // + // ========================================================================================================================================================== + // // + // ======================================== DG Jacobian + // ========================================================= // + // ========================================================================================================================================================== + // // typedef Eigen::Triplet T; /** - * @brief sets the sparsity pattern of the convection dispersion Jacobian - */ - void setGlobalJacPattern(Eigen::SparseMatrix& mat, const bool hasBulkReaction) { + * @brief sets the sparsity pattern of the convection dispersion Jacobian + */ + void setGlobalJacPattern(Eigen::SparseMatrix& mat, const bool hasBulkReaction) + { std::vector tripletList; @@ -447,21 +649,24 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase fluxPattern(tripletList); mat.setFromTriplets(tripletList.begin(), tripletList.end()); - } /** * @brief sets the sparsity pattern of the bulkreaction Jacobian pattern */ - int bulkReactionPattern(std::vector& tripletList) { + int bulkReactionPattern(std::vector& tripletList) + { Indexer idxr(_disc); - for (unsigned int blk = 0; blk < _disc.nPoints; blk++) { - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int toComp = 0; toComp < _disc.nComp; toComp++) { + for (unsigned int blk = 0; blk < _disc.nPoints; blk++) + { + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int toComp = 0; toComp < _disc.nComp; toComp++) + { tripletList.push_back(T(idxr.offsetC() + blk * idxr.strideColNode() + comp * idxr.strideColComp(), - idxr.offsetC() + blk * idxr.strideColNode() + toComp * idxr.strideColComp(), - 0.0)); + idxr.offsetC() + blk * idxr.strideColNode() + toComp * idxr.strideColComp(), + 0.0)); } } } @@ -470,18 +675,25 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase /** * @brief sets the sparsity pattern of the particle Jacobian pattern (isotherm, reaction pattern) */ - int particlePattern(std::vector& tripletList) { + int particlePattern(std::vector& tripletList) + { Indexer idxr(_disc); - for (unsigned int parType = 0; parType < _disc.nParType; parType++) { - for (unsigned int nCol = 0; nCol < _disc.nPoints; nCol++) { + for (unsigned int parType = 0; parType < _disc.nParType; parType++) + { + for (unsigned int nCol = 0; nCol < _disc.nPoints; nCol++) + { - int offset = idxr.offsetCp(ParticleTypeIndex{ parType }, ParticleIndex{ nCol }) - idxr.offsetC(); // inlet DOFs not included in Jacobian + int offset = idxr.offsetCp(ParticleTypeIndex{parType}, ParticleIndex{nCol}) - + idxr.offsetC(); // inlet DOFs not included in Jacobian // add dense nComp * nBound blocks, since all solid and liquid entries can be coupled through binding. - for (unsigned int parState = 0; parState < _disc.nComp + _disc.strideBound[parType]; parState++) { - for (unsigned int toParState = 0; toParState < _disc.nComp + _disc.strideBound[parType]; toParState++) { + for (unsigned int parState = 0; parState < _disc.nComp + _disc.strideBound[parType]; parState++) + { + for (unsigned int toParState = 0; toParState < _disc.nComp + _disc.strideBound[parType]; + toParState++) + { tripletList.push_back(T(offset + parState, offset + toParState, 0.0)); } } @@ -493,22 +705,29 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase /** * @brief sets the sparsity pattern of the flux Jacobian pattern */ - int fluxPattern(std::vector& tripletList) { - + int fluxPattern(std::vector& tripletList) + { + Indexer idxr(_disc); - for (unsigned int parType = 0; parType < _disc.nParType; parType++) { - + for (unsigned int parType = 0; parType < _disc.nParType; parType++) + { + int offC = 0; // inlet DOFs not included in Jacobian - int offP = idxr.offsetCp(ParticleTypeIndex{ parType }) - idxr.offsetC(); // inlet DOFs not included in Jacobian + int offP = + idxr.offsetCp(ParticleTypeIndex{parType}) - idxr.offsetC(); // inlet DOFs not included in Jacobian // add dependency of c^b, c^p and flux on another - for (unsigned int nCol = 0; nCol < _disc.nPoints; nCol++) { - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { + for (unsigned int nCol = 0; nCol < _disc.nPoints; nCol++) + { + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { // c^b on c^b entry already set - tripletList.push_back(T(offC + nCol * _disc.nComp + comp, offP + nCol * idxr.strideParBlock(parType) + comp, 0.0)); // c^b on c^p + tripletList.push_back(T(offC + nCol * _disc.nComp + comp, + offP + nCol * idxr.strideParBlock(parType) + comp, 0.0)); // c^b on c^p // c^p on c^p entry already set - tripletList.push_back(T(offP + nCol * idxr.strideParBlock(parType) + comp, offC + nCol * _disc.nComp + comp, 0.0)); // c^p on c^b + tripletList.push_back(T(offP + nCol * idxr.strideParBlock(parType) + comp, + offC + nCol * _disc.nComp + comp, 0.0)); // c^p on c^b } } } @@ -518,21 +737,24 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase * @brief analytically calculates the static (per section) bulk jacobian (inlet DOFs included!) * @return 1 if jacobain estimation fits the predefined pattern of the jacobian, 0 if not. */ - int calcStaticAnaGlobalJacobian(const unsigned int secIdx) { - + int calcStaticAnaGlobalJacobian(const unsigned int secIdx) + { + bool success = _convDispOp.calcStaticAnaJacobian(_globalJac, _jacInlet); success = success && calcFluxJacobians(secIdx); return success; } - int calcFluxJacobians(const unsigned int secIdx, const bool crossDepsOnly = false) { + int calcFluxJacobians(const unsigned int secIdx, const bool crossDepsOnly = false) + { Indexer idxr(_disc); const double invBetaC = 1.0 / static_cast(_colPorosity) - 1.0; - for (unsigned int type = 0; type < _disc.nParType; type++) { + for (unsigned int type = 0; type < _disc.nParType; type++) + { const double epsP = static_cast(_parPorosity[type]); const double radius = static_cast(_parRadius[type]); @@ -542,35 +764,45 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase // Ordering of diffusion: // sec0type0comp0, sec0type0comp1, sec0type0comp2, sec0type1comp0, sec0type1comp1, sec0type1comp2, // sec1type0comp0, sec1type0comp1, sec1type0comp2, sec1type1comp0, sec1type1comp1, sec1type1comp2, ... - active const* const filmDiff = getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; + active const* const filmDiff = + getSectionDependentSlice(_filmDiffusion, _disc.nComp * _disc.nParType, secIdx) + type * _disc.nComp; active const* const poreAccFactor = _poreAccessFactor.data() + type * _disc.nComp; linalg::BandedEigenSparseRowIterator jacC(_globalJac, 0); - linalg::BandedEigenSparseRowIterator jacP(_globalJac, idxr.offsetCp(ParticleTypeIndex{ type }) - idxr.offsetC()); + linalg::BandedEigenSparseRowIterator jacP(_globalJac, + idxr.offsetCp(ParticleTypeIndex{type}) - idxr.offsetC()); for (unsigned int colNode = 0; colNode < _disc.nPoints; colNode++, jacP += _disc.strideBound[type]) { - for (unsigned int comp = 0; comp < _disc.nComp; comp++, ++jacC, ++jacP) { + for (unsigned int comp = 0; comp < _disc.nComp; comp++, ++jacC, ++jacP) + { // add Cl on Cl entries (added since already set in bulk jacobian) // row: already at bulk phase. already at current node and component. // col: already at bulk phase. already at current node and component. - if(!crossDepsOnly) - jacC[0] += jacCF_val * static_cast(filmDiff[comp]) * static_cast(_parTypeVolFrac[type + _disc.nParType * colNode]); + if (!crossDepsOnly) + jacC[0] += jacCF_val * static_cast(filmDiff[comp]) * + static_cast(_parTypeVolFrac[type + _disc.nParType * colNode]); // add Cl on Cp entries // row: already at bulk phase. already at current node and component. // col: already at bulk phase. already at current node and component. - jacC[jacP.row() - jacC.row()] = -jacCF_val * static_cast(filmDiff[comp]) * static_cast(_parTypeVolFrac[type + _disc.nParType * colNode]); + jacC[jacP.row() - jacC.row()] = + -jacCF_val * static_cast(filmDiff[comp]) * + static_cast(_parTypeVolFrac[type + _disc.nParType * colNode]); // add Cp on Cp entries // row: already at particle. already at current node and liquid state. - // col: go to flux of current parType and adjust for offsetC. jump over previous colNodes and add component offset + // col: go to flux of current parType and adjust for offsetC. jump over previous colNodes and add + // component offset if (!crossDepsOnly) - jacP[0] = -jacPF_val / static_cast(poreAccFactor[comp]) * static_cast(filmDiff[comp]); + jacP[0] = + -jacPF_val / static_cast(poreAccFactor[comp]) * static_cast(filmDiff[comp]); // add Cp on Cl entries // row: already at particle. already at current node and liquid state. - // col: go to flux of current parType and adjust for offsetC. jump over previous colNodes and add component offset - jacP[jacC.row() - jacP.row()] = jacPF_val / static_cast(poreAccFactor[comp]) * static_cast(filmDiff[comp]); + // col: go to flux of current parType and adjust for offsetC. jump over previous colNodes and add + // component offset + jacP[jacC.row() - jacP.row()] = + jacPF_val / static_cast(poreAccFactor[comp]) * static_cast(filmDiff[comp]); } } } @@ -580,7 +812,8 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase * @brief calculates the number of entris for the DG convection dispersion jacobian * @note only dispersion entries are relevant for jacobian NNZ as the convection entries are a subset of these */ - unsigned int nJacEntries(const bool hasBulkReaction, const bool pureNNZ = false) { + unsigned int nJacEntries(const bool hasBulkReaction, const bool pureNNZ = false) + { unsigned int nEntries = 0; // Convection dispersion @@ -588,7 +821,8 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase // Bulk reaction entries if (hasBulkReaction) - nEntries += _disc.nPoints * _disc.nComp * _disc.nComp; // add nComp entries for every component at each discrete bulk point + nEntries += _disc.nPoints * _disc.nComp * + _disc.nComp; // add nComp entries for every component at each discrete bulk point // Particle binding and reaction entries for (unsigned int type = 0; type < _disc.nParType; type++) @@ -598,7 +832,8 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase } // testing purpose - MatrixXd calcFDJacobian(const SimulationTime simTime, util::ThreadLocalStorage& threadLocalMem, double alpha) { + MatrixXd calcFDJacobian(const SimulationTime simTime, util::ThreadLocalStorage& threadLocalMem, double alpha) + { // create solution vectors VectorXd y = VectorXd::Zero(numDofs()); @@ -614,40 +849,44 @@ class LumpedRateModelWithPoresDG : public UnitOperationBase residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, threadLocalMem); - for (int col = 0; col < Jacobian.cols(); col++) { + for (int col = 0; col < Jacobian.cols(); col++) + { Jacobian.col(col) = -(1.0 + alpha) * res; } /* Residual(y+h) */ // state DOFs - for (int dof = 0; dof < Jacobian.cols(); dof++) { + for (int dof = 0; dof < Jacobian.cols(); dof++) + { y[dof] += epsilon; - residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, threadLocalMem); + residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, + threadLocalMem); y[dof] -= epsilon; Jacobian.col(dof) += res; } // state derivative Jacobian - for (int dof = 0; dof < Jacobian.cols(); dof++) { + for (int dof = 0; dof < Jacobian.cols(); dof++) + { yDot[dof] += epsilon; - residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, threadLocalMem); + residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, + threadLocalMem); yDot[dof] -= epsilon; Jacobian.col(dof) += alpha * res; } ///* exterminate numerical noise and divide by epsilon*/ - //for (int i = 0; i < Jacobian.rows(); i++) { + // for (int i = 0; i < Jacobian.rows(); i++) { // for (int j = 0; j < Jacobian.cols(); j++) { // if (std::abs(Jacobian(i, j)) < 1e-10) Jacobian(i, j) = 0.0; // } - //} + // } Jacobian /= epsilon; return Jacobian; } - }; } // namespace model } // namespace cadet -#endif // LIBCADET_LUMPEDRATEMODELWITHPORESDG_HPP_ +#endif // LIBCADET_LUMPEDRATEMODELWITHPORESDG_HPP_ diff --git a/src/libcadet/model/LumpedRateModelWithoutPores.cpp b/src/libcadet/model/LumpedRateModelWithoutPores.cpp index 258741a0c..bb6a230db 100644 --- a/src/libcadet/model/LumpedRateModelWithoutPores.cpp +++ b/src/libcadet/model/LumpedRateModelWithoutPores.cpp @@ -36,74 +36,84 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include +#include #endif namespace { - template - class ConvOpJacobian +template class ConvOpJacobian +{ +public: + static inline void call(cadet::IModel* const model, ConvDispOperator& op, double t, unsigned int secIdx, + StateType const* const y, double const* const yDot, ResidualType* const res, + cadet::linalg::BandMatrix& jac) { - public: - static inline void call(cadet::IModel* const model, ConvDispOperator& op, double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, cadet::linalg::BandMatrix& jac) - { - // This should not be reached - cadet_assert(false); - } - }; + // This should not be reached + cadet_assert(false); + } +}; - template - class ConvOpJacobian +template class ConvOpJacobian +{ +public: + static inline void call(cadet::IModel* const model, ConvDispOperator& op, double t, unsigned int secIdx, + double const* const y, double const* const yDot, ResidualType* const res, + cadet::linalg::BandMatrix& jac) { - public: - static inline void call(cadet::IModel* const model, ConvDispOperator& op, double t, unsigned int secIdx, double const* const y, double const* const yDot, ResidualType* const res, cadet::linalg::BandMatrix& jac) - { - //op.residual(*model, t, secIdx, y, yDot, res, jac); - op.jacobian(*model, t, secIdx, y, yDot, res, jac); - } - }; + // op.residual(*model, t, secIdx, y, yDot, res, jac); + op.jacobian(*model, t, secIdx, y, yDot, res, jac); + } +}; - template - class ConvOpResidual +template +class ConvOpResidual +{ +public: + static inline void call(cadet::IModel* const model, ConvDispOperator& op, double t, unsigned int secIdx, + StateType const* const y, double const* const yDot, ResidualType* const res, + cadet::linalg::BandMatrix& jac) { - public: - static inline void call(cadet::IModel* const model, ConvDispOperator& op, double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, cadet::linalg::BandMatrix& jac) - { - // This should not be reached - cadet_assert(false); - } - }; + // This should not be reached + cadet_assert(false); + } +}; - template - class ConvOpResidual +template +class ConvOpResidual +{ +public: + static inline void call(cadet::IModel* const model, ConvDispOperator& op, double t, unsigned int secIdx, + double const* const y, double const* const yDot, ResidualType* const res, + cadet::linalg::BandMatrix& jac) { - public: - static inline void call(cadet::IModel* const model, ConvDispOperator& op, double t, unsigned int secIdx, double const* const y, double const* const yDot, ResidualType* const res, cadet::linalg::BandMatrix& jac) - { - op.residual(*model, t, secIdx, y, yDot, res, jac); - } - }; + op.residual(*model, t, secIdx, y, yDot, res, jac); + } +}; - template - class ConvOpResidual +template +class ConvOpResidual +{ +public: + static inline void call(cadet::IModel* const model, ConvDispOperator& op, double t, unsigned int secIdx, + double const* const y, double const* const yDot, ResidualType* const res, + cadet::linalg::BandMatrix& jac) { - public: - static inline void call(cadet::IModel* const model, ConvDispOperator& op, double t, unsigned int secIdx, double const* const y, double const* const yDot, ResidualType* const res, cadet::linalg::BandMatrix& jac) - { - op.residual(*model, t, secIdx, y, yDot, res, typename cadet::ParamSens::enabled()); - } - }; + op.residual(*model, t, secIdx, y, yDot, res, typename cadet::ParamSens::enabled()); + } +}; - template - class ConvOpResidual +template +class ConvOpResidual +{ +public: + static inline void call(cadet::IModel* const model, ConvDispOperator& op, double t, unsigned int secIdx, + cadet::active const* const y, double const* const yDot, ResidualType* const res, + cadet::linalg::BandMatrix& jac) { - public: - static inline void call(cadet::IModel* const model, ConvDispOperator& op, double t, unsigned int secIdx, cadet::active const* const y, double const* const yDot, ResidualType* const res, cadet::linalg::BandMatrix& jac) - { - op.residual(*model, t, secIdx, y, yDot, res, typename cadet::ParamSens::enabled()); - } - }; -} + op.residual(*model, t, secIdx, y, yDot, res, typename cadet::ParamSens::enabled()); + } +}; +} // namespace namespace cadet { @@ -112,9 +122,9 @@ namespace model { template -LumpedRateModelWithoutPores::LumpedRateModelWithoutPores(UnitOpIdx unitOpIdx) : UnitOperationBase(unitOpIdx), - _jacInlet(), _analyticJac(true), _jacobianAdDirs(0), _factorizeJacobian(false), _tempState(nullptr), _initC(0), - _initQ(0), _initState(0), _initStateDot(0) +LumpedRateModelWithoutPores::LumpedRateModelWithoutPores(UnitOpIdx unitOpIdx) + : UnitOperationBase(unitOpIdx), _jacInlet(), _analyticJac(true), _jacobianAdDirs(0), _factorizeJacobian(false), + _tempState(nullptr), _initC(0), _initQ(0), _initState(0), _initStateDot(0) { // Multiple particle types are not supported _singleBinding = true; @@ -145,9 +155,7 @@ unsigned int LumpedRateModelWithoutPores::numPureDofs() const return _disc.nCol * (_disc.nComp + _disc.strideBound); } - -template -bool LumpedRateModelWithoutPores::usesAD() const CADET_NOEXCEPT +template bool LumpedRateModelWithoutPores::usesAD() const CADET_NOEXCEPT { #ifdef CADET_CHECK_ANALYTIC_JACOBIAN // We always need AD if we want to check the analytical Jacobian @@ -159,7 +167,8 @@ bool LumpedRateModelWithoutPores::usesAD() const CADET_NOEXCEP } template -bool LumpedRateModelWithoutPores::configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper) +bool LumpedRateModelWithoutPores::configureModelDiscretization(IParameterProvider& paramProvider, + const IConfigHelper& helper) { // ==== Read discretization _disc.nComp = paramProvider.getInt("NCOMP"); @@ -169,7 +178,8 @@ bool LumpedRateModelWithoutPores::configureModelDiscretization paramProvider.pushScope("discretization"); - if (!newNBoundInterface && paramProvider.exists("NBOUND")) // done here and in this order for backwards compatibility + if (!newNBoundInterface && + paramProvider.exists("NBOUND")) // done here and in this order for backwards compatibility nBound = paramProvider.getIntArray("NBOUND"); else { @@ -178,7 +188,8 @@ bool LumpedRateModelWithoutPores::configureModelDiscretization paramProvider.pushScope("discretization"); } if (nBound.size() < _disc.nComp) - throw InvalidParameterException("Field NBOUND contains too few elements (NCOMP = " + std::to_string(_disc.nComp) + " required)"); + throw InvalidParameterException( + "Field NBOUND contains too few elements (NCOMP = " + std::to_string(_disc.nComp) + " required)"); _disc.nBound = new unsigned int[_disc.nComp]; std::copy_n(nBound.begin(), _disc.nComp, _disc.nBound); @@ -190,9 +201,9 @@ bool LumpedRateModelWithoutPores::configureModelDiscretization _disc.boundOffset[0] = 0; for (unsigned int i = 1; i < _disc.nComp; ++i) { - _disc.boundOffset[i] = _disc.boundOffset[i-1] + _disc.nBound[i-1]; + _disc.boundOffset[i] = _disc.boundOffset[i - 1] + _disc.nBound[i - 1]; } - _disc.strideBound = _disc.boundOffset[_disc.nComp-1] + _disc.nBound[_disc.nComp - 1]; + _disc.strideBound = _disc.boundOffset[_disc.nComp - 1] + _disc.nBound[_disc.nComp - 1]; // Determine whether analytic Jacobian should be used but don't set it right now. // We need to setup Jacobian matrices first. @@ -212,7 +223,8 @@ bool LumpedRateModelWithoutPores::configureModelDiscretization paramProvider.popScope(); const unsigned int strideCell = _disc.nComp + _disc.strideBound; - const bool transportSuccess = _convDispOp.configureModelDiscretization(paramProvider, helper, _disc.nComp, _disc.nCol, strideCell); + const bool transportSuccess = + _convDispOp.configureModelDiscretization(paramProvider, helper, _disc.nComp, _disc.nCol, strideCell); // Allocate memory Indexer idxr(_disc); @@ -248,7 +260,8 @@ bool LumpedRateModelWithoutPores::configureModelDiscretization if (_binding[0]->usesParamProviderInDiscretizationConfig()) paramProvider.pushScope("adsorption"); - bindingConfSuccess = _binding[0]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound, _disc.boundOffset); + bindingConfSuccess = + _binding[0]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound, _disc.boundOffset); if (_binding[0]->usesParamProviderInDiscretizationConfig()) paramProvider.popScope(); @@ -262,12 +275,14 @@ bool LumpedRateModelWithoutPores::configureModelDiscretization { _dynReaction[0] = helper.createDynamicReactionModel(paramProvider.getString("REACTION_MODEL")); if (!_dynReaction[0]) - throw InvalidParameterException("Unknown dynamic reaction model " + paramProvider.getString("REACTION_MODEL")); + throw InvalidParameterException("Unknown dynamic reaction model " + + paramProvider.getString("REACTION_MODEL")); if (_dynReaction[0]->usesParamProviderInDiscretizationConfig()) paramProvider.pushScope("reaction"); - reactionConfSuccess = _dynReaction[0]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound, _disc.boundOffset); + reactionConfSuccess = + _dynReaction[0]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound, _disc.boundOffset); if (_dynReaction[0]->usesParamProviderInDiscretizationConfig()) paramProvider.popScope(); @@ -290,11 +305,13 @@ bool LumpedRateModelWithoutPores::configure(IParameterProvider _totalPorosity = paramProvider.getDouble("TOTAL_POROSITY"); // Add parameters to map - _parameters[makeParamId(hashString("TOTAL_POROSITY"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_totalPorosity; + _parameters[makeParamId(hashString("TOTAL_POROSITY"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_totalPorosity; // Register initial conditions parameters for (unsigned int i = 0; i < _disc.nComp; ++i) - _parameters[makeParamId(hashString("INIT_C"), _unitOpIdx, i, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = _initC.data() + i; + _parameters[makeParamId(hashString("INIT_C"), _unitOpIdx, i, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep)] = _initC.data() + i; if (_binding[0]) { @@ -382,7 +399,8 @@ void LumpedRateModelWithoutPores::useAnalyticJacobian(const bo } template -void LumpedRateModelWithoutPores::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) +void LumpedRateModelWithoutPores::notifyDiscontinuousSectionTransition( + double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) { Indexer idxr(_disc); @@ -432,7 +450,8 @@ void LumpedRateModelWithoutPores::setFlowRates(active const* i } template -void LumpedRateModelWithoutPores::reportSolution(ISolutionRecorder& recorder, double const* const solution) const +void LumpedRateModelWithoutPores::reportSolution(ISolutionRecorder& recorder, + double const* const solution) const { Exporter expr(_disc, *this, solution); recorder.beginUnitOperation(_unitOpIdx, *this, expr); @@ -446,7 +465,6 @@ void LumpedRateModelWithoutPores::reportSolutionStructure(ISol recorder.unitOperationStructure(_unitOpIdx, *this, expr); } - template unsigned int LumpedRateModelWithoutPores::requiredADdirs() const CADET_NOEXCEPT { @@ -472,7 +490,8 @@ void LumpedRateModelWithoutPores::prepareADvectors(const AdJac const unsigned int lowerBandwidth = _jac.lowerBandwidth(); const unsigned int upperBandwidth = _jac.upperBandwidth(); - ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + _disc.nComp, adJac.adDirOffset, _jac.rows(), lowerBandwidth, upperBandwidth, lowerBandwidth); + ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + _disc.nComp, adJac.adDirOffset, _jac.rows(), lowerBandwidth, + upperBandwidth, lowerBandwidth); } /** @@ -481,7 +500,8 @@ void LumpedRateModelWithoutPores::prepareADvectors(const AdJac * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) */ template -void LumpedRateModelWithoutPores::extractJacobianFromAD(active const* const adRes, unsigned int adDirOffset) +void LumpedRateModelWithoutPores::extractJacobianFromAD(active const* const adRes, + unsigned int adDirOffset) { Indexer idxr(_disc); ad::extractBandedJacobianFromAd(adRes + idxr.offsetC(), adDirOffset, _jac.lowerBandwidth(), _jac); @@ -496,40 +516,55 @@ void LumpedRateModelWithoutPores::extractJacobianFromAD(active * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) */ template -void LumpedRateModelWithoutPores::checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const +void LumpedRateModelWithoutPores::checkAnalyticJacobianAgainstAd(active const* const adRes, + unsigned int adDirOffset) const { Indexer idxr(_disc); - const double maxDiff = ad::compareBandedJacobianWithAd(adRes + idxr.offsetC(), adDirOffset, _jac.lowerBandwidth(), _jac); - LOG(Debug) << "AD dir offset: " << adDirOffset << " DiagDirCol: " << _jac.lowerBandwidth() << " MaxDiff: " << maxDiff; + const double maxDiff = + ad::compareBandedJacobianWithAd(adRes + idxr.offsetC(), adDirOffset, _jac.lowerBandwidth(), _jac); + LOG(Debug) << "AD dir offset: " << adDirOffset << " DiagDirCol: " << _jac.lowerBandwidth() + << " MaxDiff: " << maxDiff; } #endif template -int LumpedRateModelWithoutPores::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithoutPores::jacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); _factorizeJacobian = true; if (_analyticJac) - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); else - return residualWithJacobian(simTime, ConstSimulationState{ simState.vecStateY, nullptr }, nullptr, adJac, threadLocalMem); + return residualWithJacobian(simTime, ConstSimulationState{simState.vecStateY, nullptr}, nullptr, adJac, + threadLocalMem); } template -int LumpedRateModelWithoutPores::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithoutPores::residual(const SimulationTime& simTime, + const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); // Evaluate residual do not compute Jacobian or parameter sensitivities - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } template -int LumpedRateModelWithoutPores::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithoutPores::residualWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + double* const res, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); @@ -538,8 +573,11 @@ int LumpedRateModelWithoutPores::residualWithJacobian(const Si } template -int LumpedRateModelWithoutPores::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, - const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity) +int LumpedRateModelWithoutPores::residual(const SimulationTime& simTime, + const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem, + bool updateJacobian, bool paramSensitivity) { if (updateJacobian) { @@ -550,7 +588,8 @@ int LumpedRateModelWithoutPores::residual(const SimulationTime { if (paramSensitivity) { - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -559,7 +598,8 @@ int LumpedRateModelWithoutPores::residual(const SimulationTime return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } else { @@ -574,9 +614,11 @@ int LumpedRateModelWithoutPores::residual(const SimulationTime // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -599,15 +641,18 @@ int LumpedRateModelWithoutPores::residual(const SimulationTime // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); // Only do comparison if we have a residuals vector (which is not always the case) if (res) { // Evaluate with analytical Jacobian which is stored in the band matrices - retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); // Compare AD with anaytic Jacobian checkAnalyticJacobianAgainstAd(adJac.adRes, adJac.adDirOffset); @@ -627,7 +672,8 @@ int LumpedRateModelWithoutPores::residual(const SimulationTime // @todo Check if this is necessary ad::resetAd(adJac.adRes, numDofs()); - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -636,18 +682,23 @@ int LumpedRateModelWithoutPores::residual(const SimulationTime return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } } template template -int LumpedRateModelWithoutPores::residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithoutPores::residualImpl(double t, unsigned int secIdx, StateType const* const y, + double const* const yDot, ResidualType* const res, + util::ThreadLocalStorage& threadLocalMem) { if (wantRes) - ConvOpResidual::call(this, _convDispOp, t, secIdx, y, yDot, res, _jac); + ConvOpResidual::call(this, _convDispOp, t, + secIdx, y, yDot, res, _jac); else - ConvOpJacobian::call(this, _convDispOp, t, secIdx, y, nullptr, nullptr, _jac); + ConvOpJacobian::call(this, _convDispOp, t, secIdx, y, nullptr, + nullptr, _jac); Indexer idxr(_disc); @@ -661,30 +712,30 @@ int LumpedRateModelWithoutPores::residualImpl(double t, unsign ResidualType* const localRes = wantRes ? res + idxr.offsetC() + idxr.strideColCell() * col : nullptr; double const* const localYdot = yDot ? yDot + idxr.offsetC() + idxr.strideColCell() * col : nullptr; - const parts::cell::CellParameters cellResParams - { - _disc.nComp, - _disc.nBound, - _disc.boundOffset, - _disc.strideBound, - _binding[0]->reactionQuasiStationarity(), - _totalPorosity, - nullptr, - _binding[0], - (_dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)) ? _dynReaction[0] : nullptr - }; + const parts::cell::CellParameters cellResParams{ + _disc.nComp, + _disc.nBound, + _disc.boundOffset, + _disc.strideBound, + _binding[0]->reactionQuasiStationarity(), + _totalPorosity, + nullptr, + _binding[0], + (_dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)) ? _dynReaction[0] : nullptr}; // Midpoint of current column cell (z coordinate) - needed in externally dependent adsorption kinetic const double z = _convDispOp.relativeCoordinate(col); if (wantRes) - parts::cell::residualKernel( - t, secIdx, ColumnPosition{ z, 0.0, 0.0 }, localY, localYdot, localRes, _jac.row(col * idxr.strideColCell()), cellResParams, threadLocalMem.get() - ); + parts::cell::residualKernel( + t, secIdx, ColumnPosition{z, 0.0, 0.0}, localY, localYdot, localRes, + _jac.row(col * idxr.strideColCell()), cellResParams, threadLocalMem.get()); else - parts::cell::residualKernel( - t, secIdx, ColumnPosition{ z, 0.0, 0.0 }, localY, localYdot, localRes, _jac.row(col * idxr.strideColCell()), cellResParams, threadLocalMem.get() - ); + parts::cell::residualKernel( + t, secIdx, ColumnPosition{z, 0.0, 0.0}, localY, localYdot, localRes, + _jac.row(col * idxr.strideColCell()), cellResParams, threadLocalMem.get()); } CADET_PARFOR_END; @@ -703,7 +754,10 @@ int LumpedRateModelWithoutPores::residualImpl(double t, unsign } template -int LumpedRateModelWithoutPores::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithoutPores::residualSensFwdWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidualSens); @@ -713,18 +767,23 @@ int LumpedRateModelWithoutPores::residualSensFwdWithJacobian(c } template -int LumpedRateModelWithoutPores::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem) +int LumpedRateModelWithoutPores::residualSensFwdAdOnly(const SimulationTime& simTime, + const ConstSimulationState& simState, + active* const adRes, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidualSens); // Evaluate residual for all parameters using AD in vector mode - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adRes, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, adRes, threadLocalMem); } template -int LumpedRateModelWithoutPores::residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3) +int LumpedRateModelWithoutPores::residualSensFwdCombine( + const SimulationTime& simTime, const ConstSimulationState& simState, const std::vector& yS, + const std::vector& ySdot, const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3) { BENCH_SCOPE(_timerResidualSens); @@ -734,10 +793,12 @@ int LumpedRateModelWithoutPores::residualSensFwdCombine(const for (std::size_t param = 0; param < yS.size(); ++param) { // Directional derivative (dF / dy) * s - multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, yS[param], 1.0, 0.0, tmp1); + multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, yS[param], 1.0, 0.0, + tmp1); // Directional derivative (dF / dyDot) * sDot - multiplyWithDerivativeJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, ySdot[param], tmp2); + multiplyWithDerivativeJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, ySdot[param], + tmp2); double* const ptrResS = resS[param]; @@ -761,11 +822,12 @@ int LumpedRateModelWithoutPores::residualSensFwdCombine(const } /** - * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, \dot{y}\right) @f$) + * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, + * \dot{y}\right) @f$) * @details Actually, the operation @f$ z = \alpha \frac{\partial F}{\partial y} x + \beta z @f$ is performed. * - * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ once - * before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. + * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ + * once before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. * @param [in] simTime Current simulation time point * @param [in] simState Simulation state vectors * @param [in] yS Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial y} @f$ @@ -774,7 +836,10 @@ int LumpedRateModelWithoutPores::residualSensFwdCombine(const * @param [in,out] ret Vector @f$ z @f$ which stores the result of the operation */ template -void LumpedRateModelWithoutPores::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret) +void LumpedRateModelWithoutPores::multiplyWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + double const* yS, double alpha, double beta, + double* ret) { Indexer idxr(_disc); @@ -792,7 +857,8 @@ void LumpedRateModelWithoutPores::multiplyWithJacobian(const S } /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ + * with a given vector * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). * @param [in] simTime Current simulation time point @@ -801,7 +867,9 @@ void LumpedRateModelWithoutPores::multiplyWithJacobian(const S * @param [out] ret Vector @f$ z @f$ which stores the result of the operation */ template -void LumpedRateModelWithoutPores::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret) +void LumpedRateModelWithoutPores::multiplyWithDerivativeJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + double const* sDot, double* ret) { Indexer idxr(_disc); const double invBeta = (1.0 / static_cast(_totalPorosity) - 1.0); @@ -813,7 +881,9 @@ void LumpedRateModelWithoutPores::multiplyWithDerivativeJacobi double const* const localSdot = sDot + localOffset; double* const localRet = ret + localOffset; - parts::cell::multiplyWithDerivativeJacobianKernel(localSdot, localRet, _disc.nComp, _disc.nBound, _disc.boundOffset, _disc.strideBound, _binding[0]->reactionQuasiStationarity(), 1.0, invBeta); + parts::cell::multiplyWithDerivativeJacobianKernel( + localSdot, localRet, _disc.nComp, _disc.nBound, _disc.boundOffset, _disc.strideBound, + _binding[0]->reactionQuasiStationarity(), 1.0, invBeta); } // Handle inlet DOFs (all algebraic) @@ -828,7 +898,8 @@ void LumpedRateModelWithoutPores::setExternalFunctions(IExtern } template -unsigned int LumpedRateModelWithoutPores::localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT +unsigned int LumpedRateModelWithoutPores::localOutletComponentIndex(unsigned int port) const + CADET_NOEXCEPT { // Inlets are duplicated so need to be accounted for if (_convDispOp.forwardFlow()) @@ -840,47 +911,53 @@ unsigned int LumpedRateModelWithoutPores::localOutletComponent } template -unsigned int LumpedRateModelWithoutPores::localInletComponentIndex(unsigned int port) const CADET_NOEXCEPT +unsigned int LumpedRateModelWithoutPores::localInletComponentIndex(unsigned int port) const + CADET_NOEXCEPT { return 0; } template -unsigned int LumpedRateModelWithoutPores::localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT +unsigned int LumpedRateModelWithoutPores::localOutletComponentStride(unsigned int port) const + CADET_NOEXCEPT { return 1; } template -unsigned int LumpedRateModelWithoutPores::localInletComponentStride(unsigned int port) const CADET_NOEXCEPT +unsigned int LumpedRateModelWithoutPores::localInletComponentStride(unsigned int port) const + CADET_NOEXCEPT { return 1; } template -void LumpedRateModelWithoutPores::expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut) +void LumpedRateModelWithoutPores::expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, + double* expandOut) { // @todo Write this function } /** * @brief Computes the solution of the linear system involving the system Jacobian - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the - * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) \f$, - * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p rhs. + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + * b \f] has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the point + * \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) \f$, may help + * with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p rhs. * * @param [in] t Current time point * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) * @param [in] outerTol Error tolerance for the solution of the linear system from outer Newton iteration * @param [in,out] rhs On entry the right hand side of the linear equation system, on exit the solution * @param [in] weight Vector with error weights - * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is evaluated + * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is + * evaluated * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ template -int LumpedRateModelWithoutPores::linearSolve(double t, double alpha, double outerTol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) +int LumpedRateModelWithoutPores::linearSolve(double t, double alpha, double outerTol, + double* const rhs, double const* const weight, + const ConstSimulationState& simState) { BENCH_SCOPE(_timerLinearSolve); @@ -915,21 +992,19 @@ int LumpedRateModelWithoutPores::linearSolve(double t, double LOG(Error) << "Solve() failed for bulk block"; } - return (success && result) ? 0 : 1;; + return (success && result) ? 0 : 1; + ; } /** * @brief Assembles the Jacobian of the time-discretized equations - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The system Jacobian of the original equations, - * \f[ \frac{\partial F}{\partial y}, \f] - * is already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible - * for adding - * \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] - * to the system Jacobian, which yields the Jacobian of the time-discretized equations - * \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] - * when a BDF method is used. The time integrator needs to solve this equation for @f$ y_0 @f$, which requires - * the solution of the linear system mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + * b \f] has to be solved. The system Jacobian of the original equations, \f[ \frac{\partial F}{\partial y}, \f] is + * already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible for + * adding \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] to the system Jacobian, which yields the Jacobian of the + * time-discretized equations \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] when a BDF method is used. + * The time integrator needs to solve this equation for @f$ y_0 @f$, which requires the solution of the linear system + * mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). * * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) * @param [in] idxr Indexer @@ -963,7 +1038,8 @@ void LumpedRateModelWithoutPores::assembleDiscretizedJacobian( * @param [in] invBeta Inverse porosity term @f$\frac{1}{\beta}@f$ */ template -void LumpedRateModelWithoutPores::addTimeDerivativeToJacobianCell(linalg::FactorizableBandMatrix::RowIterator& jac, const Indexer& idxr, double alpha, double invBeta) const +void LumpedRateModelWithoutPores::addTimeDerivativeToJacobianCell( + linalg::FactorizableBandMatrix::RowIterator& jac, const Indexer& idxr, double alpha, double invBeta) const { // Mobile phase for (int comp = 0; comp < static_cast(_disc.nComp); ++comp, ++jac) @@ -1009,7 +1085,8 @@ void LumpedRateModelWithoutPores::applyInitialCondition(const if (!_initStateDot.empty()) { std::fill(simState.vecStateYdot, simState.vecStateYdot + idxr.offsetC(), 0.0); - std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), simState.vecStateYdot + idxr.offsetC()); + std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), + simState.vecStateYdot + idxr.offsetC()); } else std::fill(simState.vecStateYdot, simState.vecStateYdot + numDofs(), 0.0); @@ -1048,7 +1125,8 @@ void LumpedRateModelWithoutPores::readInitialCondition(IParame // Check if INIT_STATE contains the full state and its time derivative if (initState.size() >= 2 * numPureDofs()) - _initStateDot = std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); + _initStateDot = + std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); return; } @@ -1077,9 +1155,9 @@ void LumpedRateModelWithoutPores::readInitialCondition(IParame * * The process works in two steps: *
                                    - *
                                  1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria).
                                  2. - *
                                  3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0. - * However, because of the algebraic equations, we need additional conditions to fully determine + *
                                  4. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction + * equilibria).
                                  5. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0. However, + * because of the algebraic equations, we need additional conditions to fully determine * @f$ \dot{y}@f$. By differentiating the algebraic equations with respect to time, we get the * missing linear equations (recall that the state vector @f$ y @f$ is fixed). * @@ -1100,7 +1178,11 @@ void LumpedRateModelWithoutPores::readInitialCondition(IParame * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ template -void LumpedRateModelWithoutPores::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) +void LumpedRateModelWithoutPores::consistentInitialState(const SimulationTime& simTime, + double* const vecStateY, + const AdJacobianParams& adJac, + double errorTol, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -1136,7 +1218,7 @@ void LumpedRateModelWithoutPores::consistentInitialState(const const linalg::ConstMaskArray mask{qsMask.data(), static_cast(_disc.nComp + _disc.strideBound)}; const int probSize = linalg::numMaskActive(mask); - //Problem capturing variables here + // Problem capturing variables here #ifdef CADET_PARALLELIZE BENCH_SCOPE(_timerConsistentInitPar); tbb::parallel_for(std::size_t(0), static_cast(_disc.nCol), [&](std::size_t col) @@ -1147,7 +1229,8 @@ void LumpedRateModelWithoutPores::consistentInitialState(const LinearBufferAllocator tlmAlloc = threadLocalMem.get(); // Reuse memory of band matrix for dense matrix - linalg::DenseMatrixView fullJacobianMatrix(_jacDisc.data() + col * _disc.strideBound * _disc.strideBound, nullptr, mask.len, mask.len); + linalg::DenseMatrixView fullJacobianMatrix(_jacDisc.data() + col * _disc.strideBound * _disc.strideBound, + nullptr, mask.len, mask.len); // Midpoint of current column cell (z coordinate) - needed in externally dependent adsorption kinetic const double z = _convDispOp.relativeCoordinate(col); @@ -1171,19 +1254,18 @@ void LumpedRateModelWithoutPores::consistentInitialState(const BufferedArray conservedQuantsBuffer = tlmAlloc.array(numActiveComp); double* const conservedQuants = static_cast(conservedQuantsBuffer); - linalg::DenseMatrixView jacobianMatrix(jacobianMem, _jacDisc.pivot() + col * _disc.strideBound, probSize, probSize); - const parts::cell::CellParameters cellResParams - { - _disc.nComp, - _disc.nBound, - _disc.boundOffset, - _disc.strideBound, - _binding[0]->reactionQuasiStationarity(), - _totalPorosity, - nullptr, - _binding[0], - (_dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)) ? _dynReaction[0] : nullptr - }; + linalg::DenseMatrixView jacobianMatrix(jacobianMem, _jacDisc.pivot() + col * _disc.strideBound, probSize, + probSize); + const parts::cell::CellParameters cellResParams{ + _disc.nComp, + _disc.nBound, + _disc.boundOffset, + _disc.strideBound, + _binding[0]->reactionQuasiStationarity(), + _totalPorosity, + nullptr, + _binding[0], + (_dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)) ? _dynReaction[0] : nullptr}; const int localOffsetToCell = idxr.offsetC() + col * idxr.strideColCell(); const int localOffsetInCell = idxr.strideColLiquid(); @@ -1196,7 +1278,8 @@ void LumpedRateModelWithoutPores::consistentInitialState(const const ColumnPosition colPos{z, 0.0, 0.0}; // Determine whether nonlinear solver is required - if (!_binding[0]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - localOffsetInCell, tlmAlloc)) + if (!_binding[0]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - localOffsetInCell, tlmAlloc)) CADET_PAR_CONTINUE; // Extract initial values from current state @@ -1204,13 +1287,13 @@ void LumpedRateModelWithoutPores::consistentInitialState(const // Save values of conserved moieties const double epsQ = 1.0 - static_cast(_totalPorosity); - linalg::conservedMoietiesFromPartitionedMask(mask, _disc.nBound, _disc.nComp, qShell - _disc.nComp, conservedQuants, static_cast(_totalPorosity), epsQ); + linalg::conservedMoietiesFromPartitionedMask(mask, _disc.nBound, _disc.nComp, qShell - _disc.nComp, + conservedQuants, static_cast(_totalPorosity), epsQ); std::function jacFunc; if (localAdY && localAdRes) { - jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) - { + jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) { // Copy over state vector to AD state vector (without changing directional values to keep seed vectors) // and initialize residuals with zero (also resetting directional values) ad::copyToAd(qShell - _disc.nComp, localAdY, mask.len); @@ -1221,32 +1304,32 @@ void LumpedRateModelWithoutPores::consistentInitialState(const linalg::applyVectorSubset(x, mask, localAdY); // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, localAdY, nullptr, localAdRes, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, localAdY, nullptr, localAdRes, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); #ifdef CADET_CHECK_ANALYTIC_JACOBIAN std::copy_n(qShell - _disc.nComp, mask.len, fullX); linalg::applyVectorSubset(x, mask, fullX); // Compute analytic Jacobian - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); // Compare const double diff = ad::compareDenseJacobianWithBandedAd( adJac.adRes + idxr.offsetC(), col * idxr.strideColCell(), adJac.adDirOffset, _jac.lowerBandwidth(), - _jac.lowerBandwidth(), _jac.upperBandwidth(), fullJacobianMatrix - ); + _jac.lowerBandwidth(), _jac.upperBandwidth(), fullJacobianMatrix); LOG(Debug) << "MaxDiff: " << diff; #endif // Extract Jacobian from AD - ad::extractDenseJacobianFromBandedAd( - adJac.adRes + idxr.offsetC(), col * idxr.strideColCell(), adJac.adDirOffset, _jac.lowerBandwidth(), - _jac.lowerBandwidth(), _jac.upperBandwidth(), fullJacobianMatrix - ); + ad::extractDenseJacobianFromBandedAd(adJac.adRes + idxr.offsetC(), col * idxr.strideColCell(), + adJac.adDirOffset, _jac.lowerBandwidth(), _jac.lowerBandwidth(), + _jac.upperBandwidth(), fullJacobianMatrix); // Extract Jacobian from full Jacobian mat.setAll(0.0); @@ -1285,16 +1368,16 @@ void LumpedRateModelWithoutPores::consistentInitialState(const } else { - jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) - { + jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) { // Prepare input vector by overwriting masked items std::copy_n(qShell - _disc.nComp, mask.len, fullX); linalg::applyVectorSubset(x, mask, fullX); // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); // Extract Jacobian from full Jacobian mat.setAll(0.0); @@ -1334,16 +1417,16 @@ void LumpedRateModelWithoutPores::consistentInitialState(const // Apply nonlinear solver _nonlinearSolver->solve( - [&](double const* const x, double* const r) - { + [&](double const* const x, double* const r) { // Prepare input vector by overwriting masked items std::copy_n(qShell - _disc.nComp, mask.len, fullX); linalg::applyVectorSubset(x, mask, fullX); // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); // Extract values from residual linalg::selectVectorSubset(fullResidual, mask, r); @@ -1383,7 +1466,8 @@ void LumpedRateModelWithoutPores::consistentInitialState(const linalg::applyVectorSubset(solution, mask, qShell - idxr.strideColLiquid()); // Refine / correct solution - _binding[0]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - idxr.strideColLiquid(), tlmAlloc); + _binding[0]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - idxr.strideColLiquid(), tlmAlloc); } CADET_PARFOR_END; } @@ -1395,9 +1479,9 @@ void LumpedRateModelWithoutPores::consistentInitialState(const * * The process works in two steps: *
                                      - *
                                    1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria).
                                    2. - *
                                    3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0. - * However, because of the algebraic equations, we need additional conditions to fully determine + *
                                    4. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction + * equilibria).
                                    5. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0. However, + * because of the algebraic equations, we need additional conditions to fully determine * @f$ \dot{y}@f$. By differentiating the algebraic equations with respect to time, we get the * missing linear equations (recall that the state vector @f$ y @f$ is fixed). * @@ -1413,10 +1497,13 @@ void LumpedRateModelWithoutPores::consistentInitialState(const * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] vecStateY Consistently initialized state vector - * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent state time derivatives. + * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent + * state time derivatives. */ template -void LumpedRateModelWithoutPores::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem) +void LumpedRateModelWithoutPores::consistentInitialTimeDerivative( + const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -1463,7 +1550,7 @@ void LumpedRateModelWithoutPores::consistentInitialTimeDerivat if (_binding[0]->dependsOnTime()) { _binding[0]->timeDerivativeQuasiStationaryFluxes(simTime.t, simTime.secIdx, ColumnPosition{z, 0.0, 0.0}, - qShellDot - _disc.nComp, qShellDot, dFluxDt, tlmAlloc); + qShellDot - _disc.nComp, qShellDot, dFluxDt, tlmAlloc); } // Copy row from original Jacobian and set right hand side @@ -1523,7 +1610,11 @@ void LumpedRateModelWithoutPores::consistentInitialTimeDerivat * @param [in] errorTol Error tolerance for algebraic equations */ template -void LumpedRateModelWithoutPores::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) +void LumpedRateModelWithoutPores::leanConsistentInitialState(const SimulationTime& simTime, + double* const vecStateY, + const AdJacobianParams& adJac, + double errorTol, + util::ThreadLocalStorage& threadLocalMem) { } @@ -1550,11 +1641,15 @@ void LumpedRateModelWithoutPores::leanConsistentInitialState(c * * @param [in] t Current time point * @param [in] vecStateY (Lean) consistently initialized state vector - * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time derivatives. - * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during execution of the function. + * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time + * derivatives. + * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during + * execution of the function. */ template -void LumpedRateModelWithoutPores::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem) +void LumpedRateModelWithoutPores::leanConsistentInitialTimeDerivative( + double t, double const* const vecStateY, double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -1583,7 +1678,8 @@ void LumpedRateModelWithoutPores::leanConsistentInitialTimeDer } template -void LumpedRateModelWithoutPores::initializeSensitivityStates(const std::vector& vecSensY) const +void LumpedRateModelWithoutPores::initializeSensitivityStates( + const std::vector& vecSensY) const { Indexer idxr(_disc); for (std::size_t param = 0; param < vecSensY.size(); ++param) @@ -1610,27 +1706,29 @@ void LumpedRateModelWithoutPores::initializeSensitivityStates( * @brief Computes consistent initial values and time derivatives of sensitivity subsystems * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, * the sensitivity system for a parameter @f$ p @f$ reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of this linear DAE, @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ - * have to be consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the time - * derivative \f$ \dot{s}_0 \f$ such that they are consistent. + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of this linear DAE, @f$ s_0 = + * \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ have to be + * consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the + * time derivative \f$ \dot{s}_0 \f$ such that they are consistent. * - * The process follows closely the one of consistentInitialConditions() and, in fact, is a linearized version of it. - * This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ are - * related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them - * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p}. @f$ - *
                                        - *
                                      1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria). - * Let @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at this point, we have - * \f[ \left( \frac{\partial F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, y_0, \dot{y}_0) \right)_{\mathcal{I}_a} = 0. \f]
                                      2. - *
                                      3. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. - * However, because of the algebraic equations, we need additional conditions to fully determine + * The process follows closely the one of consistentInitialConditions() and, in fact, is a linearized version + * of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ + * are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them + * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial + * \dot{y}_0}{\partial p}. @f$
                                        1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, + * reaction equilibria). Let @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at this point, we have + * \f[ \left( \frac{\partial F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, y_0, + * \dot{y}_0) \right)_{\mathcal{I}_a} = 0. \f]
                                        2. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ + * such that the differential equations hold. However, because of the algebraic equations, we need additional conditions + * to fully determine * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). * * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. - * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] - * which is 0 for algebraic equations (@f$ -\frac{\partial^2 F}{\partial t \partial p}@f$, to be more precise).
                                        3. + * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - + * \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] which is 0 for algebraic equations (@f$ -\frac{\partial^2 + * F}{\partial t \partial p}@f$, to be more precise). *
                                        * This function requires the parameter sensitivities to be computed beforehand and up-to-date Jacobians. * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) @@ -1641,8 +1739,9 @@ void LumpedRateModelWithoutPores::initializeSensitivityStates( * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ template -void LumpedRateModelWithoutPores::consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +void LumpedRateModelWithoutPores::consistentInitialSensitivity( + const SimulationTime& simTime, const ConstSimulationState& simState, std::vector& vecSensY, + std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -1672,11 +1771,13 @@ void LumpedRateModelWithoutPores::consistentInitialSensitivity for (unsigned int col = 0; col < _disc.nCol; ++col) #endif { - const unsigned int jacRowOffset = idxr.strideColCell() * col + static_cast(idxr.strideColLiquid()); + const unsigned int jacRowOffset = + idxr.strideColCell() * col + static_cast(idxr.strideColLiquid()); const int localQOffset = idxr.offsetC() + col * idxr.strideColCell() + idxr.strideColLiquid(); // Reuse memory of band matrix for dense matrix - linalg::DenseMatrixView jacobianMatrix(_jacDisc.data() + col * _disc.strideBound * _disc.strideBound, _jacDisc.pivot() + col * _disc.strideBound, probSize, probSize); + linalg::DenseMatrixView jacobianMatrix(_jacDisc.data() + col * _disc.strideBound * _disc.strideBound, + _jacDisc.pivot() + col * _disc.strideBound, probSize, probSize); // Get workspace memory LinearBufferAllocator tlmAlloc = threadLocalMem.get(); @@ -1701,7 +1802,8 @@ void LumpedRateModelWithoutPores::consistentInitialSensitivity linalg::fillVectorSubset(maskedMultiplier + _disc.nComp, mask, 0.0); // Assemble right hand side - _jac.submatrixMultiplyVector(maskedMultiplier, jacRowOffset, -static_cast(_disc.nComp), _disc.strideBound, idxr.strideColCell(), rhsUnmasked); + _jac.submatrixMultiplyVector(maskedMultiplier, jacRowOffset, -static_cast(_disc.nComp), + _disc.strideBound, idxr.strideColCell(), rhsUnmasked); linalg::vectorSubsetAdd(rhsUnmasked, mask, -1.0, 1.0, rhs); // Precondition @@ -1745,11 +1847,13 @@ void LumpedRateModelWithoutPores::consistentInitialSensitivity if (_binding[0]->hasQuasiStationaryReactions()) { // Get iterators to beginning of solid phase - linalg::BandMatrix::RowIterator jacSolidOrig = _jac.row(idxr.strideColCell() * col + idxr.strideColLiquid()); + linalg::BandMatrix::RowIterator jacSolidOrig = + _jac.row(idxr.strideColCell() * col + idxr.strideColLiquid()); linalg::FactorizableBandMatrix::RowIterator jacSolid = jac - idxr.strideColBound(); int const* const mask = _binding[0]->reactionQuasiStationarity(); - double* const qShellDot = sensYdot + idxr.offsetC() + idxr.strideColCell() * col + idxr.strideColLiquid(); + double* const qShellDot = + sensYdot + idxr.offsetC() + idxr.strideColCell() * col + idxr.strideColLiquid(); // Copy row from original Jacobian and set right hand side for (unsigned int i = 0; i < _disc.strideBound; ++i, ++jacSolid, ++jacSolidOrig) @@ -1791,19 +1895,19 @@ void LumpedRateModelWithoutPores::consistentInitialSensitivity * @brief Computes approximately / partially consistent initial values and time derivatives of sensitivity subsystems * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, * the sensitivity system for a parameter @f$ p @f$ reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of this linear DAE, @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ - * have to be consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the time - * derivative \f$ \dot{s}_0 \f$ such that they are consistent. + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of this linear DAE, @f$ s_0 = + * \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ have to be + * consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the + * time derivative \f$ \dot{s}_0 \f$ such that they are consistent. * - * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized version of it. - * This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ are - * related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them - * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p}. @f$ - *
                                          - *
                                        1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations).
                                        2. - *
                                        3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0 for the - * mobile phase variables.
                                        4. + * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized + * version of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ + * \dot{s}_0 \f$ are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by + * differentiating them with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = + * \frac{\partial \dot{y}_0}{\partial p}. @f$
                                          1. Keep state and time derivative vectors as they are (i.e., do not + * solve algebraic equations).
                                          2. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual + * is 0 for the mobile phase variables.
                                          3. *
                                          * This function requires the parameter sensitivities to be computed beforehand and up-to-date Jacobians. * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) @@ -1814,8 +1918,9 @@ void LumpedRateModelWithoutPores::consistentInitialSensitivity * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ template -void LumpedRateModelWithoutPores::leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +void LumpedRateModelWithoutPores::leanConsistentInitialSensitivity( + const SimulationTime& simTime, const ConstSimulationState& simState, std::vector& vecSensY, + std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -1881,7 +1986,8 @@ void LumpedRateModelWithoutPores::setSensitiveParameterValue(c } template -bool LumpedRateModelWithoutPores::setSensitiveParameter(const ParameterId& pId, unsigned int adDirection, double adValue) +bool LumpedRateModelWithoutPores::setSensitiveParameter(const ParameterId& pId, + unsigned int adDirection, double adValue) { if (_convDispOp.setSensitiveParameter(_sensParams, pId, adDirection, adValue)) { @@ -1892,7 +1998,6 @@ bool LumpedRateModelWithoutPores::setSensitiveParameter(const return UnitOperationBase::setSensitiveParameter(pId, adDirection, adValue); } - template int LumpedRateModelWithoutPores::Exporter::writeMobilePhase(double* buffer) const { @@ -1981,6 +2086,6 @@ IUnitOperation* createRadialFVLRM(UnitOpIdx uoId) return new RadialLRM(uoId); } -} // namespace model +} // namespace model -} // namespace cadet \ No newline at end of file +} // namespace cadet diff --git a/src/libcadet/model/LumpedRateModelWithoutPores.hpp b/src/libcadet/model/LumpedRateModelWithoutPores.hpp index 089647af7..8cd2d8cd0 100644 --- a/src/libcadet/model/LumpedRateModelWithoutPores.hpp +++ b/src/libcadet/model/LumpedRateModelWithoutPores.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the lumped rate model without pores (LRM). */ @@ -35,21 +35,26 @@ namespace { - template - struct LumpedRateModelWithoutPoresName { }; +template struct LumpedRateModelWithoutPoresName +{ +}; - template <> - struct LumpedRateModelWithoutPoresName +template <> struct LumpedRateModelWithoutPoresName +{ + static const char* identifier() CADET_NOEXCEPT { - static const char* identifier() CADET_NOEXCEPT { return "LUMPED_RATE_MODEL_WITHOUT_PORES"; } - }; + return "LUMPED_RATE_MODEL_WITHOUT_PORES"; + } +}; - template <> - struct LumpedRateModelWithoutPoresName +template <> struct LumpedRateModelWithoutPoresName +{ + static const char* identifier() CADET_NOEXCEPT { - static const char* identifier() CADET_NOEXCEPT { return "RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES"; } - }; -} + return "RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES"; + } +}; +} // namespace namespace cadet { @@ -60,23 +65,22 @@ namespace model /** * @brief Lumped rate model of liquid column chromatography without pores * @details See @cite Guiochon2006, @cite Gu1995, @cite Felinger2004 - * + * * @f[\begin{align} - \frac{\partial c_i}{\partial t} + \frac{1 - \varepsilon_t}{\varepsilon_t} \frac{\partial q_{i}}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 c_i}{\partial z^2} \\ - a \frac{\partial q_i}{\partial t} &= f_{\text{iso}}(c, q) -\end{align} @f] + \frac{\partial c_i}{\partial t} + \frac{1 - \varepsilon_t}{\varepsilon_t} \frac{\partial q_{i}}{\partial t} &= - u +\frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 c_i}{\partial z^2} \\ a \frac{\partial q_i}{\partial +t} &= f_{\text{iso}}(c, q) \end{align} @f] * Danckwerts boundary conditions (see @cite Danckwerts1953) @f[ \begin{align} u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partial z}(t,0) \\ \frac{\partial c_i}{\partial z}(t,L) &= 0 \end{align} @f] - * Methods are described in @cite VonLieres2010a (WENO, linear solver), @cite Puttmann2013 @cite Puttmann2016 (forward sensitivities, AD, band compression) + * Methods are described in @cite VonLieres2010a (WENO, linear solver), @cite Puttmann2013 @cite Puttmann2016 (forward +sensitivities, AD, band compression) */ -template -class LumpedRateModelWithoutPores : public UnitOperationBase +template class LumpedRateModelWithoutPores : public UnitOperationBase { public: - LumpedRateModelWithoutPores(UnitOpIdx unitOpIdx); virtual ~LumpedRateModelWithoutPores() CADET_NOEXCEPT; @@ -85,59 +89,104 @@ class LumpedRateModelWithoutPores : public UnitOperationBase virtual bool usesAD() const CADET_NOEXCEPT; virtual unsigned int requiredADdirs() const CADET_NOEXCEPT; - virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT { return _unitOpIdx; } - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } + virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT + { + return _unitOpIdx; + } + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } virtual void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT; - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual bool canAccumulate() const CADET_NOEXCEPT { return false; } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual bool canAccumulate() const CADET_NOEXCEPT + { + return false; + } - static const char* identifier() CADET_NOEXCEPT { return LumpedRateModelWithoutPoresName::identifier(); } - virtual const char* unitOperationName() const CADET_NOEXCEPT { return identifier(); } + static const char* identifier() CADET_NOEXCEPT + { + return LumpedRateModelWithoutPoresName::identifier(); + } + virtual const char* unitOperationName() const CADET_NOEXCEPT + { + return identifier(); + } virtual bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper); virtual bool configure(IParameterProvider& paramProvider); - virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac); + virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac); virtual void useAnalyticJacobian(const bool analyticJac); virtual void reportSolution(ISolutionRecorder& recorder, double const* const solution) const; virtual void reportSolutionStructure(ISolutionRecorder& recorder) const; - virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem); - - virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3); + virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + + virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3); virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState); + const ConstSimulationState& simState); virtual void prepareADvectors(const AdJacobianParams& adJac) const; virtual void applyInitialCondition(const SimulationState& simState) const; virtual void readInitialCondition(IParameterProvider& paramProvider); - virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); virtual void initializeSensitivityStates(const std::vector& vecSensY) const; virtual void consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem); virtual void leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual bool hasInlet() const CADET_NOEXCEPT { return true; } - virtual bool hasOutlet() const CADET_NOEXCEPT { return true; } + virtual bool hasInlet() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasOutlet() const CADET_NOEXCEPT + { + return true; + } virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT; virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT; @@ -145,14 +194,19 @@ class LumpedRateModelWithoutPores : public UnitOperationBase virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT; virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size); - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { } + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) + { + } virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut); - virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret); - virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret); + virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret); + virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* sDot, double* ret); - inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double* ret) + inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double* ret) { multiplyWithJacobian(simTime, simState, yS, 1.0, 0.0, ret); } @@ -166,47 +220,37 @@ class LumpedRateModelWithoutPores : public UnitOperationBase #ifdef CADET_BENCHMARK_MODE virtual std::vector benchmarkTimings() const { - return std::vector({ - static_cast(numDofs()), - _timerResidual.totalElapsedTime(), - _timerResidualPar.totalElapsedTime(), - _timerResidualSens.totalElapsedTime(), - _timerResidualSensPar.totalElapsedTime(), - _timerConsistentInit.totalElapsedTime(), - _timerConsistentInitPar.totalElapsedTime(), - _timerLinearSolve.totalElapsedTime() - }); + return std::vector({static_cast(numDofs()), _timerResidual.totalElapsedTime(), + _timerResidualPar.totalElapsedTime(), _timerResidualSens.totalElapsedTime(), + _timerResidualSensPar.totalElapsedTime(), _timerConsistentInit.totalElapsedTime(), + _timerConsistentInitPar.totalElapsedTime(), _timerLinearSolve.totalElapsedTime()}); } virtual char const* const* benchmarkDescriptions() const { static const char* const desc[] = { - "DOFs", - "Residual", - "ResidualPar", - "ResidualSens", - "ResidualSensPar", - "ConsistentInit", - "ConsistentInitPar", - "LinearSolve" - }; + "DOFs", "Residual", "ResidualPar", "ResidualSens", "ResidualSensPar", + "ConsistentInit", "ConsistentInitPar", "LinearSolve"}; return desc; } #endif protected: - class Indexer; - int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity); + int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, + bool paramSensitivity); template - int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); + int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, + ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); void extractJacobianFromAD(active const* const adRes, unsigned int adDirOffset); void assembleDiscretizedJacobian(double alpha, const Indexer& idxr); - void addTimeDerivativeToJacobianCell(linalg::FactorizableBandMatrix::RowIterator& jac, const Indexer& idxr, double alpha, double invBetaP) const; + void addTimeDerivativeToJacobianCell(linalg::FactorizableBandMatrix::RowIterator& jac, const Indexer& idxr, + double alpha, double invBetaP) const; #ifdef CADET_CHECK_ANALYTIC_JACOBIAN void checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const; @@ -214,34 +258,34 @@ class LumpedRateModelWithoutPores : public UnitOperationBase struct Discretization { - unsigned int nComp; //!< Number of components - unsigned int nCol; //!< Number of column cells - unsigned int* nBound; //!< Array with number of bound states for each component + unsigned int nComp; //!< Number of components + unsigned int nCol; //!< Number of column cells + unsigned int* nBound; //!< Array with number of bound states for each component unsigned int* boundOffset; //!< Array with offset to the first bound state of each component in the solid phase - unsigned int strideBound; //!< Total number of bound states + unsigned int strideBound; //!< Total number of bound states }; Discretization _disc; //!< Discretization info -// IExternalFunction* _extFun; //!< External function (owned by library user) + // IExternalFunction* _extFun; //!< External function (owned by library user) ConvDispOperator _convDispOp; //!< Convection dispersion operator for interstitial volume transport - linalg::BandMatrix _jac; //!< Jacobian + linalg::BandMatrix _jac; //!< Jacobian linalg::FactorizableBandMatrix _jacDisc; //!< Jacobian with time derivatives from BDF method linalg::DoubleSparseMatrix _jacInlet; //!< Jacobian inlet DOF block matrix connects inlet DOFs to first bulk cells active _totalPorosity; //!< Total porosity \f$ \varepsilon_t \f$ - bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used + bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used unsigned int _jacobianAdDirs; //!< Number of AD seed vectors required for Jacobian computation bool _factorizeJacobian; //!< Determines whether the Jacobian needs to be factorized double* _tempState; //!< Temporary storage with the size of the state vector or larger if binding models require it - std::vector _initC; //!< Liquid phase initial conditions - std::vector _initQ; //!< Solid phase initial conditions - std::vector _initState; //!< Initial conditions for state vector if given + std::vector _initC; //!< Liquid phase initial conditions + std::vector _initQ; //!< Solid phase initial conditions + std::vector _initState; //!< Initial conditions for state vector if given std::vector _initStateDot; //!< Initial conditions for time derivative BENCH_TIMER(_timerResidual) @@ -255,29 +299,68 @@ class LumpedRateModelWithoutPores : public UnitOperationBase class Indexer { public: - Indexer(const Discretization& disc) : _disc(disc) { } + Indexer(const Discretization& disc) : _disc(disc) + { + } // Strides - inline int strideColCell() const CADET_NOEXCEPT { return static_cast(_disc.nComp + _disc.strideBound); } - inline int strideColComp() const CADET_NOEXCEPT { return 1; } + inline int strideColCell() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp + _disc.strideBound); + } + inline int strideColComp() const CADET_NOEXCEPT + { + return 1; + } - inline int strideColLiquid() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideColBound() const CADET_NOEXCEPT { return static_cast(_disc.strideBound); } + inline int strideColLiquid() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideColBound() const CADET_NOEXCEPT + { + return static_cast(_disc.strideBound); + } // Offsets - inline int offsetC() const CADET_NOEXCEPT { return _disc.nComp; } - inline int offsetBoundComp(unsigned int comp) const CADET_NOEXCEPT { return _disc.boundOffset[comp]; } + inline int offsetC() const CADET_NOEXCEPT + { + return _disc.nComp; + } + inline int offsetBoundComp(unsigned int comp) const CADET_NOEXCEPT + { + return _disc.boundOffset[comp]; + } // Return pointer to first element of state variable in state vector - template inline real_t* c(real_t* const data) const { return data + offsetC(); } - template inline real_t const* c(real_t const* const data) const { return data + offsetC(); } + template inline real_t* c(real_t* const data) const + { + return data + offsetC(); + } + template inline real_t const* c(real_t const* const data) const + { + return data + offsetC(); + } - template inline real_t* q(real_t* const data) const { return data + offsetC() + strideColLiquid(); } - template inline real_t const* q(real_t const* const data) const { return data + offsetC() + strideColLiquid(); } + template inline real_t* q(real_t* const data) const + { + return data + offsetC() + strideColLiquid(); + } + template inline real_t const* q(real_t const* const data) const + { + return data + offsetC() + strideColLiquid(); + } // Return specific variable in state vector - template inline real_t& c(real_t* const data, unsigned int col, unsigned int comp) const { return data[offsetC() + comp + col * strideColCell()]; } - template inline const real_t& c(real_t const* const data, unsigned int col, unsigned int comp) const { return data[offsetC() + comp + col * strideColCell()]; } + template inline real_t& c(real_t* const data, unsigned int col, unsigned int comp) const + { + return data[offsetC() + comp + col * strideColCell()]; + } + template + inline const real_t& c(real_t const* const data, unsigned int col, unsigned int comp) const + { + return data[offsetC() + comp + col * strideColCell()]; + } protected: const Discretization& _disc; @@ -286,55 +369,140 @@ class LumpedRateModelWithoutPores : public UnitOperationBase class Exporter : public ISolutionExporter { public: - - Exporter(const Discretization& disc, const LumpedRateModelWithoutPores& model, double const* data) : _disc(disc), _idx(disc), _model(model), _data(data) { } + Exporter(const Discretization& disc, const LumpedRateModelWithoutPores& model, double const* data) + : _disc(disc), _idx(disc), _model(model), _data(data) + { + } Exporter(const Discretization&& disc, const LumpedRateModelWithoutPores& model, double const* data) = delete; - virtual bool hasParticleFlux() const CADET_NOEXCEPT { return false; } - virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT { return false; } - virtual bool hasSolidPhase() const CADET_NOEXCEPT { return _disc.strideBound > 0; } - virtual bool hasVolume() const CADET_NOEXCEPT { return false; } - virtual bool isParticleLumped() const CADET_NOEXCEPT { return false; } - virtual bool hasPrimaryExtent() const CADET_NOEXCEPT { return true; } - - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } - virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT { return _disc.nCol; } - virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numParticleTypes() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT { return _disc.strideBound; } - virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT { return _disc.nComp * _disc.nCol; } - virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return _disc.strideBound * _disc.nCol; } - virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT { return _disc.strideBound * _disc.nCol; } - virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT { return 0u; } - virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT { return 0; } + virtual bool hasParticleFlux() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasSolidPhase() const CADET_NOEXCEPT + { + return _disc.strideBound > 0; + } + virtual bool hasVolume() const CADET_NOEXCEPT + { + return false; + } + virtual bool isParticleLumped() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasPrimaryExtent() const CADET_NOEXCEPT + { + return true; + } + + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } + virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT + { + return _disc.nCol; + } + virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numParticleTypes() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.strideBound; + } + virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nCol; + } + virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.strideBound * _disc.nCol; + } + virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT + { + return _disc.strideBound * _disc.nCol; + } + virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT + { + return 0u; + } + virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT + { + return 0; + } virtual int writeMobilePhase(double* buffer) const; virtual int writeSolidPhase(double* buffer) const; - virtual int writeParticleMobilePhase(double* buffer) const { return 0; } + virtual int writeParticleMobilePhase(double* buffer) const + { + return 0; + } virtual int writeSolidPhase(unsigned int parType, double* buffer) const; - virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const { return 0; } - virtual int writeParticleFlux(double* buffer) const { return 0; } - virtual int writeParticleFlux(unsigned int parType, double* buffer) const { return 0; } - virtual int writeVolume(double* buffer) const { return 0; } + virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const + { + return 0; + } + virtual int writeParticleFlux(double* buffer) const + { + return 0; + } + virtual int writeParticleFlux(unsigned int parType, double* buffer) const + { + return 0; + } + virtual int writeVolume(double* buffer) const + { + return 0; + } virtual int writeInlet(unsigned int port, double* buffer) const; virtual int writeInlet(double* buffer) const; virtual int writeOutlet(unsigned int port, double* buffer) const; virtual int writeOutlet(double* buffer) const; - virtual int writePrimaryCoordinates(double* coords) const { for (unsigned int i = 0; i < _disc.nCol; ++i) coords[i] = _model._convDispOp.cellCenter(i); return _disc.nCol; } - virtual int writeSecondaryCoordinates(double* coords) const { return 0; } - virtual int writeParticleCoordinates(unsigned int parType, double* coords) const { return 0; } + virtual int writeSecondaryCoordinates(double* coords) const + { + return 0; + } + virtual int writeParticleCoordinates(unsigned int parType, double* coords) const + { + return 0; + } protected: const Discretization& _disc; @@ -350,4 +518,4 @@ IUnitOperation* createRadialFVLRM(UnitOpIdx uoId); } // namespace model } // namespace cadet -#endif // LIBCADET_LUMPEDRATEMODELWITHOUTPORES_HPP_ +#endif // LIBCADET_LUMPEDRATEMODELWITHOUTPORES_HPP_ diff --git a/src/libcadet/model/LumpedRateModelWithoutPoresBuilder.cpp b/src/libcadet/model/LumpedRateModelWithoutPoresBuilder.cpp index 5cf23237b..6fcfae51c 100644 --- a/src/libcadet/model/LumpedRateModelWithoutPoresBuilder.cpp +++ b/src/libcadet/model/LumpedRateModelWithoutPoresBuilder.cpp @@ -1,80 +1,84 @@ #include "model/LumpedRateModelWithoutPores.hpp" #include "CompileTimeConfig.hpp" #ifdef ENABLE_DG - #include "model/LumpedRateModelWithoutPoresDG.hpp" +#include "model/LumpedRateModelWithoutPoresDG.hpp" #endif #include "LoggingUtils.hpp" #include "Logging.hpp" - namespace cadet { namespace model { - IUnitOperation* selectAxialFlowDiscretizationLRM(UnitOpIdx uoId, IParameterProvider& paramProvider) - { - IUnitOperation* model = nullptr; +IUnitOperation* selectAxialFlowDiscretizationLRM(UnitOpIdx uoId, IParameterProvider& paramProvider) +{ + IUnitOperation* model = nullptr; - paramProvider.pushScope("discretization"); + paramProvider.pushScope("discretization"); - if (paramProvider.exists("SPATIAL_METHOD")) { + if (paramProvider.exists("SPATIAL_METHOD")) + { - const std::string discName = paramProvider.getString("SPATIAL_METHOD"); + const std::string discName = paramProvider.getString("SPATIAL_METHOD"); #ifdef ENABLE_DG - if(discName == "DG") - model = new LumpedRateModelWithoutPoresDG(uoId); - else if (discName == "FV") + if (discName == "DG") + model = new LumpedRateModelWithoutPoresDG(uoId); + else if (discName == "FV") #else - if (discName == "FV") + if (discName == "FV") #endif - model = createAxialFVLRM(uoId); - else - { - LOG(Error) << "Unknown discretization type " << discName << " for unit " << uoId; - } - } - else { model = createAxialFVLRM(uoId); + else + { + LOG(Error) << "Unknown discretization type " << discName << " for unit " << uoId; } + } + else + { + model = createAxialFVLRM(uoId); + } - paramProvider.popScope(); + paramProvider.popScope(); - return model; - } + return model; +} - IUnitOperation* selectRadialFlowDiscretizationLRM(UnitOpIdx uoId, IParameterProvider& paramProvider) - { - IUnitOperation* model = nullptr; +IUnitOperation* selectRadialFlowDiscretizationLRM(UnitOpIdx uoId, IParameterProvider& paramProvider) +{ + IUnitOperation* model = nullptr; - paramProvider.pushScope("discretization"); + paramProvider.pushScope("discretization"); - if (paramProvider.exists("SPATIAL_METHOD")) { + if (paramProvider.exists("SPATIAL_METHOD")) + { - const std::string discName = paramProvider.getString("SPATIAL_METHOD"); + const std::string discName = paramProvider.getString("SPATIAL_METHOD"); - if (discName == "DG") - { - LOG(Error) << "Radial flow not implemented for DG discretization yet, was called for unit " << uoId; - } - else if (discName == "FV") - model = createRadialFVLRM(uoId); - else - { - LOG(Error) << "Unknown discretization type " << discName << " for unit " << uoId; - } + if (discName == "DG") + { + LOG(Error) << "Radial flow not implemented for DG discretization yet, was called for unit " << uoId; } - else { + else if (discName == "FV") model = createRadialFVLRM(uoId); + else + { + LOG(Error) << "Unknown discretization type " << discName << " for unit " << uoId; } + } + else + { + model = createRadialFVLRM(uoId); + } - paramProvider.popScope(); + paramProvider.popScope(); - return model; - } + return model; +} -void registerLumpedRateModelWithoutPores(std::unordered_map>& models) +void registerLumpedRateModelWithoutPores( + std::unordered_map>& models) { typedef LumpedRateModelWithoutPores AxialLRM; typedef LumpedRateModelWithoutPores RadialLRM; @@ -92,6 +96,6 @@ void registerLumpedRateModelWithoutPores(std::unordered_map nBound; - const bool newNBoundInterface = paramProvider.exists("NBOUND"); - - paramProvider.pushScope("discretization"); - - _linearSolver = cadet::linalg::setLinearSolver(paramProvider.exists("LINEAR_SOLVER") ? paramProvider.getString("LINEAR_SOLVER") : "SparseLU"); +} - if (!newNBoundInterface && paramProvider.exists("NBOUND")) // done here and in this order for backwards compatibility - nBound = paramProvider.getIntArray("NBOUND"); - else - { - paramProvider.popScope(); - nBound = paramProvider.getIntArray("NBOUND"); - paramProvider.pushScope("discretization"); - } - if (nBound.size() < _disc.nComp) - throw InvalidParameterException("Field NBOUND contains too few elements (NCOMP = " + std::to_string(_disc.nComp) + " required)"); - - _disc.nBound = new unsigned int[_disc.nComp]; - std::copy_n(nBound.begin(), _disc.nComp, _disc.nBound); - - if (paramProvider.exists("POLYDEG")) - _disc.polyDeg = paramProvider.getInt("POLYDEG"); - else - _disc.polyDeg = 4u; // default value - if (paramProvider.getInt("POLYDEG") < 1) - throw InvalidParameterException("Polynomial degree must be at least 1!"); - else if (_disc.polyDeg < 3) - LOG(Warning) << "Polynomial degree > 2 in bulk discretization (cf. POLYDEG) is always recommended for performance reasons."; - - _disc.nNodes = _disc.polyDeg + 1; - - if (paramProvider.exists("NELEM")) - _disc.nCol = paramProvider.getInt("NELEM"); - else if (paramProvider.exists("NCOL")) - _disc.nCol = std::max(1u, paramProvider.getInt("NCOL") / _disc.nNodes); // number of elements is rounded down - else - throw InvalidParameterException("Specify field NELEM (or NCOL)"); +bool LumpedRateModelWithoutPoresDG::configureModelDiscretization(IParameterProvider& paramProvider, + const IConfigHelper& helper) +{ + // Read discretization + _disc.nComp = paramProvider.getInt("NCOMP"); - if (_disc.nCol < 1) - throw InvalidParameterException("Number of column elements must be at least 1!"); + std::vector nBound; + const bool newNBoundInterface = paramProvider.exists("NBOUND"); - _disc.nPoints = _disc.nNodes * _disc.nCol; + paramProvider.pushScope("discretization"); - int polynomial_integration_mode = 0; - if (paramProvider.exists("EXACT_INTEGRATION")) - polynomial_integration_mode = paramProvider.getInt("EXACT_INTEGRATION"); - _disc.exactInt = static_cast(polynomial_integration_mode); // only integration mode 0 applies the inexact collocated diagonal LGL mass matrix + _linearSolver = cadet::linalg::setLinearSolver( + paramProvider.exists("LINEAR_SOLVER") ? paramProvider.getString("LINEAR_SOLVER") : "SparseLU"); - // Precompute offsets and total number of bound states (DOFs in solid phase) - _disc.boundOffset = new unsigned int[_disc.nComp]; - _disc.boundOffset[0] = 0; - for (unsigned int i = 1; i < _disc.nComp; ++i) - { - _disc.boundOffset[i] = _disc.boundOffset[i - 1] + _disc.nBound[i - 1]; - } - _disc.strideBound = _disc.boundOffset[_disc.nComp - 1] + _disc.nBound[_disc.nComp - 1]; + if (!newNBoundInterface && + paramProvider.exists("NBOUND")) // done here and in this order for backwards compatibility + nBound = paramProvider.getIntArray("NBOUND"); + else + { + paramProvider.popScope(); + nBound = paramProvider.getIntArray("NBOUND"); + paramProvider.pushScope("discretization"); + } + if (nBound.size() < _disc.nComp) + throw InvalidParameterException( + "Field NBOUND contains too few elements (NCOMP = " + std::to_string(_disc.nComp) + " required)"); + + _disc.nBound = new unsigned int[_disc.nComp]; + std::copy_n(nBound.begin(), _disc.nComp, _disc.nBound); + + if (paramProvider.exists("POLYDEG")) + _disc.polyDeg = paramProvider.getInt("POLYDEG"); + else + _disc.polyDeg = 4u; // default value + if (paramProvider.getInt("POLYDEG") < 1) + throw InvalidParameterException("Polynomial degree must be at least 1!"); + else if (_disc.polyDeg < 3) + LOG(Warning) << "Polynomial degree > 2 in bulk discretization (cf. POLYDEG) is always recommended for " + "performance reasons."; + + _disc.nNodes = _disc.polyDeg + 1; + + if (paramProvider.exists("NELEM")) + _disc.nCol = paramProvider.getInt("NELEM"); + else if (paramProvider.exists("NCOL")) + _disc.nCol = std::max(1u, paramProvider.getInt("NCOL") / _disc.nNodes); // number of elements is rounded down + else + throw InvalidParameterException("Specify field NELEM (or NCOL)"); + + if (_disc.nCol < 1) + throw InvalidParameterException("Number of column elements must be at least 1!"); + + _disc.nPoints = _disc.nNodes * _disc.nCol; + + int polynomial_integration_mode = 0; + if (paramProvider.exists("EXACT_INTEGRATION")) + polynomial_integration_mode = paramProvider.getInt("EXACT_INTEGRATION"); + _disc.exactInt = static_cast( + polynomial_integration_mode); // only integration mode 0 applies the inexact collocated diagonal LGL mass matrix + + // Precompute offsets and total number of bound states (DOFs in solid phase) + _disc.boundOffset = new unsigned int[_disc.nComp]; + _disc.boundOffset[0] = 0; + for (unsigned int i = 1; i < _disc.nComp; ++i) + { + _disc.boundOffset[i] = _disc.boundOffset[i - 1] + _disc.nBound[i - 1]; + } + _disc.strideBound = _disc.boundOffset[_disc.nComp - 1] + _disc.nBound[_disc.nComp - 1]; - // Determine whether analytic Jacobian should be used but don't set it right now. - // We need to setup Jacobian matrices first. + // Determine whether analytic Jacobian should be used but don't set it right now. + // We need to setup Jacobian matrices first. #ifndef CADET_CHECK_ANALYTIC_JACOBIAN - const bool analyticJac = paramProvider.getBool("USE_ANALYTIC_JACOBIAN"); + const bool analyticJac = paramProvider.getBool("USE_ANALYTIC_JACOBIAN"); #else - const bool analyticJac = false; + const bool analyticJac = false; #endif - // Allocate space for initial conditions - _initC.resize(_disc.nCol * _disc.nNodes * _disc.nComp); - _initQ.resize(_disc.nCol * _disc.nNodes * _disc.strideBound); + // Allocate space for initial conditions + _initC.resize(_disc.nCol * _disc.nNodes * _disc.nComp); + _initQ.resize(_disc.nCol * _disc.nNodes * _disc.strideBound); - // Create nonlinear solver for consistent initialization - configureNonlinearSolver(paramProvider); + // Create nonlinear solver for consistent initialization + configureNonlinearSolver(paramProvider); - paramProvider.popScope(); + paramProvider.popScope(); - const unsigned int strideNode = _disc.nComp + _disc.strideBound; - const bool transportSuccess = _convDispOp.configureModelDiscretization(paramProvider, helper, _disc.nComp, polynomial_integration_mode, _disc.nCol, _disc.polyDeg, strideNode); + const unsigned int strideNode = _disc.nComp + _disc.strideBound; + const bool transportSuccess = _convDispOp.configureModelDiscretization( + paramProvider, helper, _disc.nComp, polynomial_integration_mode, _disc.nCol, _disc.polyDeg, strideNode); - _disc.curSection = -1; + _disc.curSection = -1; - // Allocate memory - Indexer idxr(_disc); + // Allocate memory + Indexer idxr(_disc); - if (_disc.exactInt) - _jacInlet.resize(_disc.nNodes, 1); // first cell depends on inlet concentration (same for every component) - else - _jacInlet.resize(1, 1); // first cell depends on inlet concentration (same for every component) - _jac.resize((_disc.nComp + _disc.strideBound) * _disc.nPoints, (_disc.nComp + _disc.strideBound) * _disc.nPoints); - _jacDisc.resize((_disc.nComp + _disc.strideBound) * _disc.nPoints, (_disc.nComp + _disc.strideBound) * _disc.nPoints); - // jacobian pattern is set and analyzed after reactions are configured + if (_disc.exactInt) + _jacInlet.resize(_disc.nNodes, 1); // first cell depends on inlet concentration (same for every component) + else + _jacInlet.resize(1, 1); // first cell depends on inlet concentration (same for every component) + _jac.resize((_disc.nComp + _disc.strideBound) * _disc.nPoints, (_disc.nComp + _disc.strideBound) * _disc.nPoints); + _jacDisc.resize((_disc.nComp + _disc.strideBound) * _disc.nPoints, + (_disc.nComp + _disc.strideBound) * _disc.nPoints); + // jacobian pattern is set and analyzed after reactions are configured - // Set whether analytic Jacobian is used - useAnalyticJacobian(analyticJac); + // Set whether analytic Jacobian is used + useAnalyticJacobian(analyticJac); - // ==== Construct and configure binding model + // ==== Construct and configure binding model - clearBindingModels(); - _binding.push_back(nullptr); + clearBindingModels(); + _binding.push_back(nullptr); - if (paramProvider.exists("ADSORPTION_MODEL")) - _binding[0] = helper.createBindingModel(paramProvider.getString("ADSORPTION_MODEL")); - else - _binding[0] = helper.createBindingModel("NONE"); + if (paramProvider.exists("ADSORPTION_MODEL")) + _binding[0] = helper.createBindingModel(paramProvider.getString("ADSORPTION_MODEL")); + else + _binding[0] = helper.createBindingModel("NONE"); - if (!_binding[0]) - throw InvalidParameterException("Unknown binding model " + paramProvider.getString("ADSORPTION_MODEL")); + if (!_binding[0]) + throw InvalidParameterException("Unknown binding model " + paramProvider.getString("ADSORPTION_MODEL")); - bool bindingConfSuccess = true; - if (_binding[0]->usesParamProviderInDiscretizationConfig()) - paramProvider.pushScope("adsorption"); + bool bindingConfSuccess = true; + if (_binding[0]->usesParamProviderInDiscretizationConfig()) + paramProvider.pushScope("adsorption"); - bindingConfSuccess = _binding[0]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound, _disc.boundOffset); + bindingConfSuccess = + _binding[0]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound, _disc.boundOffset); - if (_binding[0]->usesParamProviderInDiscretizationConfig()) - paramProvider.popScope(); + if (_binding[0]->usesParamProviderInDiscretizationConfig()) + paramProvider.popScope(); - // ==== Construct and configure dynamic reaction model - bool reactionConfSuccess = true; - clearDynamicReactionModels(); - _dynReaction.push_back(nullptr); + // ==== Construct and configure dynamic reaction model + bool reactionConfSuccess = true; + clearDynamicReactionModels(); + _dynReaction.push_back(nullptr); - if (paramProvider.exists("REACTION_MODEL")) - { - _dynReaction[0] = helper.createDynamicReactionModel(paramProvider.getString("REACTION_MODEL")); - if (!_dynReaction[0]) - throw InvalidParameterException("Unknown dynamic reaction model " + paramProvider.getString("REACTION_MODEL")); + if (paramProvider.exists("REACTION_MODEL")) + { + _dynReaction[0] = helper.createDynamicReactionModel(paramProvider.getString("REACTION_MODEL")); + if (!_dynReaction[0]) + throw InvalidParameterException("Unknown dynamic reaction model " + + paramProvider.getString("REACTION_MODEL")); - if (_dynReaction[0]->usesParamProviderInDiscretizationConfig()) - paramProvider.pushScope("reaction"); + if (_dynReaction[0]->usesParamProviderInDiscretizationConfig()) + paramProvider.pushScope("reaction"); - reactionConfSuccess = _dynReaction[0]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound, _disc.boundOffset); + reactionConfSuccess = + _dynReaction[0]->configureModelDiscretization(paramProvider, _disc.nComp, _disc.nBound, _disc.boundOffset); - if (_dynReaction[0]->usesParamProviderInDiscretizationConfig()) - paramProvider.popScope(); - } + if (_dynReaction[0]->usesParamProviderInDiscretizationConfig()) + paramProvider.popScope(); + } - // Setup the memory for tempState based on state vector - _tempState = new double[numDofs()]; + // Setup the memory for tempState based on state vector + _tempState = new double[numDofs()]; - return bindingConfSuccess && reactionConfSuccess; - } + return bindingConfSuccess && reactionConfSuccess; +} - bool LumpedRateModelWithoutPoresDG::configure(IParameterProvider& paramProvider) - { - _parameters.clear(); +bool LumpedRateModelWithoutPoresDG::configure(IParameterProvider& paramProvider) +{ + _parameters.clear(); - _convDispOp.configure(_unitOpIdx, paramProvider, _parameters); + _convDispOp.configure(_unitOpIdx, paramProvider, _parameters); - // Read geometry parameters - _totalPorosity = paramProvider.getDouble("TOTAL_POROSITY"); + // Read geometry parameters + _totalPorosity = paramProvider.getDouble("TOTAL_POROSITY"); - // Add parameters to map - _parameters[makeParamId(hashString("TOTAL_POROSITY"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_totalPorosity; + // Add parameters to map + _parameters[makeParamId(hashString("TOTAL_POROSITY"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_totalPorosity; - // Register initial conditions parameters - for (unsigned int i = 0; i < _disc.nComp; ++i) - _parameters[makeParamId(hashString("INIT_C"), _unitOpIdx, i, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = _initC.data() + i; + // Register initial conditions parameters + for (unsigned int i = 0; i < _disc.nComp; ++i) + _parameters[makeParamId(hashString("INIT_C"), _unitOpIdx, i, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep)] = _initC.data() + i; - if (_binding[0]) - { - std::vector initParams(_disc.strideBound); - _binding[0]->fillBoundPhaseInitialParameters(initParams.data(), _unitOpIdx, cadet::ParTypeIndep); - - for (unsigned int i = 0; i < _disc.strideBound; ++i) - _parameters[initParams[i]] = _initQ.data() + i; - } + if (_binding[0]) + { + std::vector initParams(_disc.strideBound); + _binding[0]->fillBoundPhaseInitialParameters(initParams.data(), _unitOpIdx, cadet::ParTypeIndep); - // Reconfigure binding model - bool bindingConfSuccess = true; - if (_binding[0] && paramProvider.exists("adsorption") && _binding[0]->requiresConfiguration()) - { - paramProvider.pushScope("adsorption"); - bindingConfSuccess = _binding[0]->configure(paramProvider, _unitOpIdx, cadet::ParTypeIndep); - paramProvider.popScope(); - } + for (unsigned int i = 0; i < _disc.strideBound; ++i) + _parameters[initParams[i]] = _initQ.data() + i; + } - // Reconfigure dynamic reaction model - bool reactionConfSuccess = true; - if (_dynReaction[0] && paramProvider.exists("reaction") && _dynReaction[0]->requiresConfiguration()) - { - paramProvider.pushScope("reaction"); - reactionConfSuccess = _dynReaction[0]->configure(paramProvider, _unitOpIdx, cadet::ParTypeIndep); - paramProvider.popScope(); - } + // Reconfigure binding model + bool bindingConfSuccess = true; + if (_binding[0] && paramProvider.exists("adsorption") && _binding[0]->requiresConfiguration()) + { + paramProvider.pushScope("adsorption"); + bindingConfSuccess = _binding[0]->configure(paramProvider, _unitOpIdx, cadet::ParTypeIndep); + paramProvider.popScope(); + } + + // Reconfigure dynamic reaction model + bool reactionConfSuccess = true; + if (_dynReaction[0] && paramProvider.exists("reaction") && _dynReaction[0]->requiresConfiguration()) + { + paramProvider.pushScope("reaction"); + reactionConfSuccess = _dynReaction[0]->configure(paramProvider, _unitOpIdx, cadet::ParTypeIndep); + paramProvider.popScope(); + } - //FDjac = MatrixXd::Zero(numDofs(), numDofs()); // debug code + // FDjac = MatrixXd::Zero(numDofs(), numDofs()); // debug code - setPattern(_jac, true, _dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)); - setPattern(_jacDisc, true, _dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)); + setPattern(_jac, true, _dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)); + setPattern(_jacDisc, true, _dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)); - // the solver repetitively solves the linear system with a static pattern of the jacobian (set above). - // The goal of analyzePattern() is to reorder the nonzero elements of the matrix, such that the factorization step creates less fill-in - _linearSolver->analyzePattern(_jacDisc); + // the solver repetitively solves the linear system with a static pattern of the jacobian (set above). + // The goal of analyzePattern() is to reorder the nonzero elements of the matrix, such that the factorization step + // creates less fill-in + _linearSolver->analyzePattern(_jacDisc); - return bindingConfSuccess && reactionConfSuccess; - } + return bindingConfSuccess && reactionConfSuccess; +} - unsigned int LumpedRateModelWithoutPoresDG::threadLocalMemorySize() const CADET_NOEXCEPT - { - LinearMemorySizer lms; +unsigned int LumpedRateModelWithoutPoresDG::threadLocalMemorySize() const CADET_NOEXCEPT +{ + LinearMemorySizer lms; - // Memory for parts::cell::residualKernel = residualImpl() - if (_binding[0] && _binding[0]->requiresWorkspace()) - lms.addBlock(_binding[0]->workspaceSize(_disc.nComp, _disc.strideBound, _disc.nBound)); + // Memory for parts::cell::residualKernel = residualImpl() + if (_binding[0] && _binding[0]->requiresWorkspace()) + lms.addBlock(_binding[0]->workspaceSize(_disc.nComp, _disc.strideBound, _disc.nBound)); - if (_dynReaction[0]) - { - lms.addBlock(_dynReaction[0]->workspaceSize(_disc.nComp, _disc.strideBound, _disc.nBound)); - lms.add(_disc.strideBound); - lms.add(_disc.strideBound * (_disc.strideBound + _disc.nComp)); - } + if (_dynReaction[0]) + { + lms.addBlock(_dynReaction[0]->workspaceSize(_disc.nComp, _disc.strideBound, _disc.nBound)); + lms.add(_disc.strideBound); + lms.add(_disc.strideBound * (_disc.strideBound + _disc.nComp)); + } - lms.commit(); - const std::size_t resKernelSize = lms.bufferSize(); + lms.commit(); + const std::size_t resKernelSize = lms.bufferSize(); - // Memory for consistentInitialSensitivity() - lms.add(_disc.strideBound); - lms.add(_disc.strideBound); + // Memory for consistentInitialSensitivity() + lms.add(_disc.strideBound); + lms.add(_disc.strideBound); - lms.commit(); + lms.commit(); - // Memory for consistentInitialState() - lms.add(_nonlinearSolver->workspaceSize(_disc.strideBound) * sizeof(double)); - lms.add(_disc.strideBound); - lms.add(_disc.strideBound + _disc.nComp); - lms.add(_disc.strideBound + _disc.nComp); - lms.add(_disc.strideBound * _disc.strideBound); - lms.add(_disc.nComp); - lms.addBlock(_binding[0]->workspaceSize(_disc.nComp, _disc.strideBound, _disc.nBound)); - lms.addBlock(resKernelSize); + // Memory for consistentInitialState() + lms.add(_nonlinearSolver->workspaceSize(_disc.strideBound) * sizeof(double)); + lms.add(_disc.strideBound); + lms.add(_disc.strideBound + _disc.nComp); + lms.add(_disc.strideBound + _disc.nComp); + lms.add(_disc.strideBound * _disc.strideBound); + lms.add(_disc.nComp); + lms.addBlock(_binding[0]->workspaceSize(_disc.nComp, _disc.strideBound, _disc.nBound)); + lms.addBlock(resKernelSize); - lms.commit(); + lms.commit(); - return lms.bufferSize(); - } + return lms.bufferSize(); +} - void LumpedRateModelWithoutPoresDG::useAnalyticJacobian(const bool analyticJac) - { +void LumpedRateModelWithoutPoresDG::useAnalyticJacobian(const bool analyticJac) +{ - Indexer idxr(_disc); + Indexer idxr(_disc); #ifndef CADET_CHECK_ANALYTIC_JACOBIAN - _analyticJac = analyticJac; - if (!_analyticJac) - _jacobianAdDirs = _convDispOp.requiredADdirs(); - else - _jacobianAdDirs = 0; + _analyticJac = analyticJac; + if (!_analyticJac) + _jacobianAdDirs = _convDispOp.requiredADdirs(); + else + _jacobianAdDirs = 0; #else - _analyticJac = false; - _jacobianAdDirs = _convDispOp.requiredADdirs(); + _analyticJac = false; + _jacobianAdDirs = _convDispOp.requiredADdirs(); #endif - } +} - void LumpedRateModelWithoutPoresDG::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) - { - // TODO: reset pattern every time section? +void LumpedRateModelWithoutPoresDG::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac) +{ + // TODO: reset pattern every time section? - Indexer idxr(_disc); + Indexer idxr(_disc); - // ConvectionDispersionOperator tells us whether flow direction has changed - if (!_convDispOp.notifyDiscontinuousSectionTransition(t, secIdx, _jacInlet) && (secIdx != 0)) { - // (re)compute DG Jaconian blocks - updateSection(secIdx); - return; - } - else { - // (re)compute DG Jaconian blocks - updateSection(secIdx); - } + // ConvectionDispersionOperator tells us whether flow direction has changed + if (!_convDispOp.notifyDiscontinuousSectionTransition(t, secIdx, _jacInlet) && (secIdx != 0)) + { + // (re)compute DG Jaconian blocks + updateSection(secIdx); + return; + } + else + { + // (re)compute DG Jaconian blocks + updateSection(secIdx); + } - prepareADvectors(adJac); - } + prepareADvectors(adJac); +} - void LumpedRateModelWithoutPoresDG::setFlowRates(active const* in, active const* out) CADET_NOEXCEPT - { - _convDispOp.setFlowRates(in[0], out[0], _totalPorosity); - } - - void LumpedRateModelWithoutPoresDG::reportSolution(ISolutionRecorder& recorder, double const* const solution) const - { - Exporter expr(_disc, *this, solution); - recorder.beginUnitOperation(_unitOpIdx, *this, expr); - recorder.endUnitOperation(); - } +void LumpedRateModelWithoutPoresDG::setFlowRates(active const* in, active const* out) CADET_NOEXCEPT +{ + _convDispOp.setFlowRates(in[0], out[0], _totalPorosity); +} - void LumpedRateModelWithoutPoresDG::reportSolutionStructure(ISolutionRecorder& recorder) const - { - Exporter expr(_disc, *this, nullptr); - recorder.unitOperationStructure(_unitOpIdx, *this, expr); - } +void LumpedRateModelWithoutPoresDG::reportSolution(ISolutionRecorder& recorder, double const* const solution) const +{ + Exporter expr(_disc, *this, solution); + recorder.beginUnitOperation(_unitOpIdx, *this, expr); + recorder.endUnitOperation(); +} +void LumpedRateModelWithoutPoresDG::reportSolutionStructure(ISolutionRecorder& recorder) const +{ + Exporter expr(_disc, *this, nullptr); + recorder.unitOperationStructure(_unitOpIdx, *this, expr); +} - unsigned int LumpedRateModelWithoutPoresDG::requiredADdirs() const CADET_NOEXCEPT - { - const unsigned int numDirsBinding = maxBindingAdDirs(); +unsigned int LumpedRateModelWithoutPoresDG::requiredADdirs() const CADET_NOEXCEPT +{ + const unsigned int numDirsBinding = maxBindingAdDirs(); #ifndef CADET_CHECK_ANALYTIC_JACOBIAN - return numDirsBinding + _jacobianAdDirs; + return numDirsBinding + _jacobianAdDirs; #else - return numDirsBinding + _convDispOp.requiredADdirs(); + return numDirsBinding + _convDispOp.requiredADdirs(); #endif - } - - void LumpedRateModelWithoutPoresDG::prepareADvectors(const AdJacobianParams& adJac) const - { - // Early out if AD Jacobian is disabled - if (!adJac.adY) - return; - - Indexer idxr(_disc); - // @todo use more efficient seed vectors. currently, we treat the jacobian as banded, but the pattern is actually more sparse when multiple components are considered - // (note that active type directions are limited) - // We have different jacobian structure for exact integration and collocation DG scheme, i.e. we need different seed vectors - // collocation DG: 2 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend on the next and last N_n liquid phase entries of same component) - // ex. int. DG: 4 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend on the next and last 2*N_n liquid phase entries of same component) - - int lowerBandwidth = (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); - int upperBandwidth = lowerBandwidth; +} - ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + _disc.nComp, adJac.adDirOffset, _jac.rows(), lowerBandwidth, upperBandwidth, lowerBandwidth); - } - - /** - * @brief Extracts the system Jacobian from band compressed AD seed vectors - * @param [in] adRes Residual vector of AD datatypes with band compressed seed vectors - * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) - */ - void LumpedRateModelWithoutPoresDG::extractJacobianFromAD(active const* const adRes, unsigned int adDirOffset) - { - Indexer idxr(_disc); - - const active* const adVec = adRes + idxr.offsetC(); +void LumpedRateModelWithoutPoresDG::prepareADvectors(const AdJacobianParams& adJac) const +{ + // Early out if AD Jacobian is disabled + if (!adJac.adY) + return; + + Indexer idxr(_disc); + // @todo use more efficient seed vectors. currently, we treat the jacobian as banded, but the pattern is actually + // more sparse when multiple components are considered (note that active type directions are limited) We have + // different jacobian structure for exact integration and collocation DG scheme, i.e. we need different seed vectors + // collocation DG: 2 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend on the next + // and last N_n liquid phase entries of same component) + // ex. int. DG: 4 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend on the next + // and last 2*N_n liquid phase entries of same component) + + int lowerBandwidth = + (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); + int upperBandwidth = lowerBandwidth; + + ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + _disc.nComp, adJac.adDirOffset, _jac.rows(), lowerBandwidth, + upperBandwidth, lowerBandwidth); +} + +/** + * @brief Extracts the system Jacobian from band compressed AD seed vectors + * @param [in] adRes Residual vector of AD datatypes with band compressed seed vectors + * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) + */ +void LumpedRateModelWithoutPoresDG::extractJacobianFromAD(active const* const adRes, unsigned int adDirOffset) +{ + Indexer idxr(_disc); - const int lowerBandwidth = (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); - const int upperBandwidth = lowerBandwidth; - const int stride = lowerBandwidth + 1 + upperBandwidth; + const active* const adVec = adRes + idxr.offsetC(); - int diagDir = lowerBandwidth; - const int nRows = _jac.rows(); - const int colOffset = 0; + const int lowerBandwidth = + (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); + const int upperBandwidth = lowerBandwidth; + const int stride = lowerBandwidth + 1 + upperBandwidth; - ad::extractBandedBlockEigenJacobianFromAd(adVec, adDirOffset, diagDir, lowerBandwidth, upperBandwidth, colOffset, nRows, _jac); + int diagDir = lowerBandwidth; + const int nRows = _jac.rows(); + const int colOffset = 0; - } + ad::extractBandedBlockEigenJacobianFromAd(adVec, adDirOffset, diagDir, lowerBandwidth, upperBandwidth, colOffset, + nRows, _jac); +} #ifdef CADET_CHECK_ANALYTIC_JACOBIAN - /** - * @brief Compares the analytical Jacobian with a Jacobian derived by AD - * @details The analytical Jacobian is assumed to be stored in the corresponding band matrices. - * @param [in] adRes Residual vector of AD datatypes with band compressed seed vectors - * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) - */ - void LumpedRateModelWithoutPoresDG::checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const - { - Indexer idxr(_disc); +/** + * @brief Compares the analytical Jacobian with a Jacobian derived by AD + * @details The analytical Jacobian is assumed to be stored in the corresponding band matrices. + * @param [in] adRes Residual vector of AD datatypes with band compressed seed vectors + * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) + */ +void LumpedRateModelWithoutPoresDG::checkAnalyticJacobianAgainstAd(active const* const adRes, + unsigned int adDirOffset) const +{ + Indexer idxr(_disc); - const int lowerBandwidth = (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); - const int upperBandwidth = lowerBandwidth; - const int stride = lowerBandwidth + 1 + upperBandwidth; + const int lowerBandwidth = + (_disc.exactInt) ? 2 * _disc.nNodes * idxr.strideColNode() : _disc.nNodes * idxr.strideColNode(); + const int upperBandwidth = lowerBandwidth; + const int stride = lowerBandwidth + 1 + upperBandwidth; - const double maxDiff = ad::compareBandedEigenJacobianWithAd(adRes + idxr.offsetC(), adDirOffset, lowerBandwidth, lowerBandwidth, upperBandwidth, 0, _jac.rows(), _jac, 0); + const double maxDiff = ad::compareBandedEigenJacobianWithAd( + adRes + idxr.offsetC(), adDirOffset, lowerBandwidth, lowerBandwidth, upperBandwidth, 0, _jac.rows(), _jac, 0); - if (maxDiff > 1e-6) - int jojo = 0; + if (maxDiff > 1e-6) + int jojo = 0; - LOG(Debug) << "AD dir offset: " << adDirOffset << " DiagBlockSize: " << stride << " MaxDiff: " << maxDiff; - } + LOG(Debug) << "AD dir offset: " << adDirOffset << " DiagBlockSize: " << stride << " MaxDiff: " << maxDiff; +} #endif - int LumpedRateModelWithoutPoresDG::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) - { - BENCH_SCOPE(_timerResidual); +int LumpedRateModelWithoutPoresDG::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerResidual); - _factorizeJacobian = true; + _factorizeJacobian = true; - if (_analyticJac) - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); - else - return residualWithJacobian(simTime, ConstSimulationState{ simState.vecStateY, nullptr }, nullptr, adJac, threadLocalMem); - } + if (_analyticJac) + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); + else + return residualWithJacobian(simTime, ConstSimulationState{simState.vecStateY, nullptr}, nullptr, adJac, + threadLocalMem); +} - int LumpedRateModelWithoutPoresDG::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem) - { - BENCH_SCOPE(_timerResidual); +int LumpedRateModelWithoutPoresDG::residual(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerResidual); - // Evaluate residual do not compute Jacobian or parameter sensitivities - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); - } + // Evaluate residual do not compute Jacobian or parameter sensitivities + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); +} - int LumpedRateModelWithoutPoresDG::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) - { - BENCH_SCOPE(_timerResidual); +int LumpedRateModelWithoutPoresDG::residualWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerResidual); - //FDjac = calcFDJacobian(static_cast(simState.vecStateY), static_cast(simState.vecStateYdot), simTime, threadLocalMem, 2.0); // debug code + // FDjac = calcFDJacobian(static_cast(simState.vecStateY), static_cast(simState.vecStateYdot), simTime, threadLocalMem, 2.0); // debug code - // Evaluate residual, use AD for Jacobian if required but do not evaluate parameter derivatives - return residual(simTime, simState, res, adJac, threadLocalMem, true, false); - } + // Evaluate residual, use AD for Jacobian if required but do not evaluate parameter derivatives + return residual(simTime, simState, res, adJac, threadLocalMem, true, false); +} - int LumpedRateModelWithoutPoresDG::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, - const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity) - { - if (updateJacobian) - { - _factorizeJacobian = true; +int LumpedRateModelWithoutPoresDG::residual(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, + bool paramSensitivity) +{ + if (updateJacobian) + { + _factorizeJacobian = true; #ifndef CADET_CHECK_ANALYTIC_JACOBIAN - if (_analyticJac) - { - if (paramSensitivity) - { - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); - - // Copy AD residuals to original residuals vector - if (res) - ad::copyFromAd(adJac.adRes, res, numDofs()); - - return retCode; - } - else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); - } - else - { - // Compute Jacobian via AD + if (_analyticJac) + { + if (paramSensitivity) + { + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); - // Copy over state vector to AD state vector (without changing directional values to keep seed vectors) - // and initialize residuals with zero (also resetting directional values) - ad::copyToAd(simState.vecStateY, adJac.adY, numDofs()); - // @todo Check if this is necessary - ad::resetAd(adJac.adRes, numDofs()); + // Copy AD residuals to original residuals vector + if (res) + ad::copyFromAd(adJac.adRes, res, numDofs()); - // Evaluate with AD enabled - int retCode = 0; - if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); - else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + return retCode; + } + else + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); + } + else + { + // Compute Jacobian via AD + + // Copy over state vector to AD state vector (without changing directional values to keep seed vectors) + // and initialize residuals with zero (also resetting directional values) + ad::copyToAd(simState.vecStateY, adJac.adY, numDofs()); + // @todo Check if this is necessary + ad::resetAd(adJac.adRes, numDofs()); + + // Evaluate with AD enabled + int retCode = 0; + if (paramSensitivity) + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + else + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); - // Copy AD residuals to original residuals vector - if (res) - ad::copyFromAd(adJac.adRes, res, numDofs()); + // Copy AD residuals to original residuals vector + if (res) + ad::copyFromAd(adJac.adRes, res, numDofs()); - // Extract Jacobian - extractJacobianFromAD(adJac.adRes, adJac.adDirOffset); + // Extract Jacobian + extractJacobianFromAD(adJac.adRes, adJac.adDirOffset); - return retCode; - } + return retCode; + } #else - // Compute Jacobian via AD - - // Copy over state vector to AD state vector (without changing directional values to keep seed vectors) - // and initialize residuals with zero (also resetting directional values) - ad::copyToAd(simState.vecStateY, adJac.adY, numDofs()); - // @todo Check if this is necessary - ad::resetAd(adJac.adRes, numDofs()); - - // Evaluate with AD enabled - int retCode = 0; - if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); - else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); - - // Only do comparison if we have a residuals vector (which is not always the case) - if (res) - { - // Evaluate with analytical Jacobian which is stored in the band matrices - retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + // Compute Jacobian via AD + + // Copy over state vector to AD state vector (without changing directional values to keep seed vectors) + // and initialize residuals with zero (also resetting directional values) + ad::copyToAd(simState.vecStateY, adJac.adY, numDofs()); + // @todo Check if this is necessary + ad::resetAd(adJac.adRes, numDofs()); + + // Evaluate with AD enabled + int retCode = 0; + if (paramSensitivity) + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); + else + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); + + // Only do comparison if we have a residuals vector (which is not always the case) + if (res) + { + // Evaluate with analytical Jacobian which is stored in the band matrices + retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); - // Compare AD with anaytic Jacobian - checkAnalyticJacobianAgainstAd(adJac.adRes, adJac.adDirOffset); - } + // Compare AD with anaytic Jacobian + checkAnalyticJacobianAgainstAd(adJac.adRes, adJac.adDirOffset); + } - // Extract Jacobian - extractJacobianFromAD(adJac.adRes, adJac.adDirOffset); + // Extract Jacobian + extractJacobianFromAD(adJac.adRes, adJac.adDirOffset); - return retCode; + return retCode; #endif - } - else - { - if (paramSensitivity) - { - // initialize residuals with zero - // @todo Check if this is necessary - ad::resetAd(adJac.adRes, numDofs()); + } + else + { + if (paramSensitivity) + { + // initialize residuals with zero + // @todo Check if this is necessary + ad::resetAd(adJac.adRes, numDofs()); - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); - // Copy AD residuals to original residuals vector - if (res) - ad::copyFromAd(adJac.adRes, res, numDofs()); + // Copy AD residuals to original residuals vector + if (res) + ad::copyFromAd(adJac.adRes, res, numDofs()); - return retCode; - } - else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); - } + return retCode; } + else + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); + } +} + +template +int LumpedRateModelWithoutPoresDG::residualImpl(double t, unsigned int secIdx, StateType const* const y_, + double const* const yDot_, ResidualType* const res_, + util::ThreadLocalStorage& threadLocalMem) +{ + Indexer idxr(_disc); - template - int LumpedRateModelWithoutPoresDG::residualImpl(double t, unsigned int secIdx, StateType const* const y_, double const* const yDot_, ResidualType* const res_, util::ThreadLocalStorage& threadLocalMem) - { - Indexer idxr(_disc); - - bool success = 1; - - // determine wether we have a section switch. If so, set velocity, dispersion, newStaticJac + bool success = 1; - if (wantJac) { + // determine wether we have a section switch. If so, set velocity, dispersion, newStaticJac - if (!wantRes || _disc.newStaticJac) { + if (wantJac) + { - success = _convDispOp.calcStaticAnaJacobian(_jac, _jacInlet); + if (!wantRes || _disc.newStaticJac) + { - _disc.newStaticJac = false; - } + success = _convDispOp.calcStaticAnaJacobian(_jac, _jacInlet); - if (cadet_unlikely(!success)) - LOG(Error) << "Jacobian pattern did not fit the Jacobian estimation"; + _disc.newStaticJac = false; + } - } + if (cadet_unlikely(!success)) + LOG(Error) << "Jacobian pattern did not fit the Jacobian estimation"; + } - /* Compute bulk convection dispersion residual */ - if (wantRes) - _convDispOp.residual(*this, t, secIdx, y_, yDot_, res_, typename cadet::ParamSens::enabled()); + /* Compute bulk convection dispersion residual */ + if (wantRes) + _convDispOp.residual(*this, t, secIdx, y_, yDot_, res_, typename cadet::ParamSens::enabled()); - /* Compute binding, reaction residual */ + /* Compute binding, reaction residual */ #ifdef CADET_PARALLELIZE tbb::parallel_for(std::size_t(0), static_cast(_disc.nPoints), [&](std::size_t blk) #else - for (unsigned int blk = 0; blk < _disc.nPoints; ++blk) + for (unsigned int blk = 0; blk < _disc.nPoints; ++blk) #endif { - linalg::BandedEigenSparseRowIterator jacIt(_jac, blk * idxr.strideColNode()); - StateType const* const localY = y_ + idxr.offsetC() + idxr.strideColNode() * blk; - - const parts::cell::CellParameters cellResParams - { - _disc.nComp, - _disc.nBound, - _disc.boundOffset, - _disc.strideBound, - _binding[0]->reactionQuasiStationarity(), - _totalPorosity, - nullptr, - _binding[0], - (_dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)) ? _dynReaction[0] : nullptr - }; - - // position of current column node (z coordinate) - needed in externally dependent adsorption kinetic - double z = _convDispOp.relativeCoordinate(blk); - if (wantRes) - { - ResidualType* const localRes = res_ + idxr.offsetC() + idxr.strideColNode() * blk; - double const* const localYdot = yDot_ ? yDot_ + idxr.offsetC() + idxr.strideColNode() * blk : nullptr; - parts::cell::residualKernel( - t, secIdx, ColumnPosition{ z, 0.0, 0.0 }, localY, localYdot, localRes, jacIt, cellResParams, threadLocalMem.get() - ); - } - else - { - parts::cell::residualKernel( - t, secIdx, ColumnPosition{ z, 0.0, 0.0 }, localY, nullptr, nullptr, jacIt, cellResParams, threadLocalMem.get() - ); - } + linalg::BandedEigenSparseRowIterator jacIt(_jac, blk * idxr.strideColNode()); + StateType const* const localY = y_ + idxr.offsetC() + idxr.strideColNode() * blk; + + const parts::cell::CellParameters cellResParams{ + _disc.nComp, + _disc.nBound, + _disc.boundOffset, + _disc.strideBound, + _binding[0]->reactionQuasiStationarity(), + _totalPorosity, + nullptr, + _binding[0], + (_dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)) ? _dynReaction[0] : nullptr}; + + // position of current column node (z coordinate) - needed in externally dependent adsorption kinetic + double z = _convDispOp.relativeCoordinate(blk); + if (wantRes) + { + ResidualType* const localRes = res_ + idxr.offsetC() + idxr.strideColNode() * blk; + double const* const localYdot = yDot_ ? yDot_ + idxr.offsetC() + idxr.strideColNode() * blk : nullptr; + parts::cell::residualKernel( + t, secIdx, ColumnPosition{z, 0.0, 0.0}, localY, localYdot, localRes, jacIt, cellResParams, + threadLocalMem.get()); + } + else + { + parts::cell::residualKernel( + t, secIdx, ColumnPosition{z, 0.0, 0.0}, localY, nullptr, nullptr, jacIt, cellResParams, + threadLocalMem.get()); + } } CADET_PARFOR_END; // Handle inlet DOFs, which are simply copied to res @@ -670,1203 +719,1260 @@ namespace cadet res_[i] = y_[i]; return 0; - } - - int LumpedRateModelWithoutPoresDG::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) - { - BENCH_SCOPE(_timerResidualSens); +} - // Evaluate residual for all parameters using AD in vector mode and at the same time update the - // Jacobian (in one AD run, if analytic Jacobians are disabled) - return residual(simTime, simState, nullptr, adJac, threadLocalMem, true, true); - } - - int LumpedRateModelWithoutPoresDG::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem) - { - BENCH_SCOPE(_timerResidualSens); +int LumpedRateModelWithoutPoresDG::residualSensFwdWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerResidualSens); - // Evaluate residual for all parameters using AD in vector mode - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adRes, threadLocalMem); - } + // Evaluate residual for all parameters using AD in vector mode and at the same time update the + // Jacobian (in one AD run, if analytic Jacobians are disabled) + return residual(simTime, simState, nullptr, adJac, threadLocalMem, true, true); +} - int LumpedRateModelWithoutPoresDG::residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3) - { - BENCH_SCOPE(_timerResidualSens); +int LumpedRateModelWithoutPoresDG::residualSensFwdAdOnly(const SimulationTime& simTime, + const ConstSimulationState& simState, active* const adRes, + util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerResidualSens); + + // Evaluate residual for all parameters using AD in vector mode + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, adRes, threadLocalMem); +} + +int LumpedRateModelWithoutPoresDG::residualSensFwdCombine(const SimulationTime& simTime, + const ConstSimulationState& simState, + const std::vector& yS, + const std::vector& ySdot, + const std::vector& resS, active const* adRes, + double* const tmp1, double* const tmp2, double* const tmp3) +{ + BENCH_SCOPE(_timerResidualSens); - // tmp1 stores result of (dF / dy) * s - // tmp2 stores result of (dF / dyDot) * sDot + // tmp1 stores result of (dF / dy) * s + // tmp2 stores result of (dF / dyDot) * sDot - for (std::size_t param = 0; param < yS.size(); ++param) - { - // Directional derivative (dF / dy) * s - multiplyWithJacobian(SimulationTime{ 0.0, 0u }, ConstSimulationState{ nullptr, nullptr }, yS[param], 1.0, 0.0, tmp1); + for (std::size_t param = 0; param < yS.size(); ++param) + { + // Directional derivative (dF / dy) * s + multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, yS[param], 1.0, 0.0, + tmp1); - // Directional derivative (dF / dyDot) * sDot - multiplyWithDerivativeJacobian(SimulationTime{ 0.0, 0u }, ConstSimulationState{ nullptr, nullptr }, ySdot[param], tmp2); + // Directional derivative (dF / dyDot) * sDot + multiplyWithDerivativeJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{nullptr, nullptr}, ySdot[param], + tmp2); - double* const ptrResS = resS[param]; + double* const ptrResS = resS[param]; - BENCH_START(_timerResidualSensPar); + BENCH_START(_timerResidualSensPar); - // Complete sens residual is the sum: - // TODO: Chunk TBB loop + // Complete sens residual is the sum: + // TODO: Chunk TBB loop #ifdef CADET_PARALLELIZE tbb::parallel_for(std::size_t(0), static_cast(numDofs()), [&](std::size_t i) #else - for (unsigned int i = 0; i < numDofs(); ++i) + for (unsigned int i = 0; i < numDofs(); ++i) #endif { - ptrResS[i] = tmp1[i] + tmp2[i] + adRes[i].getADValue(param); + ptrResS[i] = tmp1[i] + tmp2[i] + adRes[i].getADValue(param); } CADET_PARFOR_END; BENCH_STOP(_timerResidualSensPar); - } - - return 0; - } - - /** - * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, \dot{y}\right) @f$) - * @details Actually, the operation @f$ z = \alpha \frac{\partial F}{\partial y} x + \beta z @f$ is performed. - * - * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ once - * before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. - * @param [in] simTime Current simulation time point - * @param [in] simState Simulation state vectors - * @param [in] yS Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial y} @f$ - * @param [in] alpha Factor @f$ \alpha @f$ in front of @f$ \frac{\partial F}{\partial y} @f$ - * @param [in] beta Factor @f$ \beta @f$ in front of @f$ z @f$ - * @param [in,out] ret Vector @f$ z @f$ which stores the result of the operation - */ - void LumpedRateModelWithoutPoresDG::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret) - { - Indexer idxr(_disc); - - // Handle identity matrix of inlet DOFs - for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - { - ret[comp] = alpha * yS[comp] + beta * ret[comp]; - } + } + + return 0; +} + +/** + * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, + * \dot{y}\right) @f$) + * @details Actually, the operation @f$ z = \alpha \frac{\partial F}{\partial y} x + \beta z @f$ is performed. + * + * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ + * once before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. + * @param [in] simTime Current simulation time point + * @param [in] simState Simulation state vectors + * @param [in] yS Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial y} @f$ + * @param [in] alpha Factor @f$ \alpha @f$ in front of @f$ \frac{\partial F}{\partial y} @f$ + * @param [in] beta Factor @f$ \beta @f$ in front of @f$ z @f$ + * @param [in,out] ret Vector @f$ z @f$ which stores the result of the operation + */ +void LumpedRateModelWithoutPoresDG::multiplyWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, double const* yS, + double alpha, double beta, double* ret) +{ + Indexer idxr(_disc); - // Main Jacobian - Eigen::Map ret_vec(ret + idxr.offsetC(), numPureDofs()); - Eigen::Map yS_vec(yS + idxr.offsetC(), numPureDofs()); - ret_vec = alpha * _jac * yS_vec + beta * ret_vec; + // Handle identity matrix of inlet DOFs + for (unsigned int comp = 0; comp < _disc.nComp; ++comp) + { + ret[comp] = alpha * yS[comp] + beta * ret[comp]; + } - // Map inlet DOFs to the column inlet (first bulk cells) - // Inlet at z = 0 for forward flow, at z = L for backward flow. - unsigned int offInlet = _convDispOp.forwardFlow() ? 0 : (_disc.nCol - 1u) * idxr.strideColCell(); + // Main Jacobian + Eigen::Map ret_vec(ret + idxr.offsetC(), numPureDofs()); + Eigen::Map yS_vec(yS + idxr.offsetC(), numPureDofs()); + ret_vec = alpha * _jac * yS_vec + beta * ret_vec; - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int node = 0; node < (_disc.exactInt ? _disc.nNodes : 1); node++) { - ret[idxr.offsetC() + offInlet + comp * idxr.strideColComp() + node * idxr.strideColNode()] += alpha * _jacInlet(node, 0) * yS[comp]; - } - } - } + // Map inlet DOFs to the column inlet (first bulk cells) + // Inlet at z = 0 for forward flow, at z = L for backward flow. + unsigned int offInlet = _convDispOp.forwardFlow() ? 0 : (_disc.nCol - 1u) * idxr.strideColCell(); - /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector - * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. - * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). - * @param [in] simTime Current simulation time point - * @param [in] simState Simulation state vectors - * @param [in] sDot Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ - * @param [out] ret Vector @f$ z @f$ which stores the result of the operation - */ - void LumpedRateModelWithoutPoresDG::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret) + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int node = 0; node < (_disc.exactInt ? _disc.nNodes : 1); node++) { - Indexer idxr(_disc); - const double invBeta = (1.0 / static_cast(_totalPorosity) - 1.0); - - _convDispOp.multiplyWithDerivativeJacobian(simTime, sDot, ret); - - for (unsigned int node = 0; node < _disc.nPoints; ++node) - { - const unsigned int localOffset = idxr.offsetC() + node * idxr.strideColNode(); - double const* const localSdot = sDot + localOffset; - double* const localRet = ret + localOffset; - - parts::cell::multiplyWithDerivativeJacobianKernel(localSdot, localRet, _disc.nComp, _disc.nBound, _disc.boundOffset, _disc.strideBound, _binding[0]->reactionQuasiStationarity(), 1.0, invBeta); - } - - // Handle inlet DOFs (all algebraic) - std::fill_n(ret, _disc.nComp, 0.0); + ret[idxr.offsetC() + offInlet + comp * idxr.strideColComp() + node * idxr.strideColNode()] += + alpha * _jacInlet(node, 0) * yS[comp]; } + } +} + +/** + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ + * with a given vector + * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. + * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). + * @param [in] simTime Current simulation time point + * @param [in] simState Simulation state vectors + * @param [in] sDot Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ + * @param [out] ret Vector @f$ z @f$ which stores the result of the operation + */ +void LumpedRateModelWithoutPoresDG::multiplyWithDerivativeJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + double const* sDot, double* ret) +{ + Indexer idxr(_disc); + const double invBeta = (1.0 / static_cast(_totalPorosity) - 1.0); - void LumpedRateModelWithoutPoresDG::setExternalFunctions(IExternalFunction** extFuns, unsigned int size) - { - if (_binding[0]) - _binding[0]->setExternalFunctions(extFuns, size); - } + _convDispOp.multiplyWithDerivativeJacobian(simTime, sDot, ret); - unsigned int LumpedRateModelWithoutPoresDG::localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT - { - // Inlets are duplicated so need to be accounted for - if (static_cast(_convDispOp.currentVelocity()) >= 0.0) - // Forward Flow: outlet is last cell - return _disc.nComp + (_disc.nPoints - 1) * (_disc.nComp + _disc.strideBound); - else - // Backward flow: Outlet is first cell - return _disc.nComp; - } + for (unsigned int node = 0; node < _disc.nPoints; ++node) + { + const unsigned int localOffset = idxr.offsetC() + node * idxr.strideColNode(); + double const* const localSdot = sDot + localOffset; + double* const localRet = ret + localOffset; - unsigned int LumpedRateModelWithoutPoresDG::localInletComponentIndex(unsigned int port) const CADET_NOEXCEPT - { - return 0; - } + parts::cell::multiplyWithDerivativeJacobianKernel( + localSdot, localRet, _disc.nComp, _disc.nBound, _disc.boundOffset, _disc.strideBound, + _binding[0]->reactionQuasiStationarity(), 1.0, invBeta); + } - unsigned int LumpedRateModelWithoutPoresDG::localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT - { - return 1; - } + // Handle inlet DOFs (all algebraic) + std::fill_n(ret, _disc.nComp, 0.0); +} - unsigned int LumpedRateModelWithoutPoresDG::localInletComponentStride(unsigned int port) const CADET_NOEXCEPT - { - return 1; - } +void LumpedRateModelWithoutPoresDG::setExternalFunctions(IExternalFunction** extFuns, unsigned int size) +{ + if (_binding[0]) + _binding[0]->setExternalFunctions(extFuns, size); +} - void LumpedRateModelWithoutPoresDG::expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut) - { - // @todo Write this function - } +unsigned int LumpedRateModelWithoutPoresDG::localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT +{ + // Inlets are duplicated so need to be accounted for + if (static_cast(_convDispOp.currentVelocity()) >= 0.0) + // Forward Flow: outlet is last cell + return _disc.nComp + (_disc.nPoints - 1) * (_disc.nComp + _disc.strideBound); + else + // Backward flow: Outlet is first cell + return _disc.nComp; +} + +unsigned int LumpedRateModelWithoutPoresDG::localInletComponentIndex(unsigned int port) const CADET_NOEXCEPT +{ + return 0; +} - /** - * @brief Computes the solution of the linear system involving the system Jacobian - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the - * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) \f$, - * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p rhs. - * - * @param [in] t Current time point - * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) - * @param [in] outerTol Error tolerance for the solution of the linear system from outer Newton iteration - * @param [in,out] rhs On entry the right hand side of the linear equation system, on exit the solution - * @param [in] weight Vector with error weights - * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is evaluated - * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error - */ - int LumpedRateModelWithoutPoresDG::linearSolve(double t, double alpha, double outerTol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) - { - BENCH_SCOPE(_timerLinearSolve); +unsigned int LumpedRateModelWithoutPoresDG::localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT +{ + return 1; +} - Indexer idxr(_disc); +unsigned int LumpedRateModelWithoutPoresDG::localInletComponentStride(unsigned int port) const CADET_NOEXCEPT +{ + return 1; +} - bool success = true; - bool result = true; +void LumpedRateModelWithoutPoresDG::expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, + double* expandOut) +{ + // @todo Write this function +} + +/** + * @brief Computes the solution of the linear system involving the system Jacobian + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + * b \f] has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the point + * \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) \f$, may help + * with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p rhs. + * + * @param [in] t Current time point + * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) + * @param [in] outerTol Error tolerance for the solution of the linear system from outer Newton iteration + * @param [in,out] rhs On entry the right hand side of the linear equation system, on exit the solution + * @param [in] weight Vector with error weights + * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is + * evaluated + * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error + */ +int LumpedRateModelWithoutPoresDG::linearSolve(double t, double alpha, double outerTol, double* const rhs, + double const* const weight, const ConstSimulationState& simState) +{ + BENCH_SCOPE(_timerLinearSolve); - // Assemble Jacobian - assembleDiscretizedJacobian(alpha, idxr); + Indexer idxr(_disc); - // solve J x = rhs - Eigen::Map r(rhs, numDofs()); + bool success = true; + bool result = true; - // Factorize Jacobian only if required - if (_factorizeJacobian) - { - _linearSolver->factorize(_jacDisc); + // Assemble Jacobian + assembleDiscretizedJacobian(alpha, idxr); - if (_linearSolver->info() != Success) { - LOG(Error) << "factorization failed"; - success = false; - } - } + // solve J x = rhs + Eigen::Map r(rhs, numDofs()); - // Use the factors to solve the linear system - r.segment(idxr.offsetC(), numPureDofs()) = _linearSolver->solve(r.segment(idxr.offsetC(), numPureDofs())); + // Factorize Jacobian only if required + if (_factorizeJacobian) + { + _linearSolver->factorize(_jacDisc); - if (_linearSolver->info() != Success) { - LOG(Error) << "solve() failed"; - result = false; - } + if (_linearSolver->info() != Success) + { + LOG(Error) << "factorization failed"; + success = false; + } + } - // Handle inlet DOFs: - // Inlet at z = 0 for forward flow, at z = L for backward flow. - unsigned int offInlet = _convDispOp.forwardFlow() ? 0 : (_disc.nCol - 1u) * idxr.strideColCell(); + // Use the factors to solve the linear system + r.segment(idxr.offsetC(), numPureDofs()) = _linearSolver->solve(r.segment(idxr.offsetC(), numPureDofs())); - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - for (unsigned int node = 0; node < (_disc.exactInt ? _disc.nNodes : 1); node++) { - r[idxr.offsetC() + offInlet + comp * idxr.strideColComp() + node * idxr.strideColNode()] += _jacInlet(node, 0) * r[comp]; - } - } + if (_linearSolver->info() != Success) + { + LOG(Error) << "solve() failed"; + result = false; + } - return (success && result) ? 0 : 1; - } + // Handle inlet DOFs: + // Inlet at z = 0 for forward flow, at z = L for backward flow. + unsigned int offInlet = _convDispOp.forwardFlow() ? 0 : (_disc.nCol - 1u) * idxr.strideColCell(); - /** - * @brief Assembles the Jacobian of the time-discretized equations - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The system Jacobian of the original equations, - * \f[ \frac{\partial F}{\partial y}, \f] - * is already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible - * for adding - * \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] - * to the system Jacobian, which yields the Jacobian of the time-discretized equations - * \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] - * when a BDF method is used. The time integrator needs to solve this equation for @f$ y_0 @f$, which requires - * the solution of the linear system mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). - * - * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) - * @param [in] idxr Indexer - */ - void LumpedRateModelWithoutPoresDG::assembleDiscretizedJacobian(double alpha, const Indexer& idxr) + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + for (unsigned int node = 0; node < (_disc.exactInt ? _disc.nNodes : 1); node++) { - // set to static jacobian entries - _jacDisc = _jac; - - // add time derivative jacobian entries (dc_b / dt terms) - _convDispOp.addTimeDerivativeToJacobian(alpha, _jacDisc); - - // add time derivative jacobian entries (dc_s / dt terms) - const double invBeta = 1.0 / static_cast(_totalPorosity) - 1.0; - linalg::BandedEigenSparseRowIterator jac(_jacDisc, 0); - for (unsigned int j = 0; j < _disc.nPoints; ++j) - { - addTimeDerivativeToJacobianNode(jac, idxr, alpha, invBeta); - } + r[idxr.offsetC() + offInlet + comp * idxr.strideColComp() + node * idxr.strideColNode()] += + _jacInlet(node, 0) * r[comp]; } - /** - * @brief Adds Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ to cell of system Jacobian - * @details Actually adds @f$ \alpha \frac{\partial F}{\partial \dot{y}} @f$, which is useful - * for constructing the linear system in BDF time discretization. - * @param [in,out] jac On entry, RowIterator pointing to the beginning of a cell; - * on exit, the iterator points to the end of the cell - * @param [in] idxr Indexer - * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) - * @param [in] invBeta Inverse porosity term @f$\frac{1}{\beta}@f$ - */ - void LumpedRateModelWithoutPoresDG::addTimeDerivativeToJacobianNode(linalg::BandedEigenSparseRowIterator& jac, const Indexer& idxr, double alpha, double invBeta) const - { - // Mobile phase - for (int comp = 0; comp < static_cast(_disc.nComp); ++comp, ++jac) - { - // dc / dt is handled by convection dispersion operator - - // Add derivative with respect to dq / dt to Jacobian - for (int i = 0; i < static_cast(_disc.nBound[comp]); ++i) - { - // Index explanation: - // -comp -> go back to beginning of liquid phase - // + strideColLiquid() skip to solid phase - // + offsetBoundComp() jump to component (skips all bound states of previous components) - // + i go to current bound state - jac[idxr.strideColLiquid() - comp + idxr.offsetBoundComp(comp) + i] += alpha * invBeta; - } - } + } + + return (success && result) ? 0 : 1; +} + +/** + * @brief Assembles the Jacobian of the time-discretized equations + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + * b \f] has to be solved. The system Jacobian of the original equations, \f[ \frac{\partial F}{\partial y}, \f] is + * already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible for + * adding \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] to the system Jacobian, which yields the Jacobian of the + * time-discretized equations \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] when a BDF method is used. + * The time integrator needs to solve this equation for @f$ y_0 @f$, which requires the solution of the linear system + * mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). + * + * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) + * @param [in] idxr Indexer + */ +void LumpedRateModelWithoutPoresDG::assembleDiscretizedJacobian(double alpha, const Indexer& idxr) +{ + // set to static jacobian entries + _jacDisc = _jac; - // Solid phase - int const* const qsReaction = _binding[0]->reactionQuasiStationarity(); - for (unsigned int bnd = 0; bnd < _disc.strideBound; ++bnd, ++jac) - { - // Add derivative with respect to dynamic states to Jacobian - if (qsReaction[bnd]) - continue; + // add time derivative jacobian entries (dc_b / dt terms) + _convDispOp.addTimeDerivativeToJacobian(alpha, _jacDisc); - // Add derivative with respect to dq / dt to Jacobian - jac[0] += alpha; - } - } + // add time derivative jacobian entries (dc_s / dt terms) + const double invBeta = 1.0 / static_cast(_totalPorosity) - 1.0; + linalg::BandedEigenSparseRowIterator jac(_jacDisc, 0); + for (unsigned int j = 0; j < _disc.nPoints; ++j) + { + addTimeDerivativeToJacobianNode(jac, idxr, alpha, invBeta); + } +} +/** + * @brief Adds Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ to cell of system Jacobian + * @details Actually adds @f$ \alpha \frac{\partial F}{\partial \dot{y}} @f$, which is useful + * for constructing the linear system in BDF time discretization. + * @param [in,out] jac On entry, RowIterator pointing to the beginning of a cell; + * on exit, the iterator points to the end of the cell + * @param [in] idxr Indexer + * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) + * @param [in] invBeta Inverse porosity term @f$\frac{1}{\beta}@f$ + */ +void LumpedRateModelWithoutPoresDG::addTimeDerivativeToJacobianNode(linalg::BandedEigenSparseRowIterator& jac, + const Indexer& idxr, double alpha, + double invBeta) const +{ + // Mobile phase + for (int comp = 0; comp < static_cast(_disc.nComp); ++comp, ++jac) + { + // dc / dt is handled by convection dispersion operator - void LumpedRateModelWithoutPoresDG::applyInitialCondition(const SimulationState& simState) const + // Add derivative with respect to dq / dt to Jacobian + for (int i = 0; i < static_cast(_disc.nBound[comp]); ++i) { - Indexer idxr(_disc); + // Index explanation: + // -comp -> go back to beginning of liquid phase + // + strideColLiquid() skip to solid phase + // + offsetBoundComp() jump to component (skips all bound states of previous components) + // + i go to current bound state + jac[idxr.strideColLiquid() - comp + idxr.offsetBoundComp(comp) + i] += alpha * invBeta; + } + } - // Check whether full state vector is available as initial condition - if (!_initState.empty()) - { - std::fill(simState.vecStateY, simState.vecStateY + idxr.offsetC(), 0.0); - std::copy(_initState.data(), _initState.data() + numPureDofs(), simState.vecStateY + idxr.offsetC()); + // Solid phase + int const* const qsReaction = _binding[0]->reactionQuasiStationarity(); + for (unsigned int bnd = 0; bnd < _disc.strideBound; ++bnd, ++jac) + { + // Add derivative with respect to dynamic states to Jacobian + if (qsReaction[bnd]) + continue; - if (!_initStateDot.empty()) - { - std::fill(simState.vecStateYdot, simState.vecStateYdot + idxr.offsetC(), 0.0); - std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), simState.vecStateYdot + idxr.offsetC()); - } - else - std::fill(simState.vecStateYdot, simState.vecStateYdot + numDofs(), 0.0); + // Add derivative with respect to dq / dt to Jacobian + jac[0] += alpha; + } +} - return; - } +void LumpedRateModelWithoutPoresDG::applyInitialCondition(const SimulationState& simState) const +{ + Indexer idxr(_disc); - double* const stateYbulk = simState.vecStateY + idxr.offsetC(); + // Check whether full state vector is available as initial condition + if (!_initState.empty()) + { + std::fill(simState.vecStateY, simState.vecStateY + idxr.offsetC(), 0.0); + std::copy(_initState.data(), _initState.data() + numPureDofs(), simState.vecStateY + idxr.offsetC()); - // Loop over column cells - for (unsigned int point = 0; point < _disc.nPoints; ++point) - { - const unsigned int localOffset = point * idxr.strideColNode(); + if (!_initStateDot.empty()) + { + std::fill(simState.vecStateYdot, simState.vecStateYdot + idxr.offsetC(), 0.0); + std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), + simState.vecStateYdot + idxr.offsetC()); + } + else + std::fill(simState.vecStateYdot, simState.vecStateYdot + numDofs(), 0.0); - // Loop over components in cell - for (unsigned comp = 0; comp < _disc.nComp; ++comp) - stateYbulk[localOffset + comp * idxr.strideColComp()] = static_cast(_initC[comp]); + return; + } - // Initialize q - for (unsigned int bnd = 0; bnd < _disc.strideBound; ++bnd) - stateYbulk[localOffset + idxr.strideColLiquid() + bnd] = static_cast(_initQ[bnd]); - } - } + double* const stateYbulk = simState.vecStateY + idxr.offsetC(); - void LumpedRateModelWithoutPoresDG::readInitialCondition(IParameterProvider& paramProvider) - { - _initState.clear(); - _initStateDot.clear(); + // Loop over column cells + for (unsigned int point = 0; point < _disc.nPoints; ++point) + { + const unsigned int localOffset = point * idxr.strideColNode(); - // Check if INIT_STATE is present - if (paramProvider.exists("INIT_STATE")) - { - const std::vector initState = paramProvider.getDoubleArray("INIT_STATE"); - _initState = std::vector(initState.begin(), initState.begin() + numPureDofs()); + // Loop over components in cell + for (unsigned comp = 0; comp < _disc.nComp; ++comp) + stateYbulk[localOffset + comp * idxr.strideColComp()] = static_cast(_initC[comp]); - // Check if INIT_STATE contains the full state and its time derivative - if (initState.size() >= 2 * numPureDofs()) - _initStateDot = std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); - return; - } + // Initialize q + for (unsigned int bnd = 0; bnd < _disc.strideBound; ++bnd) + stateYbulk[localOffset + idxr.strideColLiquid() + bnd] = static_cast(_initQ[bnd]); + } +} - const std::vector initC = paramProvider.getDoubleArray("INIT_C"); - std::vector initQ; +void LumpedRateModelWithoutPoresDG::readInitialCondition(IParameterProvider& paramProvider) +{ + _initState.clear(); + _initStateDot.clear(); - if (paramProvider.exists("INIT_Q")) - initQ = paramProvider.getDoubleArray("INIT_Q"); + // Check if INIT_STATE is present + if (paramProvider.exists("INIT_STATE")) + { + const std::vector initState = paramProvider.getDoubleArray("INIT_STATE"); + _initState = std::vector(initState.begin(), initState.begin() + numPureDofs()); + + // Check if INIT_STATE contains the full state and its time derivative + if (initState.size() >= 2 * numPureDofs()) + _initStateDot = + std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); + return; + } + + const std::vector initC = paramProvider.getDoubleArray("INIT_C"); + std::vector initQ; + + if (paramProvider.exists("INIT_Q")) + initQ = paramProvider.getDoubleArray("INIT_Q"); + + if (initC.size() < _disc.nComp) + throw InvalidParameterException("INIT_C does not contain enough values for all components"); + + if ((_disc.strideBound > 0) && (initQ.size() < _disc.strideBound)) + throw InvalidParameterException("INIT_Q does not contain enough values for all bound states"); + + ad::copyToAd(initC.data(), _initC.data(), _disc.nComp); + if (!initQ.empty()) + ad::copyToAd(initQ.data(), _initQ.data(), _disc.strideBound); +} + +/** + * @brief Computes consistent initial values (state variables without their time derivatives) + * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have + * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time + * derivative \f$ \dot{y}_0 \f$ such that they are consistent. + * + * The process works in two steps: + *
                                            + *
                                          1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction + * equilibria).
                                          2. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0. However, + * because of the algebraic equations, we need additional conditions to fully determine + * @f$ \dot{y}@f$. By differentiating the algebraic equations with respect to time, we get the + * missing linear equations (recall that the state vector @f$ y @f$ is fixed). + * + * The right hand side of the linear system is given by the negative residual without contribution + * of @f$ \dot{y} @f$ for differential equations and 0 for algebraic equations + * (@f$ -\frac{\partial F}{\partial t}@f$, to be more precise).
                                          3. + *
                                          + * + * This function performs step 1. See consistentInitialTimeDerivative() for step 2. + * + * This function is to be used with consistentInitialTimeDerivative(). Do not mix normal and lean + * consistent initialization! + * + * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) + * @param [in,out] vecStateY State vector with initial values that are to be updated for consistency + * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) + * @param [in] errorTol Error tolerance for algebraic equations + * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) + */ +void LumpedRateModelWithoutPoresDG::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerConsistentInit); - if (initC.size() < _disc.nComp) - throw InvalidParameterException("INIT_C does not contain enough values for all components"); + Indexer idxr(_disc); - if ((_disc.strideBound > 0) && (initQ.size() < _disc.strideBound)) - throw InvalidParameterException("INIT_Q does not contain enough values for all bound states"); + // Step 1: Solve algebraic equations + if (!_binding[0]->hasQuasiStationaryReactions()) + return; - ad::copyToAd(initC.data(), _initC.data(), _disc.nComp); - if (!initQ.empty()) - ad::copyToAd(initQ.data(), _initQ.data(), _disc.strideBound); - } + // Copy quasi-stationary binding mask to a local array that also includes the mobile phase + std::vector qsMask(_disc.nComp + _disc.strideBound, false); + int const* const qsMaskSrc = _binding[0]->reactionQuasiStationarity(); + std::copy_n(qsMaskSrc, _disc.strideBound, qsMask.data() + _disc.nComp); - /** - * @brief Computes consistent initial values (state variables without their time derivatives) - * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have - * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time - * derivative \f$ \dot{y}_0 \f$ such that they are consistent. - * - * The process works in two steps: - *
                                            - *
                                          1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria).
                                          2. - *
                                          3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0. - * However, because of the algebraic equations, we need additional conditions to fully determine - * @f$ \dot{y}@f$. By differentiating the algebraic equations with respect to time, we get the - * missing linear equations (recall that the state vector @f$ y @f$ is fixed). - * - * The right hand side of the linear system is given by the negative residual without contribution - * of @f$ \dot{y} @f$ for differential equations and 0 for algebraic equations - * (@f$ -\frac{\partial F}{\partial t}@f$, to be more precise).
                                          4. - *
                                          - * - * This function performs step 1. See consistentInitialTimeDerivative() for step 2. - * - * This function is to be used with consistentInitialTimeDerivative(). Do not mix normal and lean - * consistent initialization! - * - * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) - * @param [in,out] vecStateY State vector with initial values that are to be updated for consistency - * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) - * @param [in] errorTol Error tolerance for algebraic equations - * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) - */ - void LumpedRateModelWithoutPoresDG::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) + // Activate mobile phase components that have at least one active bound state + unsigned int bndStartIdx = 0; + unsigned int numActiveComp = 0; + for (unsigned int comp = 0; comp < _disc.nComp; ++comp) + { + for (unsigned int bnd = 0; bnd < _disc.nBound[comp]; ++bnd) { - BENCH_SCOPE(_timerConsistentInit); - - Indexer idxr(_disc); - - // Step 1: Solve algebraic equations - if (!_binding[0]->hasQuasiStationaryReactions()) - return; - - // Copy quasi-stationary binding mask to a local array that also includes the mobile phase - std::vector qsMask(_disc.nComp + _disc.strideBound, false); - int const* const qsMaskSrc = _binding[0]->reactionQuasiStationarity(); - std::copy_n(qsMaskSrc, _disc.strideBound, qsMask.data() + _disc.nComp); - - // Activate mobile phase components that have at least one active bound state - unsigned int bndStartIdx = 0; - unsigned int numActiveComp = 0; - for (unsigned int comp = 0; comp < _disc.nComp; ++comp) + if (qsMaskSrc[bndStartIdx + bnd]) { - for (unsigned int bnd = 0; bnd < _disc.nBound[comp]; ++bnd) - { - if (qsMaskSrc[bndStartIdx + bnd]) - { - ++numActiveComp; - qsMask[comp] = true; - break; - } - } - - bndStartIdx += _disc.nBound[comp]; + ++numActiveComp; + qsMask[comp] = true; + break; } + } - const linalg::ConstMaskArray mask{ qsMask.data(), static_cast(_disc.nComp + _disc.strideBound) }; - const int probSize = linalg::numMaskActive(mask); + bndStartIdx += _disc.nBound[comp]; + } - //Problem capturing variables here + const linalg::ConstMaskArray mask{qsMask.data(), static_cast(_disc.nComp + _disc.strideBound)}; + const int probSize = linalg::numMaskActive(mask); + + // Problem capturing variables here #ifdef CADET_PARALLELIZE - BENCH_SCOPE(_timerConsistentInitPar); + BENCH_SCOPE(_timerConsistentInitPar); tbb::parallel_for(std::size_t(0), static_cast(_disc.nPoints), [&](std::size_t point) #else - for (unsigned int point = 0; point < _disc.nPoints; point++) + for (unsigned int point = 0; point < _disc.nPoints; point++) #endif { - LinearBufferAllocator tlmAlloc = threadLocalMem.get(); - // Reuse memory of band matrix for dense matrix - linalg::DenseMatrixView fullJacobianMatrix(_jacDisc.valuePtr() + point * _disc.strideBound * _disc.strideBound, nullptr, mask.len, mask.len); - - // z coordinate (column length normed to 1) of current node - needed in externally dependent adsorption kinetic - const double z = _convDispOp.relativeCoordinate(point); - - // Get workspace memory - BufferedArray nonlinMemBuffer = tlmAlloc.array(_nonlinearSolver->workspaceSize(probSize)); - double* const nonlinMem = static_cast(nonlinMemBuffer); - - BufferedArray solutionBuffer = tlmAlloc.array(probSize); - double* const solution = static_cast(solutionBuffer); - - BufferedArray fullResidualBuffer = tlmAlloc.array(mask.len); - double* const fullResidual = static_cast(fullResidualBuffer); - - BufferedArray fullXBuffer = tlmAlloc.array(mask.len); - double* const fullX = static_cast(fullXBuffer); - - BufferedArray jacobianMemBuffer = tlmAlloc.array(probSize * probSize); - double* const jacobianMem = static_cast(jacobianMemBuffer); - - BufferedArray conservedQuantsBuffer = tlmAlloc.array(numActiveComp); - double* const conservedQuants = static_cast(conservedQuantsBuffer); - - linalg::DenseMatrixView jacobianMatrix(jacobianMem, _jacDisc.innerIndexPtr() + point * _disc.strideBound, probSize, probSize); - const parts::cell::CellParameters cellResParams + LinearBufferAllocator tlmAlloc = threadLocalMem.get(); + // Reuse memory of band matrix for dense matrix + linalg::DenseMatrixView fullJacobianMatrix(_jacDisc.valuePtr() + point * _disc.strideBound * _disc.strideBound, + nullptr, mask.len, mask.len); + + // z coordinate (column length normed to 1) of current node - needed in externally dependent adsorption kinetic + const double z = _convDispOp.relativeCoordinate(point); + + // Get workspace memory + BufferedArray nonlinMemBuffer = tlmAlloc.array(_nonlinearSolver->workspaceSize(probSize)); + double* const nonlinMem = static_cast(nonlinMemBuffer); + + BufferedArray solutionBuffer = tlmAlloc.array(probSize); + double* const solution = static_cast(solutionBuffer); + + BufferedArray fullResidualBuffer = tlmAlloc.array(mask.len); + double* const fullResidual = static_cast(fullResidualBuffer); + + BufferedArray fullXBuffer = tlmAlloc.array(mask.len); + double* const fullX = static_cast(fullXBuffer); + + BufferedArray jacobianMemBuffer = tlmAlloc.array(probSize * probSize); + double* const jacobianMem = static_cast(jacobianMemBuffer); + + BufferedArray conservedQuantsBuffer = tlmAlloc.array(numActiveComp); + double* const conservedQuants = static_cast(conservedQuantsBuffer); + + linalg::DenseMatrixView jacobianMatrix(jacobianMem, _jacDisc.innerIndexPtr() + point * _disc.strideBound, + probSize, probSize); + const parts::cell::CellParameters cellResParams{ + _disc.nComp, + _disc.nBound, + _disc.boundOffset, + _disc.strideBound, + _binding[0]->reactionQuasiStationarity(), + _totalPorosity, + nullptr, + _binding[0], + (_dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)) ? _dynReaction[0] : nullptr}; + + const int localOffsetToCell = idxr.offsetC() + point * idxr.strideColNode(); + const int localOffsetInCell = idxr.strideColLiquid(); + + // Get pointer to q variables in cell + double* const qShell = vecStateY + localOffsetToCell + localOffsetInCell; + active* const localAdRes = adJac.adRes ? adJac.adRes + localOffsetToCell : nullptr; + active* const localAdY = adJac.adY ? adJac.adY + localOffsetToCell : nullptr; + + const ColumnPosition colPos{z, 0.0, 0.0}; + + // Determine whether nonlinear solver is required + if (!_binding[0]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - localOffsetInCell, tlmAlloc)) + CADET_PAR_CONTINUE; + + // Extract initial values from current state + linalg::selectVectorSubset(qShell - _disc.nComp, mask, solution); + + // Save values of conserved moieties + const double epsQ = 1.0 - static_cast(_totalPorosity); + linalg::conservedMoietiesFromPartitionedMask(mask, _disc.nBound, _disc.nComp, qShell - _disc.nComp, + conservedQuants, static_cast(_totalPorosity), epsQ); + + std::function jacFunc; + + jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) { + // Prepare input vector by overwriting masked items + std::copy_n(qShell - _disc.nComp, mask.len, fullX); + linalg::applyVectorSubset(x, mask, fullX); + + // Call residual function + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); + + // Extract Jacobian from full Jacobian + mat.setAll(0.0); + linalg::copyMatrixSubset(fullJacobianMatrix, mask, mask, mat); + + // Replace upper part with conservation relations + mat.submatrixSetAll(0.0, 0, 0, numActiveComp, probSize); + + unsigned int bndIdx = 0; + unsigned int rIdx = 0; + unsigned int bIdx = 0; + for (unsigned int comp = 0; comp < _disc.nComp; ++comp) + { + if (!mask.mask[comp]) { - _disc.nComp, - _disc.nBound, - _disc.boundOffset, - _disc.strideBound, - _binding[0]->reactionQuasiStationarity(), - _totalPorosity, - nullptr, - _binding[0], - (_dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)) ? _dynReaction[0] : nullptr - }; - - const int localOffsetToCell = idxr.offsetC() + point * idxr.strideColNode(); - const int localOffsetInCell = idxr.strideColLiquid(); - - // Get pointer to q variables in cell - double* const qShell = vecStateY + localOffsetToCell + localOffsetInCell; - active* const localAdRes = adJac.adRes ? adJac.adRes + localOffsetToCell : nullptr; - active* const localAdY = adJac.adY ? adJac.adY + localOffsetToCell : nullptr; - - const ColumnPosition colPos{ z, 0.0, 0.0 }; - - // Determine whether nonlinear solver is required - if (!_binding[0]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - localOffsetInCell, tlmAlloc)) - CADET_PAR_CONTINUE; - - // Extract initial values from current state - linalg::selectVectorSubset(qShell - _disc.nComp, mask, solution); - - // Save values of conserved moieties - const double epsQ = 1.0 - static_cast(_totalPorosity); - linalg::conservedMoietiesFromPartitionedMask(mask, _disc.nBound, _disc.nComp, qShell - _disc.nComp, conservedQuants, static_cast(_totalPorosity), epsQ); + bndIdx += _disc.nBound[comp]; + continue; + } - std::function jacFunc; + mat.native(rIdx, rIdx) = static_cast(_totalPorosity); - jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) + for (unsigned int bnd = 0; bnd < _disc.nBound[comp]; ++bnd, ++bndIdx) { - // Prepare input vector by overwriting masked items - std::copy_n(qShell - _disc.nComp, mask.len, fullX); - linalg::applyVectorSubset(x, mask, fullX); - - // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); - - // Extract Jacobian from full Jacobian - mat.setAll(0.0); - linalg::copyMatrixSubset(fullJacobianMatrix, mask, mask, mat); - - // Replace upper part with conservation relations - mat.submatrixSetAll(0.0, 0, 0, numActiveComp, probSize); - - unsigned int bndIdx = 0; - unsigned int rIdx = 0; - unsigned int bIdx = 0; - for (unsigned int comp = 0; comp < _disc.nComp; ++comp) + if (mask.mask[bndIdx]) { - if (!mask.mask[comp]) - { - bndIdx += _disc.nBound[comp]; - continue; - } - - mat.native(rIdx, rIdx) = static_cast(_totalPorosity); + mat.native(rIdx, bIdx + numActiveComp) = epsQ; + ++bIdx; + } + } - for (unsigned int bnd = 0; bnd < _disc.nBound[comp]; ++bnd, ++bndIdx) - { - if (mask.mask[bndIdx]) - { - mat.native(rIdx, bIdx + numActiveComp) = epsQ; - ++bIdx; - } - } + ++rIdx; + } - ++rIdx; + return true; + }; + + // Apply nonlinear solver + _nonlinearSolver->solve( + [&](double const* const x, double* const r) { + // Prepare input vector by overwriting masked items + std::copy_n(qShell - _disc.nComp, mask.len, fullX); + linalg::applyVectorSubset(x, mask, fullX); + + // Call residual function + parts::cell::residualKernel( + simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), + cellResParams, tlmAlloc); + + // Extract values from residual + linalg::selectVectorSubset(fullResidual, mask, r); + + // Calculate residual of conserved moieties + std::fill_n(r, numActiveComp, 0.0); + unsigned int bndIdx = _disc.nComp; + unsigned int rIdx = 0; + unsigned int bIdx = 0; + for (unsigned int comp = 0; comp < _disc.nComp; ++comp) + { + if (!mask.mask[comp]) + { + bndIdx += _disc.nBound[comp]; + continue; } - return true; - }; + r[rIdx] = static_cast(_totalPorosity) * x[rIdx] - conservedQuants[rIdx]; - // Apply nonlinear solver - _nonlinearSolver->solve( - [&](double const* const x, double* const r) + for (unsigned int bnd = 0; bnd < _disc.nBound[comp]; ++bnd, ++bndIdx) { - // Prepare input vector by overwriting masked items - std::copy_n(qShell - _disc.nComp, mask.len, fullX); - linalg::applyVectorSubset(x, mask, fullX); - - // Call residual function - parts::cell::residualKernel( - simTime.t, simTime.secIdx, colPos, fullX, nullptr, fullResidual, fullJacobianMatrix.row(0), cellResParams, tlmAlloc - ); - - // Extract values from residual - linalg::selectVectorSubset(fullResidual, mask, r); - - // Calculate residual of conserved moieties - std::fill_n(r, numActiveComp, 0.0); - unsigned int bndIdx = _disc.nComp; - unsigned int rIdx = 0; - unsigned int bIdx = 0; - for (unsigned int comp = 0; comp < _disc.nComp; ++comp) + if (mask.mask[bndIdx]) { - if (!mask.mask[comp]) - { - bndIdx += _disc.nBound[comp]; - continue; - } - - r[rIdx] = static_cast(_totalPorosity) * x[rIdx] - conservedQuants[rIdx]; - - for (unsigned int bnd = 0; bnd < _disc.nBound[comp]; ++bnd, ++bndIdx) - { - if (mask.mask[bndIdx]) - { - r[rIdx] += epsQ * x[bIdx + numActiveComp]; - ++bIdx; - } - } - - ++rIdx; + r[rIdx] += epsQ * x[bIdx + numActiveComp]; + ++bIdx; } + } + + ++rIdx; + } - return true; - }, - jacFunc, errorTol, solution, nonlinMem, jacobianMatrix, probSize); + return true; + }, + jacFunc, errorTol, solution, nonlinMem, jacobianMatrix, probSize); - // Apply solution - linalg::applyVectorSubset(solution, mask, qShell - idxr.strideColLiquid()); + // Apply solution + linalg::applyVectorSubset(solution, mask, qShell - idxr.strideColLiquid()); - // Refine / correct solution - _binding[0]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, qShell - idxr.strideColLiquid(), tlmAlloc); + // Refine / correct solution + _binding[0]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, qShell, + qShell - idxr.strideColLiquid(), tlmAlloc); } CADET_PARFOR_END; // restore _jacDisc pattern setPattern(_jacDisc, true, _dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)); +} + +/** + * @brief Computes consistent initial time derivatives + * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have + * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time + * derivative \f$ \dot{y}_0 \f$ such that they are consistent. + * + * The process works in two steps: + *
                                            + *
                                          1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction + * equilibria).
                                          2. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0. However, + * because of the algebraic equations, we need additional conditions to fully determine + * @f$ \dot{y}@f$. By differentiating the algebraic equations with respect to time, we get the + * missing linear equations (recall that the state vector @f$ y @f$ is fixed). + * + * The right hand side of the linear system is given by the negative residual without contribution + * of @f$ \dot{y} @f$ for differential equations and 0 for algebraic equations + * (@f$ -\frac{\partial F}{\partial t}@f$, to be more precise).
                                          3. + *
                                          + * + * This function performs step 2. See consistentInitialState() for step 1. + * + * This function is to be used with consistentInitialState(). Do not mix normal and lean + * consistent initialization! + * + * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) + * @param [in] vecStateY Consistently initialized state vector + * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent + * state time derivatives. + */ +void LumpedRateModelWithoutPoresDG::consistentInitialTimeDerivative(const SimulationTime& simTime, + double const* vecStateY, double* const vecStateYdot, + util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerConsistentInit); - } + Indexer idxr(_disc); + + // Step 2: Compute the correct time derivative of the state vector + + // Note that the residual has not been negated, yet. We will do that now. + for (unsigned int i = 0; i < numDofs(); ++i) + vecStateYdot[i] = -vecStateYdot[i]; - /** - * @brief Computes consistent initial time derivatives - * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have - * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time - * derivative \f$ \dot{y}_0 \f$ such that they are consistent. - * - * The process works in two steps: - *
                                            - *
                                          1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria).
                                          2. - *
                                          3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0. - * However, because of the algebraic equations, we need additional conditions to fully determine - * @f$ \dot{y}@f$. By differentiating the algebraic equations with respect to time, we get the - * missing linear equations (recall that the state vector @f$ y @f$ is fixed). - * - * The right hand side of the linear system is given by the negative residual without contribution - * of @f$ \dot{y} @f$ for differential equations and 0 for algebraic equations - * (@f$ -\frac{\partial F}{\partial t}@f$, to be more precise).
                                          4. - *
                                          - * - * This function performs step 2. See consistentInitialState() for step 1. - * - * This function is to be used with consistentInitialState(). Do not mix normal and lean - * consistent initialization! - * - * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) - * @param [in] vecStateY Consistently initialized state vector - * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent state time derivatives. - */ - void LumpedRateModelWithoutPoresDG::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem) + double* entries = _jacDisc.valuePtr(); + for (unsigned int nz = 0; nz < _jacDisc.nonZeros(); nz++) + entries[nz] = 0.0; + + // Handle transport equations (dc_i / dt terms) + const int gapNode = idxr.strideColNode() - static_cast(_disc.nComp) * idxr.strideColComp(); + linalg::BandedEigenSparseRowIterator jac(_jacDisc, 0); + for (unsigned int i = 0; i < _disc.nPoints; ++i, jac += gapNode) + { + for (unsigned int j = 0; j < _disc.nComp; ++j, ++jac) { - BENCH_SCOPE(_timerConsistentInit); + // Add time derivative to liquid states (on main diagonal) + jac[0] += 1.0; + } + } - Indexer idxr(_disc); + const double invBeta = 1.0 / static_cast(_totalPorosity) - 1.0; + double* const dFluxDt = _tempState + idxr.offsetC(); + LinearBufferAllocator tlmAlloc = threadLocalMem.get(); + for (unsigned int col = 0; col < _disc.nPoints; ++col) + { + // Assemble + linalg::BandedEigenSparseRowIterator jac(_jacDisc, idxr.strideColNode() * col); - // Step 2: Compute the correct time derivative of the state vector + // Mobile and solid phase (advances jac accordingly) + addTimeDerivativeToJacobianNode(jac, idxr, 1.0, invBeta); - // Note that the residual has not been negated, yet. We will do that now. - for (unsigned int i = 0; i < numDofs(); ++i) - vecStateYdot[i] = -vecStateYdot[i]; + // Stationary phase + if (!_binding[0]->hasQuasiStationaryReactions()) + continue; - double* entries = _jacDisc.valuePtr(); - for (unsigned int nz = 0; nz < _jacDisc.nonZeros(); nz++) - entries[nz] = 0.0; + // Midpoint of current column node (z coordinate) - needed in externally dependent adsorption kinetic + double z = _convDispOp.relativeCoordinate(col); - // Handle transport equations (dc_i / dt terms) - const int gapNode = idxr.strideColNode() - static_cast(_disc.nComp) * idxr.strideColComp(); - linalg::BandedEigenSparseRowIterator jac(_jacDisc, 0); - for (unsigned int i = 0; i < _disc.nPoints; ++i, jac += gapNode) - { - for (unsigned int j = 0; j < _disc.nComp; ++j, ++jac) - { - // Add time derivative to liquid states (on main diagonal) - jac[0] += 1.0; - } - } + // Get iterators to beginning of solid phase + linalg::BandedEigenSparseRowIterator jacSolidOrig(_jac, idxr.strideColNode() * col + idxr.strideColLiquid()); + linalg::BandedEigenSparseRowIterator jacSolid = jac - idxr.strideColBound(); - const double invBeta = 1.0 / static_cast(_totalPorosity) - 1.0; - double* const dFluxDt = _tempState + idxr.offsetC(); - LinearBufferAllocator tlmAlloc = threadLocalMem.get(); - for (unsigned int col = 0; col < _disc.nPoints; ++col) - { - // Assemble - linalg::BandedEigenSparseRowIterator jac(_jacDisc, idxr.strideColNode() * col); + int const* const mask = _binding[0]->reactionQuasiStationarity(); + double* const qNodeDot = vecStateYdot + idxr.offsetC() + col * idxr.strideColNode() + idxr.strideColLiquid(); - // Mobile and solid phase (advances jac accordingly) - addTimeDerivativeToJacobianNode(jac, idxr, 1.0, invBeta); + // Obtain derivative of fluxes wrt. time + std::fill_n(dFluxDt, _disc.strideBound, 0.0); + if (_binding[0]->dependsOnTime()) + { + _binding[0]->timeDerivativeQuasiStationaryFluxes(simTime.t, simTime.secIdx, ColumnPosition{z, 0.0, 0.0}, + qNodeDot - _disc.nComp, qNodeDot, dFluxDt, tlmAlloc); + } - // Stationary phase - if (!_binding[0]->hasQuasiStationaryReactions()) - continue; + // Copy row from original Jacobian and set right hand side + for (unsigned int i = 0; i < _disc.strideBound; ++i, ++jacSolid, ++jacSolidOrig) + { + if (!mask[i]) + continue; - // Midpoint of current column node (z coordinate) - needed in externally dependent adsorption kinetic - double z = _convDispOp.relativeCoordinate(col); + jacSolid.copyRowFrom(jacSolidOrig); + qNodeDot[i] = -dFluxDt[i]; + } + } - // Get iterators to beginning of solid phase - linalg::BandedEigenSparseRowIterator jacSolidOrig(_jac, idxr.strideColNode() * col + idxr.strideColLiquid()); - linalg::BandedEigenSparseRowIterator jacSolid = jac - idxr.strideColBound(); + _linearSolver->factorize(_jacDisc); - int const* const mask = _binding[0]->reactionQuasiStationarity(); - double* const qNodeDot = vecStateYdot + idxr.offsetC() + col * idxr.strideColNode() + idxr.strideColLiquid(); + if (_linearSolver->info() != Success) + { + LOG(Error) << "factorization failed in consistent initialization"; + } - // Obtain derivative of fluxes wrt. time - std::fill_n(dFluxDt, _disc.strideBound, 0.0); - if (_binding[0]->dependsOnTime()) - { - _binding[0]->timeDerivativeQuasiStationaryFluxes(simTime.t, simTime.secIdx, ColumnPosition{ z, 0.0, 0.0 }, - qNodeDot - _disc.nComp, qNodeDot, dFluxDt, tlmAlloc); - } + Eigen::Map yp(vecStateYdot + idxr.offsetC(), numPureDofs()); - // Copy row from original Jacobian and set right hand side - for (unsigned int i = 0; i < _disc.strideBound; ++i, ++jacSolid, ++jacSolidOrig) - { - if (!mask[i]) - continue; + yp = _linearSolver->solve(yp); - jacSolid.copyRowFrom(jacSolidOrig); - qNodeDot[i] = -dFluxDt[i]; - } - } + if (_linearSolver->info() != Success) + { + LOG(Error) << "Solve failed in consistent initialization"; + } + + // reset jacobian pattern + setPattern(_jacDisc, true, _dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)); +} + +/** + * @brief Computes approximately / partially consistent initial values (state variables without their time derivatives) + * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have + * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time + * derivative \f$ \dot{y}_0 \f$ such that they are consistent. + * + * This function performs a relaxed consistent initialization: Only parts of the vectors are updated + * and, hence, consistency is not guaranteed. Since there is less work to do, it is probably faster than + * the standard process represented by consistentInitialState(). + * + * The process works in two steps: + *
                                            + *
                                          1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations).
                                          2. + *
                                          3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0 for the + * mobile phase variables.
                                          4. + *
                                          + * This function performs step 1. See leanConsistentInitialTimeDerivative() for step 2. + * + * This function is to be used with leanConsistentInitialTimeDerivative(). Do not mix normal and lean + * consistent initialization! + * + * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) + * @param [in,out] vecStateY State vector with initial values that are to be updated for consistency + * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) + * @param [in] errorTol Error tolerance for algebraic equations + */ +void LumpedRateModelWithoutPoresDG::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) +{ +} + +/** + * @brief Computes approximately / partially consistent initial time derivatives + * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have + * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time + * derivative \f$ \dot{y}_0 \f$ such that they are consistent. + * + * This function performs a relaxed consistent initialization: Only parts of the vectors are updated + * and, hence, consistency is not guaranteed. Since there is less work to do, it is probably faster than + * the standard process represented by consistentInitialState(). + * + * The process works in two steps: + *
                                            + *
                                          1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations).
                                          2. + *
                                          3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0 for the + * mobile phase variables.
                                          4. + *
                                          + * This function performs step 2. See leanConsistentInitialState() for step 1. + * + * This function is to be used with leanConsistentInitialState(). Do not mix normal and lean + * consistent initialization! + * + * @param [in] t Current time point + * @param [in] vecStateY (Lean) consistently initialized state vector + * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time + * derivatives. + * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during + * execution of the function. + */ +void LumpedRateModelWithoutPoresDG::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerConsistentInit); - _linearSolver->factorize(_jacDisc); - - if (_linearSolver->info() != Success) { - LOG(Error) << "factorization failed in consistent initialization"; - } + Indexer idxr(_disc); - Eigen::Map yp(vecStateYdot + idxr.offsetC(), numPureDofs()); + // Step 2: Compute the correct time derivative of the state vector (only mobile phase DOFs) - yp = _linearSolver->solve(yp); + const double invBeta = (1.0 / static_cast(_totalPorosity) - 1.0); + for (unsigned int node = 0; node < _disc.nPoints; ++node) + { + // Offset to current cell's c and q variables + const unsigned int localOffset = idxr.offsetC() + node * idxr.strideColNode(); + const unsigned int localOffsetQ = localOffset + idxr.strideColLiquid(); - if (_linearSolver->info() != Success) { - LOG(Error) << "Solve failed in consistent initialization"; + for (unsigned int comp = 0; comp < _disc.nComp; ++comp) + { + // dq_{i,j} / dt is assumed to be fixed, so bring it on the right hand side + for (unsigned int i = 0; i < _disc.nBound[comp]; ++i) + { + res[localOffset + comp] += invBeta * vecStateYdot[localOffsetQ + _disc.boundOffset[comp] + i]; } - // reset jacobian pattern - setPattern(_jacDisc, true, _dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)); - + vecStateYdot[localOffset + comp] = -res[localOffset + comp]; } + } +} + +void LumpedRateModelWithoutPoresDG::initializeSensitivityStates(const std::vector& vecSensY) const +{ + Indexer idxr(_disc); + for (std::size_t param = 0; param < vecSensY.size(); ++param) + { + double* const stateYbulk = vecSensY[param] + idxr.offsetC(); - /** - * @brief Computes approximately / partially consistent initial values (state variables without their time derivatives) - * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have - * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time - * derivative \f$ \dot{y}_0 \f$ such that they are consistent. - * - * This function performs a relaxed consistent initialization: Only parts of the vectors are updated - * and, hence, consistency is not guaranteed. Since there is less work to do, it is probably faster than - * the standard process represented by consistentInitialState(). - * - * The process works in two steps: - *
                                            - *
                                          1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations).
                                          2. - *
                                          3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0 for the - * mobile phase variables.
                                          4. - *
                                          - * This function performs step 1. See leanConsistentInitialTimeDerivative() for step 2. - * - * This function is to be used with leanConsistentInitialTimeDerivative(). Do not mix normal and lean - * consistent initialization! - * - * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) - * @param [in,out] vecStateY State vector with initial values that are to be updated for consistency - * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) - * @param [in] errorTol Error tolerance for algebraic equations - */ - void LumpedRateModelWithoutPoresDG::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) + // Loop over column cells + for (unsigned int node = 0; node < _disc.nPoints; ++node) { + const unsigned int localOffset = node * idxr.strideColNode(); + + // Loop over components in cell + for (unsigned comp = 0; comp < _disc.nComp; ++comp) + stateYbulk[localOffset + comp * idxr.strideColComp()] = _initC[comp].getADValue(param); + + // Initialize q + for (unsigned int bnd = 0; bnd < _disc.strideBound; ++bnd) + stateYbulk[localOffset + idxr.strideColLiquid() + bnd] = _initQ[bnd].getADValue(param); } + } +} + +/** + * @brief Computes consistent initial values and time derivatives of sensitivity subsystems + * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, + * the sensitivity system for a parameter @f$ p @f$ reads + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of this linear DAE, @f$ s_0 = + * \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ have to be + * consistent with the sensitivity DAE. This function updates the initial sensitivity\f$ s_0 \f$ and overwrites the time + * derivative \f$ \dot{s}_0 \f$ such that they are consistent. + * + * The process follows closely the one of consistentInitialConditions() and, in fact, is a linearized version + * of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ + * are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them + * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial + * \dot{y}_0}{\partial p}. @f$
                                          1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, + * reaction equilibria). Let @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at this point, we have + * \f[ \left( \frac{\partial F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, y_0, + * \dot{y}_0) \right)_{\mathcal{I}_a} = 0. \f]
                                          2. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ + * such that the differential equations hold. However, because of the algebraic equations, we need additional conditions + * to fully determine + * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the + * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). + * + * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. + * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - + * \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] which is 0 for algebraic equations (@f$ -\frac{\partial^2 + * F}{\partial t \partial p}@f$, to be more precise).
                                          3. + *
                                          + * This function requires the parameter sensitivities to be computed beforehand and up-to-date Jacobians. + * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) + * @param [in] simState Consistent state of the simulation (state vector and its time derivative) + * @param [in,out] vecSensY Sensitivity subsystem state vectors + * @param [in,out] vecSensYdot Time derivative state vectors of the sensitivity subsystems to be initialized + * @param [in] adRes Pointer to residual vector of AD datatypes with parameter sensitivities + * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) + */ +void LumpedRateModelWithoutPoresDG::consistentInitialSensitivity( + const SimulationTime& simTime, const ConstSimulationState& simState, std::vector& vecSensY, + std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerConsistentInit); - /** - * @brief Computes approximately / partially consistent initial time derivatives - * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have - * to be consistent. This functions updates the initial state \f$ y_0 \f$ and overwrites the time - * derivative \f$ \dot{y}_0 \f$ such that they are consistent. - * - * This function performs a relaxed consistent initialization: Only parts of the vectors are updated - * and, hence, consistency is not guaranteed. Since there is less work to do, it is probably faster than - * the standard process represented by consistentInitialState(). - * - * The process works in two steps: - *
                                            - *
                                          1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations).
                                          2. - *
                                          3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0 for the - * mobile phase variables.
                                          4. - *
                                          - * This function performs step 2. See leanConsistentInitialState() for step 1. - * - * This function is to be used with leanConsistentInitialState(). Do not mix normal and lean - * consistent initialization! - * - * @param [in] t Current time point - * @param [in] vecStateY (Lean) consistently initialized state vector - * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time derivatives. - * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during execution of the function. - */ - void LumpedRateModelWithoutPoresDG::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem) - { - BENCH_SCOPE(_timerConsistentInit); + Indexer idxr(_disc); - Indexer idxr(_disc); + for (std::size_t param = 0; param < vecSensY.size(); ++param) + { + double* const sensY = vecSensY[param]; + double* const sensYdot = vecSensYdot[param]; - // Step 2: Compute the correct time derivative of the state vector (only mobile phase DOFs) + // Copy parameter derivative dF / dp from AD and negate it + for (unsigned int i = _disc.nComp; i < numDofs(); ++i) + sensYdot[i] = -adRes[i].getADValue(param); - const double invBeta = (1.0 / static_cast(_totalPorosity) - 1.0); - for (unsigned int node = 0; node < _disc.nPoints; ++node) - { - // Offset to current cell's c and q variables - const unsigned int localOffset = idxr.offsetC() + node * idxr.strideColNode(); - const unsigned int localOffsetQ = localOffset + idxr.strideColLiquid(); + // Step 1: Solve algebraic equations - for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - { - // dq_{i,j} / dt is assumed to be fixed, so bring it on the right hand side - for (unsigned int i = 0; i < _disc.nBound[comp]; ++i) + if (_binding[0]->hasQuasiStationaryReactions()) + { + int const* const qsMask = _binding[0]->reactionQuasiStationarity(); + const linalg::ConstMaskArray mask{qsMask, static_cast(_disc.strideBound)}; + const int probSize = linalg::numMaskActive(mask); + +#ifdef CADET_PARALLELIZE + BENCH_SCOPE(_timerConsistentInitPar); + tbb::parallel_for(std::size_t(0), static_cast(_disc.nPoints), [&](std::size_t node) +#else + for (unsigned int node = 0; node < _disc.nPoints; ++node) +#endif { - res[localOffset + comp] += invBeta * vecStateYdot[localOffsetQ + _disc.boundOffset[comp] + i]; - } + const unsigned int jacRowOffset = + idxr.strideColNode() * node + static_cast(idxr.strideColLiquid()); + const int localQOffset = idxr.offsetC() + node * idxr.strideColNode() + idxr.strideColLiquid(); - vecStateYdot[localOffset + comp] = -res[localOffset + comp]; - } - } - } + // Reuse memory of sparse jacobian for dense matrix + linalg::DenseMatrixView jacobianMatrix( + _jacDisc.valuePtr() + node * _disc.strideBound * _disc.strideBound, + _jacDisc.innerIndexPtr() + node * _disc.strideBound, probSize, probSize); - void LumpedRateModelWithoutPoresDG::initializeSensitivityStates(const std::vector& vecSensY) const - { - Indexer idxr(_disc); - for (std::size_t param = 0; param < vecSensY.size(); ++param) - { - double* const stateYbulk = vecSensY[param] + idxr.offsetC(); + // Get workspace memory + LinearBufferAllocator tlmAlloc = threadLocalMem.get(); - // Loop over column cells - for (unsigned int node = 0; node < _disc.nPoints; ++node) - { - const unsigned int localOffset = node * idxr.strideColNode(); + BufferedArray rhsBuffer = tlmAlloc.array(probSize); + double* const rhs = static_cast(rhsBuffer); - // Loop over components in cell - for (unsigned comp = 0; comp < _disc.nComp; ++comp) - stateYbulk[localOffset + comp * idxr.strideColComp()] = _initC[comp].getADValue(param); + BufferedArray rhsUnmaskedBuffer = tlmAlloc.array(_disc.strideBound); + double* const rhsUnmasked = static_cast(rhsUnmaskedBuffer); - // Initialize q - for (unsigned int bnd = 0; bnd < _disc.strideBound; ++bnd) - stateYbulk[localOffset + idxr.strideColLiquid() + bnd] = _initQ[bnd].getADValue(param); - } - } - } + double* const maskedMultiplier = _tempState + idxr.offsetC() + node * idxr.strideColNode(); - /** - * @brief Computes consistent initial values and time derivatives of sensitivity subsystems - * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, - * the sensitivity system for a parameter @f$ p @f$ reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of this linear DAE, @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ - * have to be consistent with the sensitivity DAE. This function updates the initial sensitivity\f$ s_0 \f$ and overwrites the time - * derivative \f$ \dot{s}_0 \f$ such that they are consistent. - * - * The process follows closely the one of consistentInitialConditions() and, in fact, is a linearized version of it. - * This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ are - * related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them - * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p}. @f$ - *
                                            - *
                                          1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria). - * Let @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at this point, we have - * \f[ \left( \frac{\partial F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, y_0, \dot{y}_0) \right)_{\mathcal{I}_a} = 0. \f]
                                          2. - *
                                          3. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. - * However, because of the algebraic equations, we need additional conditions to fully determine - * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the - * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). - * - * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. - * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] - * which is 0 for algebraic equations (@f$ -\frac{\partial^2 F}{\partial t \partial p}@f$, to be more precise).
                                          4. - *
                                          - * This function requires the parameter sensitivities to be computed beforehand and up-to-date Jacobians. - * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) - * @param [in] simState Consistent state of the simulation (state vector and its time derivative) - * @param [in,out] vecSensY Sensitivity subsystem state vectors - * @param [in,out] vecSensYdot Time derivative state vectors of the sensitivity subsystems to be initialized - * @param [in] adRes Pointer to residual vector of AD datatypes with parameter sensitivities - * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) - */ - void LumpedRateModelWithoutPoresDG::consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) - { - BENCH_SCOPE(_timerConsistentInit); + // Extract subproblem Jacobian from full Jacobian + jacobianMatrix.setAll(0.0); + linalg::copyMatrixSubset(_jac, mask, mask, jacRowOffset, jacRowOffset, jacobianMatrix); - Indexer idxr(_disc); + // Construct right hand side: rhs = -dF / dp |\mathcal{I}_a + linalg::selectVectorSubset(sensYdot + localQOffset, mask, rhs); - for (std::size_t param = 0; param < vecSensY.size(); ++param) - { - double* const sensY = vecSensY[param]; - double* const sensYdot = vecSensYdot[param]; - - // Copy parameter derivative dF / dp from AD and negate it - for (unsigned int i = _disc.nComp; i < numDofs(); ++i) - sensYdot[i] = -adRes[i].getADValue(param); - - // Step 1: Solve algebraic equations - - if (_binding[0]->hasQuasiStationaryReactions()) - { - int const* const qsMask = _binding[0]->reactionQuasiStationarity(); - const linalg::ConstMaskArray mask{qsMask, static_cast(_disc.strideBound)}; - const int probSize = linalg::numMaskActive(mask); - - #ifdef CADET_PARALLELIZE - BENCH_SCOPE(_timerConsistentInitPar); - tbb::parallel_for(std::size_t(0), static_cast(_disc.nPoints), [&](std::size_t node) - #else - for (unsigned int node = 0; node < _disc.nPoints; ++node) - #endif - { - const unsigned int jacRowOffset = idxr.strideColNode() * node + static_cast(idxr.strideColLiquid()); - const int localQOffset = idxr.offsetC() + node * idxr.strideColNode() + idxr.strideColLiquid(); - - // Reuse memory of sparse jacobian for dense matrix - linalg::DenseMatrixView jacobianMatrix(_jacDisc.valuePtr() + node * _disc.strideBound * _disc.strideBound, _jacDisc.innerIndexPtr() + node * _disc.strideBound, probSize, probSize); - - // Get workspace memory - LinearBufferAllocator tlmAlloc = threadLocalMem.get(); - - BufferedArray rhsBuffer = tlmAlloc.array(probSize); - double* const rhs = static_cast(rhsBuffer); - - BufferedArray rhsUnmaskedBuffer = tlmAlloc.array(_disc.strideBound); - double* const rhsUnmasked = static_cast(rhsUnmaskedBuffer); - - double* const maskedMultiplier = _tempState + idxr.offsetC() + node * idxr.strideColNode(); - - // Extract subproblem Jacobian from full Jacobian - jacobianMatrix.setAll(0.0); - linalg::copyMatrixSubset(_jac, mask, mask, jacRowOffset, jacRowOffset, jacobianMatrix); - - // Construct right hand side: rhs = -dF / dp |\mathcal{I}_a - linalg::selectVectorSubset(sensYdot + localQOffset, mask, rhs); - - // Zero out masked elements: maskedMultiplier|\mathcal{I}_d = s_0, maskedMultiplier|\mathcal{I}_a = 0.0 - std::copy_n(sensY + localQOffset - idxr.strideColLiquid(), idxr.strideColNode(), maskedMultiplier); - linalg::fillVectorSubset(maskedMultiplier + _disc.nComp, mask, 0.0); - - // Assemble right hand side: rhs += {dF/dy * maskedMultiplier}|\mathcal{I}_a - Eigen::Map maskedMultiplier_vec(maskedMultiplier, idxr.strideColNode()); - Eigen::Map rhsUnmasked_vec(rhsUnmasked, _disc.strideBound); - - rhsUnmasked_vec=_jac.block(jacRowOffset, jacRowOffset - static_cast(_disc.nComp), _disc.strideBound, idxr.strideColNode()) * maskedMultiplier_vec; - linalg::vectorSubsetAdd(rhsUnmasked, mask, -1.0, 1.0, rhs); - - // Precondition - double* const scaleFactors = _tempState + idxr.offsetC() + node * idxr.strideColNode(); - jacobianMatrix.rowScaleFactors(scaleFactors); - jacobianMatrix.scaleRows(scaleFactors); - - // Solve _jac|\mathcal{I}_a x = rhs - jacobianMatrix.factorize(); - jacobianMatrix.solve(scaleFactors, rhs); - - // Write back - linalg::applyVectorSubset(rhs, mask, sensY + localQOffset); + // Zero out masked elements: maskedMultiplier|\mathcal{I}_d = s_0, maskedMultiplier|\mathcal{I}_a = 0.0 + std::copy_n(sensY + localQOffset - idxr.strideColLiquid(), idxr.strideColNode(), maskedMultiplier); + linalg::fillVectorSubset(maskedMultiplier + _disc.nComp, mask, 0.0); + + // Assemble right hand side: rhs += {dF/dy * maskedMultiplier}|\mathcal{I}_a + Eigen::Map maskedMultiplier_vec(maskedMultiplier, idxr.strideColNode()); + Eigen::Map rhsUnmasked_vec(rhsUnmasked, _disc.strideBound); + + rhsUnmasked_vec = _jac.block(jacRowOffset, jacRowOffset - static_cast(_disc.nComp), + _disc.strideBound, idxr.strideColNode()) * + maskedMultiplier_vec; + linalg::vectorSubsetAdd(rhsUnmasked, mask, -1.0, 1.0, rhs); + + // Precondition + double* const scaleFactors = _tempState + idxr.offsetC() + node * idxr.strideColNode(); + jacobianMatrix.rowScaleFactors(scaleFactors); + jacobianMatrix.scaleRows(scaleFactors); + + // Solve _jac|\mathcal{I}_a x = rhs + jacobianMatrix.factorize(); + jacobianMatrix.solve(scaleFactors, rhs); + + // Write back + linalg::applyVectorSubset(rhs, mask, sensY + localQOffset); } CADET_PARFOR_END; - } - - // Step 2: Compute the correct time derivative of the state vector - - // Compute right hand side by adding -dF / dy * s = -J * s to -dF / dp which is already stored in sensYdot - multiplyWithJacobian(simTime, simState, sensY, -1.0, 1.0, sensYdot); - - // Note that we have correctly negated the right hand side - setPattern(_jacDisc, true, _dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)); - //for (int entry = 0; entry < _jacDisc.nonZeros(); entry++) - // _jacDisc.valuePtr()[entry] = 0.0; - - // Handle transport equations (dc_i / dt terms) - const int gapNode = idxr.strideColNode() - static_cast(_disc.nComp) * idxr.strideColComp(); - linalg::BandedEigenSparseRowIterator jac(_jacDisc, 0); - for (unsigned int i = 0; i < _disc.nPoints; ++i, jac += gapNode) - { - for (unsigned int j = 0; j < _disc.nComp; ++j, ++jac) - { - // Add time derivative to liquid states (on main diagonal) - jac[0] += 1.0; - } - } - - const double invBeta = 1.0 / static_cast(_totalPorosity) - 1.0; - for (unsigned int node = 0; node < _disc.nPoints; ++node) - { - // Assemble - linalg::BandedEigenSparseRowIterator jac(_jacDisc, idxr.strideColNode() * node); - - // Mobile and solid phase (advances jac accordingly) - addTimeDerivativeToJacobianNode(jac, idxr, 1.0, invBeta); - - // Iterator jac has already been advanced to next shell - - // Overwrite rows corresponding to algebraic equations with the Jacobian and set right hand side to 0 - if (_binding[0]->hasQuasiStationaryReactions()) - { - // Get iterators to beginning of solid phase - linalg::BandedEigenSparseRowIterator jacSolidOrig(_jac, idxr.strideColNode() * node + idxr.strideColLiquid()); - linalg::BandedEigenSparseRowIterator jacSolid = jac - idxr.strideColLiquid(); - - int const* const mask = _binding[0]->reactionQuasiStationarity(); - double* const qShellDot = sensYdot + idxr.offsetC() + idxr.strideColNode() * node + idxr.strideColLiquid(); - - // Copy row from original Jacobian and set right hand side - for (unsigned int i = 0; i < _disc.strideBound; ++i, ++jacSolid, ++jacSolidOrig) - { - if (!mask[i]) - continue; - - jacSolid.copyRowFrom(jacSolidOrig); - - // Right hand side is -\frac{\partial^2 res(t, y, \dot{y})}{\partial p \partial t} - // If the residual is not explicitly depending on time, this expression is 0 - // @todo This is wrong if external functions are used. Take that into account! - qShellDot[i] = 0.0; - } - } - } - - _linearSolver->factorize(_jacDisc); + } - if (_linearSolver->info() != Success) { - LOG(Error) << "factorization failed in sensitivity initialization"; - } + // Step 2: Compute the correct time derivative of the state vector - Map sensYdot_vec(sensYdot + idxr.offsetC(), numPureDofs()); - sensYdot_vec = _linearSolver->solve(sensYdot_vec); + // Compute right hand side by adding -dF / dy * s = -J * s to -dF / dp which is already stored in sensYdot + multiplyWithJacobian(simTime, simState, sensY, -1.0, 1.0, sensYdot); - // Use the factors to solve the linear system - if (_linearSolver->info() != Success) { - LOG(Error) << "solve failed in sensitivity initialization"; - } + // Note that we have correctly negated the right hand side + setPattern(_jacDisc, true, _dynReaction[0] && (_dynReaction[0]->numReactionsCombined() > 0)); + // for (int entry = 0; entry < _jacDisc.nonZeros(); entry++) + // _jacDisc.valuePtr()[entry] = 0.0; + + // Handle transport equations (dc_i / dt terms) + const int gapNode = idxr.strideColNode() - static_cast(_disc.nComp) * idxr.strideColComp(); + linalg::BandedEigenSparseRowIterator jac(_jacDisc, 0); + for (unsigned int i = 0; i < _disc.nPoints; ++i, jac += gapNode) + { + for (unsigned int j = 0; j < _disc.nComp; ++j, ++jac) + { + // Add time derivative to liquid states (on main diagonal) + jac[0] += 1.0; } } - /** - * @brief Computes approximately / partially consistent initial values and time derivatives of sensitivity subsystems - * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, - * the sensitivity system for a parameter @f$ p @f$ reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of this linear DAE, @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ - * have to be consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the time - * derivative \f$ \dot{s}_0 \f$ such that they are consistent. - * - * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized version of it. - * This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ are - * related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them - * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p}. @f$ - *
                                            - *
                                          1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations).
                                          2. - *
                                          3. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual is 0 for the - * mobile phase variables.
                                          4. - *
                                          - * This function requires the parameter sensitivities to be computed beforehand and up-to-date Jacobians. - * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) - * @param [in] simState Consistent state of the simulation (state vector and its time derivative) - * @param [in,out] vecSensY Sensitivity subsystem state vectors - * @param [in,out] vecSensYdot Time derivative state vectors of the sensitivity subsystems to be initialized - * @param [in] adRes Pointer to residual vector of AD datatypes with parameter sensitivities - * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) - */ - void LumpedRateModelWithoutPoresDG::leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) + const double invBeta = 1.0 / static_cast(_totalPorosity) - 1.0; + for (unsigned int node = 0; node < _disc.nPoints; ++node) { - BENCH_SCOPE(_timerConsistentInit); + // Assemble + linalg::BandedEigenSparseRowIterator jac(_jacDisc, idxr.strideColNode() * node); - Indexer idxr(_disc); + // Mobile and solid phase (advances jac accordingly) + addTimeDerivativeToJacobianNode(jac, idxr, 1.0, invBeta); - for (std::size_t param = 0; param < vecSensY.size(); ++param) - { - double* const sensY = vecSensY[param]; - double* const sensYdot = vecSensYdot[param]; + // Iterator jac has already been advanced to next shell - // Copy parameter derivative from AD to tempState and negate it - for (unsigned int node = 0; node < _disc.nPoints; ++node) - { - const unsigned int localOffset = idxr.offsetC() + node * idxr.strideColNode(); - for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - { - _tempState[localOffset + comp] = -adRes[localOffset + comp].getADValue(param); - } - } - - // Step 2: Compute the correct time derivative of the state vector + // Overwrite rows corresponding to algebraic equations with the Jacobian and set right hand side to 0 + if (_binding[0]->hasQuasiStationaryReactions()) + { + // Get iterators to beginning of solid phase + linalg::BandedEigenSparseRowIterator jacSolidOrig(_jac, + idxr.strideColNode() * node + idxr.strideColLiquid()); + linalg::BandedEigenSparseRowIterator jacSolid = jac - idxr.strideColLiquid(); - // Compute right hand side by adding -dF / dy * s = -J * s to -dF / dp which is already stored in _tempState - multiplyWithJacobian(simTime, simState, sensY, -1.0, 1.0, _tempState); + int const* const mask = _binding[0]->reactionQuasiStationarity(); + double* const qShellDot = + sensYdot + idxr.offsetC() + idxr.strideColNode() * node + idxr.strideColLiquid(); - const double invBeta = (1.0 / static_cast(_totalPorosity) - 1.0); - for (unsigned int node = 0; node < _disc.nPoints; ++node) + // Copy row from original Jacobian and set right hand side + for (unsigned int i = 0; i < _disc.strideBound; ++i, ++jacSolid, ++jacSolidOrig) { - // Offset to current cell's c and q variables - const unsigned int localOffset = idxr.offsetC() + node * idxr.strideColNode(); - const unsigned int localOffsetQ = localOffset + idxr.strideColLiquid(); + if (!mask[i]) + continue; - for (unsigned int comp = 0; comp < _disc.nComp; ++comp) - { - // dq_{i,j} / dt is assumed to be fixed, so bring it on the right hand side - for (unsigned int i = 0; i < _disc.nBound[comp]; ++i) - { - _tempState[localOffset + comp] -= invBeta * sensYdot[localOffsetQ + _disc.boundOffset[comp] + i]; - } + jacSolid.copyRowFrom(jacSolidOrig); - sensYdot[localOffset + comp] = _tempState[localOffset + comp]; - } + // Right hand side is -\frac{\partial^2 res(t, y, \dot{y})}{\partial p \partial t} + // If the residual is not explicitly depending on time, this expression is 0 + // @todo This is wrong if external functions are used. Take that into account! + qShellDot[i] = 0.0; } } } - bool LumpedRateModelWithoutPoresDG::setParameter(const ParameterId& pId, double value) - { - if (_convDispOp.setParameter(pId, value)) - return true; - - return UnitOperationBase::setParameter(pId, value); - } + _linearSolver->factorize(_jacDisc); - void LumpedRateModelWithoutPoresDG::setSensitiveParameterValue(const ParameterId& pId, double value) + if (_linearSolver->info() != Success) { - if (_convDispOp.setSensitiveParameterValue(_sensParams, pId, value)) - return; - - UnitOperationBase::setSensitiveParameterValue(pId, value); + LOG(Error) << "factorization failed in sensitivity initialization"; } - bool LumpedRateModelWithoutPoresDG::setSensitiveParameter(const ParameterId& pId, unsigned int adDirection, double adValue) - { - if (_convDispOp.setSensitiveParameter(_sensParams, pId, adDirection, adValue)) - { - LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; - return true; - } + Map sensYdot_vec(sensYdot + idxr.offsetC(), numPureDofs()); + sensYdot_vec = _linearSolver->solve(sensYdot_vec); - return UnitOperationBase::setSensitiveParameter(pId, adDirection, adValue); + // Use the factors to solve the linear system + if (_linearSolver->info() != Success) + { + LOG(Error) << "solve failed in sensitivity initialization"; } + } +} + +/** + * @brief Computes approximately / partially consistent initial values and time derivatives of sensitivity subsystems + * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, + * the sensitivity system for a parameter @f$ p @f$ reads + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of this linear DAE, @f$ s_0 = + * \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ have to be + * consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the + * time derivative \f$ \dot{s}_0 \f$ such that they are consistent. + * + * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized + * version of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ + * \dot{s}_0 \f$ are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by + * differentiating them with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = + * \frac{\partial \dot{y}_0}{\partial p}. @f$
                                          1. Keep state and time derivative vectors as they are (i.e., do not + * solve algebraic equations).
                                          2. Compute the time derivatives of the state @f$ \dot{y} @f$ such that the residual + * is 0 for the mobile phase variables.
                                          3. + *
                                          + * This function requires the parameter sensitivities to be computed beforehand and up-to-date Jacobians. + * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) + * @param [in] simState Consistent state of the simulation (state vector and its time derivative) + * @param [in,out] vecSensY Sensitivity subsystem state vectors + * @param [in,out] vecSensYdot Time derivative state vectors of the sensitivity subsystems to be initialized + * @param [in] adRes Pointer to residual vector of AD datatypes with parameter sensitivities + * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) + */ +void LumpedRateModelWithoutPoresDG::leanConsistentInitialSensitivity( + const SimulationTime& simTime, const ConstSimulationState& simState, std::vector& vecSensY, + std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +{ + BENCH_SCOPE(_timerConsistentInit); + + Indexer idxr(_disc); + for (std::size_t param = 0; param < vecSensY.size(); ++param) + { + double* const sensY = vecSensY[param]; + double* const sensYdot = vecSensYdot[param]; - int LumpedRateModelWithoutPoresDG::Exporter::writeMobilePhase(double* buffer) const + // Copy parameter derivative from AD to tempState and negate it + for (unsigned int node = 0; node < _disc.nPoints; ++node) { - const int stride = _idx.strideColNode(); - double const* ptr = _data + _idx.offsetC(); - for (unsigned int i = 0; i < _disc.nPoints; ++i) + const unsigned int localOffset = idxr.offsetC() + node * idxr.strideColNode(); + for (unsigned int comp = 0; comp < _disc.nComp; ++comp) { - std::copy_n(ptr, _disc.nComp, buffer); - buffer += _disc.nComp; - ptr += stride; + _tempState[localOffset + comp] = -adRes[localOffset + comp].getADValue(param); } - return _disc.nPoints * _disc.nComp; } - int LumpedRateModelWithoutPoresDG::Exporter::writeSolidPhase(double* buffer) const + // Step 2: Compute the correct time derivative of the state vector + + // Compute right hand side by adding -dF / dy * s = -J * s to -dF / dp which is already stored in _tempState + multiplyWithJacobian(simTime, simState, sensY, -1.0, 1.0, _tempState); + + const double invBeta = (1.0 / static_cast(_totalPorosity) - 1.0); + for (unsigned int node = 0; node < _disc.nPoints; ++node) { - const int stride = _idx.strideColNode(); - double const* ptr = _data + _idx.offsetC() + _idx.strideColLiquid(); - for (unsigned int i = 0; i < _disc.nPoints; ++i) + // Offset to current cell's c and q variables + const unsigned int localOffset = idxr.offsetC() + node * idxr.strideColNode(); + const unsigned int localOffsetQ = localOffset + idxr.strideColLiquid(); + + for (unsigned int comp = 0; comp < _disc.nComp; ++comp) { - std::copy_n(ptr, _disc.strideBound, buffer); - buffer += _disc.strideBound; - ptr += stride; + // dq_{i,j} / dt is assumed to be fixed, so bring it on the right hand side + for (unsigned int i = 0; i < _disc.nBound[comp]; ++i) + { + _tempState[localOffset + comp] -= invBeta * sensYdot[localOffsetQ + _disc.boundOffset[comp] + i]; + } + + sensYdot[localOffset + comp] = _tempState[localOffset + comp]; } - return _disc.nPoints * _disc.strideBound; } + } +} - int LumpedRateModelWithoutPoresDG::Exporter::writeSolidPhase(unsigned int parType, double* buffer) const - { - cadet_assert(parType == 0); - return writeSolidPhase(buffer); - } +bool LumpedRateModelWithoutPoresDG::setParameter(const ParameterId& pId, double value) +{ + if (_convDispOp.setParameter(pId, value)) + return true; - int LumpedRateModelWithoutPoresDG::Exporter::writeInlet(unsigned int port, double* buffer) const - { - cadet_assert(port == 0); - std::copy_n(_data, _disc.nComp, buffer); - return _disc.nComp; - } + return UnitOperationBase::setParameter(pId, value); +} - int LumpedRateModelWithoutPoresDG::Exporter::writeInlet(double* buffer) const - { - std::copy_n(_data, _disc.nComp, buffer); - return _disc.nComp; - } +void LumpedRateModelWithoutPoresDG::setSensitiveParameterValue(const ParameterId& pId, double value) +{ + if (_convDispOp.setSensitiveParameterValue(_sensParams, pId, value)) + return; - int LumpedRateModelWithoutPoresDG::Exporter::writeOutlet(unsigned int port, double* buffer) const - { - cadet_assert(port == 0); + UnitOperationBase::setSensitiveParameterValue(pId, value); +} - if (_model._convDispOp.currentVelocity() >= 0) - std::copy_n(&_idx.c(_data, _disc.nPoints - 1, 0), _disc.nComp, buffer); - else - std::copy_n(&_idx.c(_data, 0, 0), _disc.nComp, buffer); +bool LumpedRateModelWithoutPoresDG::setSensitiveParameter(const ParameterId& pId, unsigned int adDirection, + double adValue) +{ + if (_convDispOp.setSensitiveParameter(_sensParams, pId, adDirection, adValue)) + { + LOG(Debug) << "Found parameter " << pId << ": Dir " << adDirection << " is set to " << adValue; + return true; + } - return _disc.nComp; - } + return UnitOperationBase::setSensitiveParameter(pId, adDirection, adValue); +} - int LumpedRateModelWithoutPoresDG::Exporter::writeOutlet(double* buffer) const - { - if (_model._convDispOp.currentVelocity() >= 0) - std::copy_n(&_idx.c(_data, _disc.nPoints - 1, 0), _disc.nComp, buffer); - else - std::copy_n(&_idx.c(_data, 0, 0), _disc.nComp, buffer); +int LumpedRateModelWithoutPoresDG::Exporter::writeMobilePhase(double* buffer) const +{ + const int stride = _idx.strideColNode(); + double const* ptr = _data + _idx.offsetC(); + for (unsigned int i = 0; i < _disc.nPoints; ++i) + { + std::copy_n(ptr, _disc.nComp, buffer); + buffer += _disc.nComp; + ptr += stride; + } + return _disc.nPoints * _disc.nComp; +} + +int LumpedRateModelWithoutPoresDG::Exporter::writeSolidPhase(double* buffer) const +{ + const int stride = _idx.strideColNode(); + double const* ptr = _data + _idx.offsetC() + _idx.strideColLiquid(); + for (unsigned int i = 0; i < _disc.nPoints; ++i) + { + std::copy_n(ptr, _disc.strideBound, buffer); + buffer += _disc.strideBound; + ptr += stride; + } + return _disc.nPoints * _disc.strideBound; +} + +int LumpedRateModelWithoutPoresDG::Exporter::writeSolidPhase(unsigned int parType, double* buffer) const +{ + cadet_assert(parType == 0); + return writeSolidPhase(buffer); +} - return _disc.nComp; - } +int LumpedRateModelWithoutPoresDG::Exporter::writeInlet(unsigned int port, double* buffer) const +{ + cadet_assert(port == 0); + std::copy_n(_data, _disc.nComp, buffer); + return _disc.nComp; +} + +int LumpedRateModelWithoutPoresDG::Exporter::writeInlet(double* buffer) const +{ + std::copy_n(_data, _disc.nComp, buffer); + return _disc.nComp; +} + +int LumpedRateModelWithoutPoresDG::Exporter::writeOutlet(unsigned int port, double* buffer) const +{ + cadet_assert(port == 0); + + if (_model._convDispOp.currentVelocity() >= 0) + std::copy_n(&_idx.c(_data, _disc.nPoints - 1, 0), _disc.nComp, buffer); + else + std::copy_n(&_idx.c(_data, 0, 0), _disc.nComp, buffer); + + return _disc.nComp; +} + +int LumpedRateModelWithoutPoresDG::Exporter::writeOutlet(double* buffer) const +{ + if (_model._convDispOp.currentVelocity() >= 0) + std::copy_n(&_idx.c(_data, _disc.nPoints - 1, 0), _disc.nComp, buffer); + else + std::copy_n(&_idx.c(_data, 0, 0), _disc.nComp, buffer); + + return _disc.nComp; +} - } // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/LumpedRateModelWithoutPoresDG.hpp b/src/libcadet/model/LumpedRateModelWithoutPoresDG.hpp index a7d33f1f3..63a713fde 100644 --- a/src/libcadet/model/LumpedRateModelWithoutPoresDG.hpp +++ b/src/libcadet/model/LumpedRateModelWithoutPoresDG.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -45,487 +45,695 @@ using namespace Eigen; namespace cadet { - namespace model - { - - /** - * @brief Lumped rate model of liquid column chromatography without pores - * @details See @cite Guiochon2006, @cite Gu1995, @cite Felinger2004 - * - * @f[\begin{align} - \frac{\partial c_i}{\partial t} + \frac{1 - \varepsilon_t}{\varepsilon_t} \frac{\partial q_{i}}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 c_i}{\partial z^2} \\ - a \frac{\partial q_i}{\partial t} &= f_{\text{iso}}(c, q) - \end{align} @f] - * Danckwerts boundary conditions (see @cite Danckwerts1953) - @f[ \begin{align} - u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partial z}(t,0) \\ - \frac{\partial c_i}{\partial z}(t,L) &= 0 - \end{align} @f] - * Methods are described in @cite Breuer2023 (DGSEM discretization), @cite Puttmann2013 @cite Puttmann2016 (forward sensitivities, AD, band compression) - */ - class LumpedRateModelWithoutPoresDG : public UnitOperationBase - { - public: - - LumpedRateModelWithoutPoresDG(UnitOpIdx unitOpIdx); - virtual ~LumpedRateModelWithoutPoresDG() CADET_NOEXCEPT; - - virtual unsigned int numDofs() const CADET_NOEXCEPT; - virtual unsigned int numPureDofs() const CADET_NOEXCEPT; - virtual bool usesAD() const CADET_NOEXCEPT; - virtual unsigned int requiredADdirs() const CADET_NOEXCEPT; - - virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT { return _unitOpIdx; } - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } - virtual void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT; - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual bool canAccumulate() const CADET_NOEXCEPT { return false; } - - static const char* identifier() { return "LUMPED_RATE_MODEL_WITHOUT_PORES_DG"; } - virtual const char* unitOperationName() const CADET_NOEXCEPT { return identifier(); } - - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper); - virtual bool configure(IParameterProvider& paramProvider); - virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac); - - virtual void useAnalyticJacobian(const bool analyticJac); - - virtual void reportSolution(ISolutionRecorder& recorder, double const* const solution) const; - virtual void reportSolutionStructure(ISolutionRecorder& recorder) const; - - virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem); - - virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - - virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3); - - virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState); - - virtual void prepareADvectors(const AdJacobianParams& adJac) const; - - virtual void applyInitialCondition(const SimulationState& simState) const; - virtual void readInitialCondition(IParameterProvider& paramProvider); - - virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); - - virtual void initializeSensitivityStates(const std::vector& vecSensY) const; - virtual void consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); +namespace model +{ - virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem); +/** +* @brief Lumped rate model of liquid column chromatography without pores +* @details See @cite Guiochon2006, @cite Gu1995, @cite Felinger2004 +* +* @f[\begin{align} + \frac{\partial c_i}{\partial t} + \frac{1 - \varepsilon_t}{\varepsilon_t} \frac{\partial q_{i}}{\partial t} &= - +u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 c_i}{\partial z^2} \\ a \frac{\partial +q_i}{\partial t} &= f_{\text{iso}}(c, q) \end{align} @f] +* Danckwerts boundary conditions (see @cite Danckwerts1953) + @f[ \begin{align} + u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partial z}(t,0) \\ + \frac{\partial c_i}{\partial z}(t,L) &= 0 + \end{align} @f] +* Methods are described in @cite Breuer2023 (DGSEM discretization), @cite Puttmann2013 @cite Puttmann2016 (forward +sensitivities, AD, band compression) +*/ +class LumpedRateModelWithoutPoresDG : public UnitOperationBase +{ +public: + LumpedRateModelWithoutPoresDG(UnitOpIdx unitOpIdx); + virtual ~LumpedRateModelWithoutPoresDG() CADET_NOEXCEPT; - virtual void leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + virtual unsigned int numDofs() const CADET_NOEXCEPT; + virtual unsigned int numPureDofs() const CADET_NOEXCEPT; + virtual bool usesAD() const CADET_NOEXCEPT; + virtual unsigned int requiredADdirs() const CADET_NOEXCEPT; - virtual bool hasInlet() const CADET_NOEXCEPT { return true; } - virtual bool hasOutlet() const CADET_NOEXCEPT { return true; } + virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT + { + return _unitOpIdx; + } + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } + virtual void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT; + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual bool canAccumulate() const CADET_NOEXCEPT + { + return false; + } - virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT; - virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT; - virtual unsigned int localInletComponentIndex(unsigned int port) const CADET_NOEXCEPT; - virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT; + static const char* identifier() + { + return "LUMPED_RATE_MODEL_WITHOUT_PORES_DG"; + } + virtual const char* unitOperationName() const CADET_NOEXCEPT + { + return identifier(); + } + + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper); + virtual bool configure(IParameterProvider& paramProvider); + virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac); + + virtual void useAnalyticJacobian(const bool analyticJac); + + virtual void reportSolution(ISolutionRecorder& recorder, double const* const solution) const; + virtual void reportSolutionStructure(ISolutionRecorder& recorder) const; + + virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem); + + virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + + virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3); + + virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, + const ConstSimulationState& simState); + + virtual void prepareADvectors(const AdJacobianParams& adJac) const; + + virtual void applyInitialCondition(const SimulationState& simState) const; + virtual void readInitialCondition(IParameterProvider& paramProvider); + + virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); + + virtual void initializeSensitivityStates(const std::vector& vecSensY) const; + virtual void consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + + virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem); + + virtual void leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + + virtual bool hasInlet() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasOutlet() const CADET_NOEXCEPT + { + return true; + } - virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size); - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { } + virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT; + virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT; + virtual unsigned int localInletComponentIndex(unsigned int port) const CADET_NOEXCEPT; + virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT; - virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut); + virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size); + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) + { + } - virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret); - virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret); + virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut); - inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double* ret) - { - multiplyWithJacobian(simTime, simState, yS, 1.0, 0.0, ret); - } + virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret); + virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* sDot, double* ret); - virtual bool setParameter(const ParameterId& pId, double value); - virtual bool setSensitiveParameter(const ParameterId& pId, unsigned int adDirection, double adValue); - virtual void setSensitiveParameterValue(const ParameterId& id, double value); + inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double* ret) + { + multiplyWithJacobian(simTime, simState, yS, 1.0, 0.0, ret); + } - virtual unsigned int threadLocalMemorySize() const CADET_NOEXCEPT; + virtual bool setParameter(const ParameterId& pId, double value); + virtual bool setSensitiveParameter(const ParameterId& pId, unsigned int adDirection, double adValue); + virtual void setSensitiveParameterValue(const ParameterId& id, double value); + virtual unsigned int threadLocalMemorySize() const CADET_NOEXCEPT; #ifdef CADET_BENCHMARK_MODE - virtual std::vector benchmarkTimings() const - { - return std::vector({ - static_cast(numDofs()), - _timerResidual.totalElapsedTime(), - _timerResidualPar.totalElapsedTime(), - _timerResidualSens.totalElapsedTime(), - _timerResidualSensPar.totalElapsedTime(), - _timerConsistentInit.totalElapsedTime(), - _timerConsistentInitPar.totalElapsedTime(), - _timerLinearSolve.totalElapsedTime() - }); - } + virtual std::vector benchmarkTimings() const + { + return std::vector({static_cast(numDofs()), _timerResidual.totalElapsedTime(), + _timerResidualPar.totalElapsedTime(), _timerResidualSens.totalElapsedTime(), + _timerResidualSensPar.totalElapsedTime(), _timerConsistentInit.totalElapsedTime(), + _timerConsistentInitPar.totalElapsedTime(), _timerLinearSolve.totalElapsedTime()}); + } - virtual char const* const* benchmarkDescriptions() const - { - static const char* const desc[] = { - "DOFs", - "Residual", - "ResidualPar", - "ResidualSens", - "ResidualSensPar", - "ConsistentInit", - "ConsistentInitPar", - "LinearSolve" - }; - return desc; - } + virtual char const* const* benchmarkDescriptions() const + { + static const char* const desc[] = { + "DOFs", "Residual", "ResidualPar", "ResidualSens", "ResidualSensPar", + "ConsistentInit", "ConsistentInitPar", "LinearSolve"}; + return desc; + } #endif - protected: - - class Indexer; +protected: + class Indexer; - int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity); + int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, + bool paramSensitivity); - template - int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); + template + int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, + ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); - void extractJacobianFromAD(active const* const adRes, unsigned int adDirOffset); + void extractJacobianFromAD(active const* const adRes, unsigned int adDirOffset); - void assembleDiscretizedJacobian(double alpha, const Indexer& idxr); - void addTimeDerivativeToJacobianNode(linalg::BandedEigenSparseRowIterator& jac, const Indexer& idxr, double alpha, double invBetaP) const; + void assembleDiscretizedJacobian(double alpha, const Indexer& idxr); + void addTimeDerivativeToJacobianNode(linalg::BandedEigenSparseRowIterator& jac, const Indexer& idxr, double alpha, + double invBetaP) const; #ifdef CADET_CHECK_ANALYTIC_JACOBIAN - void checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const; + void checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const; #endif - class Discretization - { - public: - unsigned int nComp; //!< Number of components - unsigned int nCol; //!< Number of column cells - unsigned int polyDeg; //!< polynomial degree - unsigned int nNodes; //!< Number of nodes per cell - unsigned int nPoints; //!< Number of discrete Points - bool exactInt; //!< 1 for exact integration, 0 for LGL quadrature - - unsigned int* nBound; //!< Array with number of bound states for each component - unsigned int* boundOffset; //!< Array with offset to the first bound state of each component in the solid phase - unsigned int strideBound; //!< Total number of bound states - - int curSection; //!< current section index - bool newStaticJac; //!< determines wether static analytical jacobian needs to be computed (every section) - }; - - Discretization _disc; //!< Discretization info - - // IExternalFunction* _extFun; //!< External function (owned by library user) - - // used as auxiliary supplier - parts::AxialConvectionDispersionOperatorBaseDG _convDispOp; //!< Convection dispersion operator for interstitial volume transport - - cadet::linalg::EigenSolverBase* _linearSolver; //!< Linear solver - - Eigen::SparseMatrix _jac; //!< Jacobian - Eigen::SparseMatrix _jacDisc; //!< Jacobian with time derivatives from BDF method - Eigen::MatrixXd _jacInlet; //!< Jacobian inlet DOF block matrix connects inlet DOFs to first bulk cells - //MatrixXd FDjac; // test purpose FD Jacobian - - active _totalPorosity; //!< Total porosity \f$ \varepsilon_t \f$ - - bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used - unsigned int _jacobianAdDirs; //!< Number of AD seed vectors required for Jacobian computation - bool _factorizeJacobian; //!< Determines whether the Jacobian needs to be factorized - double* _tempState; //!< Temporary storage with the size of the state vector or larger if binding models require it + class Discretization + { + public: + unsigned int nComp; //!< Number of components + unsigned int nCol; //!< Number of column cells + unsigned int polyDeg; //!< polynomial degree + unsigned int nNodes; //!< Number of nodes per cell + unsigned int nPoints; //!< Number of discrete Points + bool exactInt; //!< 1 for exact integration, 0 for LGL quadrature + + unsigned int* nBound; //!< Array with number of bound states for each component + unsigned int* boundOffset; //!< Array with offset to the first bound state of each component in the solid phase + unsigned int strideBound; //!< Total number of bound states + + int curSection; //!< current section index + bool newStaticJac; //!< determines wether static analytical jacobian needs to be computed (every section) + }; + + Discretization _disc; //!< Discretization info + + // IExternalFunction* _extFun; //!< External function (owned by library user) + + // used as auxiliary supplier + parts::AxialConvectionDispersionOperatorBaseDG + _convDispOp; //!< Convection dispersion operator for interstitial volume transport + + cadet::linalg::EigenSolverBase* _linearSolver; //!< Linear solver + + Eigen::SparseMatrix _jac; //!< Jacobian + Eigen::SparseMatrix _jacDisc; //!< Jacobian with time derivatives from BDF method + Eigen::MatrixXd _jacInlet; //!< Jacobian inlet DOF block matrix connects inlet DOFs to first bulk cells + // MatrixXd FDjac; // test purpose FD Jacobian + + active _totalPorosity; //!< Total porosity \f$ \varepsilon_t \f$ + + bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used + unsigned int _jacobianAdDirs; //!< Number of AD seed vectors required for Jacobian computation + bool _factorizeJacobian; //!< Determines whether the Jacobian needs to be factorized + double* _tempState; //!< Temporary storage with the size of the state vector or larger if binding models require it + + std::vector _initC; //!< Liquid phase initial conditions + std::vector _initQ; //!< Solid phase initial conditions + std::vector _initState; //!< Initial conditions for state vector if given + std::vector _initStateDot; //!< Initial conditions for time derivative + + BENCH_TIMER(_timerResidual) + BENCH_TIMER(_timerResidualPar) + BENCH_TIMER(_timerResidualSens) + BENCH_TIMER(_timerResidualSensPar) + BENCH_TIMER(_timerConsistentInit) + BENCH_TIMER(_timerConsistentInitPar) + BENCH_TIMER(_timerLinearSolve) + + class Indexer + { + public: + Indexer(const Discretization& disc) : _disc(disc) + { + } - std::vector _initC; //!< Liquid phase initial conditions - std::vector _initQ; //!< Solid phase initial conditions - std::vector _initState; //!< Initial conditions for state vector if given - std::vector _initStateDot; //!< Initial conditions for time derivative + // Strides + inline int strideColNode() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp + _disc.strideBound); + } + inline int strideColCell() const CADET_NOEXCEPT + { + return static_cast(_disc.nNodes * strideColNode()); + } + inline int strideColComp() const CADET_NOEXCEPT + { + return 1; + } - BENCH_TIMER(_timerResidual) - BENCH_TIMER(_timerResidualPar) - BENCH_TIMER(_timerResidualSens) - BENCH_TIMER(_timerResidualSensPar) - BENCH_TIMER(_timerConsistentInit) - BENCH_TIMER(_timerConsistentInitPar) - BENCH_TIMER(_timerLinearSolve) + inline int strideColLiquid() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideColBound() const CADET_NOEXCEPT + { + return static_cast(_disc.strideBound); + } - class Indexer - { - public: - Indexer(const Discretization& disc) : _disc(disc) { } + // Offsets + inline int offsetC() const CADET_NOEXCEPT + { + return _disc.nComp; + } + inline int offsetBoundComp(unsigned int comp) const CADET_NOEXCEPT + { + return _disc.boundOffset[comp]; + } - // Strides - inline int strideColNode() const CADET_NOEXCEPT { return static_cast(_disc.nComp + _disc.strideBound); } - inline int strideColCell() const CADET_NOEXCEPT { return static_cast(_disc.nNodes * strideColNode()); } - inline int strideColComp() const CADET_NOEXCEPT { return 1; } + // Return pointer to first element of state variable in state vector + template inline real_t* c(real_t* const data) const + { + return data + offsetC(); + } + template inline real_t const* c(real_t const* const data) const + { + return data + offsetC(); + } - inline int strideColLiquid() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideColBound() const CADET_NOEXCEPT { return static_cast(_disc.strideBound); } + template inline real_t* q(real_t* const data) const + { + return data + offsetC() + strideColLiquid(); + } + template inline real_t const* q(real_t const* const data) const + { + return data + offsetC() + strideColLiquid(); + } - // Offsets - inline int offsetC() const CADET_NOEXCEPT { return _disc.nComp; } - inline int offsetBoundComp(unsigned int comp) const CADET_NOEXCEPT { return _disc.boundOffset[comp]; } + // Return specific variable in state vector + template inline real_t& c(real_t* const data, unsigned int node, unsigned int comp) const + { + return data[offsetC() + comp * strideColComp() + node * strideColNode()]; + } + template + inline const real_t& c(real_t const* const data, unsigned int node, unsigned int comp) const + { + return data[offsetC() + comp * strideColComp() + node * strideColNode()]; + } - // Return pointer to first element of state variable in state vector - template inline real_t* c(real_t* const data) const { return data + offsetC(); } - template inline real_t const* c(real_t const* const data) const { return data + offsetC(); } + protected: + const Discretization& _disc; + }; - template inline real_t* q(real_t* const data) const { return data + offsetC() + strideColLiquid(); } - template inline real_t const* q(real_t const* const data) const { return data + offsetC() + strideColLiquid(); } + class Exporter : public ISolutionExporter + { + public: + Exporter(const Discretization& disc, const LumpedRateModelWithoutPoresDG& model, double const* data) + : _disc(disc), _idx(disc), _model(model), _data(data) + { + } + Exporter(const Discretization&& disc, const LumpedRateModelWithoutPoresDG& model, double const* data) = delete; - // Return specific variable in state vector - template inline real_t& c(real_t* const data, unsigned int node, unsigned int comp) const { return data[offsetC() + comp * strideColComp() + node * strideColNode()]; } - template inline const real_t& c(real_t const* const data, unsigned int node, unsigned int comp) const { return data[offsetC() + comp * strideColComp() + node * strideColNode()]; } + virtual bool hasParticleFlux() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasSolidPhase() const CADET_NOEXCEPT + { + return _disc.strideBound > 0; + } + virtual bool hasVolume() const CADET_NOEXCEPT + { + return false; + } + virtual bool isParticleLumped() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasPrimaryExtent() const CADET_NOEXCEPT + { + return true; + } - protected: - const Discretization& _disc; - }; + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } + virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT + { + return _disc.nPoints; + } + virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numParticleTypes() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.strideBound; + } + virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nPoints; + } + virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return _disc.strideBound * _disc.nPoints; + } + virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT + { + return _disc.strideBound * _disc.nPoints; + } + virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT + { + return 0u; + } + virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT + { + return 0; + } - class Exporter : public ISolutionExporter + virtual int writeMobilePhase(double* buffer) const; + virtual int writeSolidPhase(double* buffer) const; + virtual int writeParticleMobilePhase(double* buffer) const + { + return 0; + } + virtual int writeSolidPhase(unsigned int parType, double* buffer) const; + virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const + { + return 0; + } + virtual int writeParticleFlux(double* buffer) const + { + return 0; + } + virtual int writeParticleFlux(unsigned int parType, double* buffer) const + { + return 0; + } + virtual int writeVolume(double* buffer) const + { + return 0; + } + virtual int writeInlet(unsigned int port, double* buffer) const; + virtual int writeInlet(double* buffer) const; + virtual int writeOutlet(unsigned int port, double* buffer) const; + virtual int writeOutlet(double* buffer) const; + /** + * @brief calculates the physical node coordinates of the DG discretization with double! interface nodes + */ + virtual int writePrimaryCoordinates(double* coords) const + { + for (unsigned int i = 0; i < _disc.nCol; i++) { - public: - - Exporter(const Discretization& disc, const LumpedRateModelWithoutPoresDG& model, double const* data) : _disc(disc), _idx(disc), _model(model), _data(data) { } - Exporter(const Discretization&& disc, const LumpedRateModelWithoutPoresDG& model, double const* data) = delete; - - virtual bool hasParticleFlux() const CADET_NOEXCEPT { return false; } - virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT { return false; } - virtual bool hasSolidPhase() const CADET_NOEXCEPT { return _disc.strideBound > 0; } - virtual bool hasVolume() const CADET_NOEXCEPT { return false; } - virtual bool isParticleLumped() const CADET_NOEXCEPT { return false; } - virtual bool hasPrimaryExtent() const CADET_NOEXCEPT { return true; } - - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } - virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT { return _disc.nPoints; } - virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numParticleTypes() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT { return _disc.strideBound; } - virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT { return _disc.nComp * _disc.nPoints; } - virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return _disc.strideBound * _disc.nPoints; } - virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT { return _disc.strideBound * _disc.nPoints; } - virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT { return 0u; } - virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT { return 0; } - - virtual int writeMobilePhase(double* buffer) const; - virtual int writeSolidPhase(double* buffer) const; - virtual int writeParticleMobilePhase(double* buffer) const { return 0; } - virtual int writeSolidPhase(unsigned int parType, double* buffer) const; - virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const { return 0; } - virtual int writeParticleFlux(double* buffer) const { return 0; } - virtual int writeParticleFlux(unsigned int parType, double* buffer) const { return 0; } - virtual int writeVolume(double* buffer) const { return 0; } - virtual int writeInlet(unsigned int port, double* buffer) const; - virtual int writeInlet(double* buffer) const; - virtual int writeOutlet(unsigned int port, double* buffer) const; - virtual int writeOutlet(double* buffer) const; - /** - * @brief calculates the physical node coordinates of the DG discretization with double! interface nodes - */ - virtual int writePrimaryCoordinates(double* coords) const + for (unsigned int j = 0; j < _disc.nNodes; j++) { - for (unsigned int i = 0; i < _disc.nCol; i++) { - for (unsigned int j = 0; j < _disc.nNodes; j++) { - // mapping - coords[i * _disc.nNodes + j] = _model._convDispOp.cellLeftBound(i) + 0.5 * (static_cast(_model._convDispOp.columnLength()) / static_cast(_disc.nCol)) * (1.0 + _model._convDispOp.LGLnodes()[j]); - } - } - return _disc.nPoints; - } - - virtual int writeSecondaryCoordinates(double* coords) const { return 0; } - virtual int writeParticleCoordinates(unsigned int parType, double* coords) const { return 0; } - - protected: - const Discretization& _disc; - const Indexer _idx; - const LumpedRateModelWithoutPoresDG& _model; - double const* const _data; - }; - - /** - * @brief sets the current section index and section dependend velocity, dispersion - */ - void updateSection(int secIdx) { - - if (_disc.curSection != secIdx) { - _disc.curSection = secIdx; - _disc.newStaticJac = true; + // mapping + coords[i * _disc.nNodes + j] = + _model._convDispOp.cellLeftBound(i) + + 0.5 * + (static_cast(_model._convDispOp.columnLength()) / static_cast(_disc.nCol)) * + (1.0 + _model._convDispOp.LGLnodes()[j]); } } + return _disc.nPoints; + } - // ========================================================================================================================================================== // - // ======================================== DG Jacobian ========================================================= // - // ========================================================================================================================================================== // - - typedef Eigen::Triplet T; + virtual int writeSecondaryCoordinates(double* coords) const + { + return 0; + } + virtual int writeParticleCoordinates(unsigned int parType, double* coords) const + { + return 0; + } + + protected: + const Discretization& _disc; + const Indexer _idx; + const LumpedRateModelWithoutPoresDG& _model; + double const* const _data; + }; + + /** + * @brief sets the current section index and section dependend velocity, dispersion + */ + void updateSection(int secIdx) + { - /** - * @brief sets the sparsity pattern of the static Jacobian - * @detail DG ConvDisp pattern and isotherm pattern. Independent of the isotherm, - all liquid and solid entries at a discrete point are set. - * @param [in] stateDer bool if state derivative pattern should be added (for _jacDisc) - */ - void setPattern(Eigen::SparseMatrix& mat, bool stateDer, bool has_reaction) { + if (_disc.curSection != secIdx) + { + _disc.curSection = secIdx; + _disc.newStaticJac = true; + } + } + + // ========================================================================================================================================================== + // // + // ======================================== DG Jacobian + // ========================================================= // + // ========================================================================================================================================================== + // // + + typedef Eigen::Triplet T; + + /** + * @brief sets the sparsity pattern of the static Jacobian + * @detail DG ConvDisp pattern and isotherm pattern. Independent of the isotherm, + all liquid and solid entries at a discrete point are set. + * @param [in] stateDer bool if state derivative pattern should be added (for _jacDisc) + */ + void setPattern(Eigen::SparseMatrix& mat, bool stateDer, bool has_reaction) + { - std::vector tripletList; + std::vector tripletList; - unsigned int isotherm_entries = _disc.nPoints * _disc.strideBound * (_disc.strideBound + _disc.nComp); - unsigned int reaction_entries = has_reaction ? _disc.nPoints * _disc.nComp * (_disc.strideBound + _disc.nComp) : 0; + unsigned int isotherm_entries = _disc.nPoints * _disc.strideBound * (_disc.strideBound + _disc.nComp); + unsigned int reaction_entries = + has_reaction ? _disc.nPoints * _disc.nComp * (_disc.strideBound + _disc.nComp) : 0; - tripletList.reserve(_convDispOp.nConvDispEntries(false) + isotherm_entries + reaction_entries); + tripletList.reserve(_convDispOp.nConvDispEntries(false) + isotherm_entries + reaction_entries); - _convDispOp.convDispJacPattern(tripletList); + _convDispOp.convDispJacPattern(tripletList); - bindingAndReactionPattern(tripletList, has_reaction); + bindingAndReactionPattern(tripletList, has_reaction); - if (stateDer) - stateDerPattern(tripletList); // only adds [ d convDisp / d q_t ] because main diagonal is already included ! + if (stateDer) + stateDerPattern( + tripletList); // only adds [ d convDisp / d q_t ] because main diagonal is already included ! - mat.setFromTriplets(tripletList.begin(), tripletList.end()); + mat.setFromTriplets(tripletList.begin(), tripletList.end()); + } - } + /** + * @brief computes the convection dispersion part of the state derivative pattern of the jacobian. + * @detail the main also diagonal belongs to the state derivative pattern but is not set here, so it should be set + * previously. + */ + void stateDerPattern(std::vector& tripletList) + { - /** - * @brief computes the convection dispersion part of the state derivative pattern of the jacobian. - * @detail the main also diagonal belongs to the state derivative pattern but is not set here, so it should be set previously. - */ - void stateDerPattern(std::vector& tripletList) { - - Indexer idxr(_disc); - unsigned int offC = 0; // inlet DOFs not included in Jacobian - - for (unsigned int point = 0; point < _disc.nPoints; point++) { - for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - if (_disc.nBound[comp]) // either one or null - // row: jump over inlet, go node and comp strides to find all liquid states - // column: jump over inlet and liquid states, add component offset and go node strides to find corresponding solid states - tripletList.push_back(T(offC + point * idxr.strideColNode() + comp * idxr.strideColComp(), - offC + idxr.strideColLiquid() + idxr.offsetBoundComp(comp) + point * idxr.strideColNode(), - 0.0)); - } - } + Indexer idxr(_disc); + unsigned int offC = 0; // inlet DOFs not included in Jacobian + for (unsigned int point = 0; point < _disc.nPoints; point++) + { + for (unsigned int comp = 0; comp < _disc.nComp; comp++) + { + if (_disc.nBound[comp]) // either one or null + // row: jump over inlet, go node and comp strides to find all liquid states + // column: jump over inlet and liquid states, add component offset and go node strides to find + // corresponding solid states + tripletList.push_back( + T(offC + point * idxr.strideColNode() + comp * idxr.strideColComp(), + offC + idxr.strideColLiquid() + idxr.offsetBoundComp(comp) + point * idxr.strideColNode(), + 0.0)); } - /** - * @brief sets the sparsity pattern of the isotherm and reaction Jacobian - * @detail Independent of the isotherm, all liquid and solid entries (so all entries, the isotherm could theoretically depend on) at a discrete point are set. - */ - void bindingAndReactionPattern(std::vector& tripletList, bool has_reaction) { - - Indexer idxr(_disc); - int offC = 0; // inlet DOFs not included in Jacobian - - // isotherm pattern: loop over all discrete points and solid states and add all liquid plus solid entries at that solid state at that discrete point - for (unsigned int point = 0; point < _disc.nPoints; point++) { - for (unsigned int solid = 0; solid < _disc.strideBound; solid++) { - for (unsigned int conc = 0; conc < _disc.nComp + _disc.strideBound; conc++) { - // row: jump over inlet and previous discrete points, liquid concentration and add the offset of the current bound state - // column: jump over inlet and previous discrete points. add entries for all liquid and bound concentrations (conc) - tripletList.push_back(T(offC + idxr.strideColNode() * point + idxr.strideColLiquid() + solid, - offC + idxr.strideColNode() * point + conc, - 0.0)); - } - } - } + } + } + /** + * @brief sets the sparsity pattern of the isotherm and reaction Jacobian + * @detail Independent of the isotherm, all liquid and solid entries (so all entries, the isotherm could + * theoretically depend on) at a discrete point are set. + */ + void bindingAndReactionPattern(std::vector& tripletList, bool has_reaction) + { - // reaction pattern: loop over all discrete points and liquid states and add all liquid plus solid entries at that liquid state at that discrete point - if (has_reaction) { - for (unsigned int point = 0; point < _disc.nPoints; point++) { - for (unsigned int liquid = 0; liquid < _disc.nComp; liquid++) { - for (unsigned int conc = 0; conc < _disc.nComp + _disc.strideBound; conc++) { - // row: jump over inlet and previous discrete points, liquid concentration and add the offset of the current bound state - // column: jump over inlet and previous discrete points. add entries for all liquid and bound concentrations (conc) - tripletList.push_back(T(offC + idxr.strideColNode() * point + liquid, - offC + idxr.strideColNode() * point + conc, - 0.0)); - } - } - } - } - } - /** - * @brief computes the jacobian via finite differences (testing purpose) - */ - MatrixXd calcFDJacobian(const double* y_, const double* yDot_, const SimulationTime simTime, util::ThreadLocalStorage& threadLocalMem, double alpha) { - - ////// create solution vectors - Eigen::Map hmpf(y_, numDofs()); - VectorXd y = hmpf; - VectorXd yDot; - if (yDot_) { - Eigen::Map hmpf2(yDot_, numDofs()); - yDot = hmpf2; - - //// set to LWE initial conditions - //Indexer idxr(_disc); - //for(unsigned int blk=0;blk<_disc.nPoints;blk++){ - // y[idxr.offsetC() + blk * idxr.strideColNode()] = 50.0; - // y[idxr.offsetC() + blk * idxr.strideColNode() + idxr.strideColLiquid()] = 1200.0; - //} - //yDot.setZero(); - } - else { - return MatrixXd::Zero(numDofs(), numDofs()); - } - //VectorXd y = VectorXd::Zero(numDofs()); - //VectorXd yDot = VectorXd::Zero(numDofs()); - - VectorXd res = VectorXd::Zero(numDofs()); - const double* yPtr = &y[0]; - const double* yDotPtr = &yDot[0]; - double* resPtr = &res[0]; - // create FD jacobian - MatrixXd Jacobian = MatrixXd::Zero(numDofs(), numDofs()); - // set FD step - double epsilon = 0.01; - - residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, threadLocalMem); - - for (int col = 0; col < Jacobian.cols(); col++) { - Jacobian.col(col) = -(1.0 + alpha) * res; - } - /* Residual(y+h) */ - // state DOFs - for (int dof = 0; dof < Jacobian.cols(); dof++) { - y[dof] += epsilon; - residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, threadLocalMem); - y[dof] -= epsilon; - Jacobian.col(dof) += res; - } + Indexer idxr(_disc); + int offC = 0; // inlet DOFs not included in Jacobian - // state derivative Jacobian - for (int dof = 0; dof < Jacobian.cols(); dof++) { - yDot[dof] += epsilon; - residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, threadLocalMem); - yDot[dof] -= epsilon; - Jacobian.col(dof) += alpha * res; + // isotherm pattern: loop over all discrete points and solid states and add all liquid plus solid entries at + // that solid state at that discrete point + for (unsigned int point = 0; point < _disc.nPoints; point++) + { + for (unsigned int solid = 0; solid < _disc.strideBound; solid++) + { + for (unsigned int conc = 0; conc < _disc.nComp + _disc.strideBound; conc++) + { + // row: jump over inlet and previous discrete points, liquid concentration and add the offset + // of the current bound state column: jump over inlet and previous discrete points. add entries + // for all liquid and bound concentrations (conc) + tripletList.push_back(T(offC + idxr.strideColNode() * point + idxr.strideColLiquid() + solid, + offC + idxr.strideColNode() * point + conc, 0.0)); } + } + } - ////* exterminate numerical noise and divide by epsilon*/ - for (int i = 0; i < Jacobian.rows(); i++) { - for (int j = 0; j < Jacobian.cols(); j++) { - if (std::abs(Jacobian(i, j)) < 1e-10) Jacobian(i, j) = 0.0; + // reaction pattern: loop over all discrete points and liquid states and add all liquid plus solid entries at + // that liquid state at that discrete point + if (has_reaction) + { + for (unsigned int point = 0; point < _disc.nPoints; point++) + { + for (unsigned int liquid = 0; liquid < _disc.nComp; liquid++) + { + for (unsigned int conc = 0; conc < _disc.nComp + _disc.strideBound; conc++) + { + // row: jump over inlet and previous discrete points, liquid concentration and add the + // offset of the current bound state column: jump over inlet and previous discrete points. + // add entries for all liquid and bound concentrations (conc) + tripletList.push_back(T(offC + idxr.strideColNode() * point + liquid, + offC + idxr.strideColNode() * point + conc, 0.0)); } } - Jacobian /= epsilon; + } + } + } + /** + * @brief computes the jacobian via finite differences (testing purpose) + */ + MatrixXd calcFDJacobian(const double* y_, const double* yDot_, const SimulationTime simTime, + util::ThreadLocalStorage& threadLocalMem, double alpha) + { - return Jacobian; + ////// create solution vectors + Eigen::Map hmpf(y_, numDofs()); + VectorXd y = hmpf; + VectorXd yDot; + if (yDot_) + { + Eigen::Map hmpf2(yDot_, numDofs()); + yDot = hmpf2; + + //// set to LWE initial conditions + // Indexer idxr(_disc); + // for(unsigned int blk=0;blk<_disc.nPoints;blk++){ + // y[idxr.offsetC() + blk * idxr.strideColNode()] = 50.0; + // y[idxr.offsetC() + blk * idxr.strideColNode() + idxr.strideColLiquid()] = 1200.0; + // } + // yDot.setZero(); + } + else + { + return MatrixXd::Zero(numDofs(), numDofs()); + } + // VectorXd y = VectorXd::Zero(numDofs()); + // VectorXd yDot = VectorXd::Zero(numDofs()); + + VectorXd res = VectorXd::Zero(numDofs()); + const double* yPtr = &y[0]; + const double* yDotPtr = &yDot[0]; + double* resPtr = &res[0]; + // create FD jacobian + MatrixXd Jacobian = MatrixXd::Zero(numDofs(), numDofs()); + // set FD step + double epsilon = 0.01; + + residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, threadLocalMem); + + for (int col = 0; col < Jacobian.cols(); col++) + { + Jacobian.col(col) = -(1.0 + alpha) * res; + } + /* Residual(y+h) */ + // state DOFs + for (int dof = 0; dof < Jacobian.cols(); dof++) + { + y[dof] += epsilon; + residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, + threadLocalMem); + y[dof] -= epsilon; + Jacobian.col(dof) += res; + } + + // state derivative Jacobian + for (int dof = 0; dof < Jacobian.cols(); dof++) + { + yDot[dof] += epsilon; + residualImpl(simTime.t, simTime.secIdx, yPtr, yDotPtr, resPtr, + threadLocalMem); + yDot[dof] -= epsilon; + Jacobian.col(dof) += alpha * res; + } + + ////* exterminate numerical noise and divide by epsilon*/ + for (int i = 0; i < Jacobian.rows(); i++) + { + for (int j = 0; j < Jacobian.cols(); j++) + { + if (std::abs(Jacobian(i, j)) < 1e-10) + Jacobian(i, j) = 0.0; } + } + Jacobian /= epsilon; - }; + return Jacobian; + } +}; - } // namespace model +} // namespace model } // namespace cadet -#endif // LIBCADET_LUMPEDRATEMODELWITHOUTPORESDG_HPP_ +#endif // LIBCADET_LUMPEDRATEMODELWITHOUTPORESDG_HPP_ diff --git a/src/libcadet/model/ModelSystemImpl-Helper.hpp b/src/libcadet/model/ModelSystemImpl-Helper.hpp index 131c9d90b..8e69c84a7 100644 --- a/src/libcadet/model/ModelSystemImpl-Helper.hpp +++ b/src/libcadet/model/ModelSystemImpl-Helper.hpp @@ -11,112 +11,114 @@ // ============================================================================= /** - * @file + * @file * Defines helper functions used by all ModelSystemImpl-xyz.cpp files. */ namespace { - /** - * @brief Computes a total return code from a list of separate return codes - * @details A negative return code indicates a non-recoverable error. Positive - * values indicate recoverable errors and a value of @c 0 indicates - * no error. - * @param [in] err List of error codes to be fused into one - * @return Total error code summarizing all codes in the list - */ - inline int totalErrorIndicatorFromLocal(const std::vector& err) +/** + * @brief Computes a total return code from a list of separate return codes + * @details A negative return code indicates a non-recoverable error. Positive + * values indicate recoverable errors and a value of @c 0 indicates + * no error. + * @param [in] err List of error codes to be fused into one + * @return Total error code summarizing all codes in the list + */ +inline int totalErrorIndicatorFromLocal(const std::vector& err) +{ + int totalError = 0; + for (std::size_t i = 0; i < err.size(); ++i) { - int totalError = 0; - for (std::size_t i = 0; i < err.size(); ++i) - { - // Negative values are non-recoverable errors - if (err[i] < 0) - return err[i]; + // Negative values are non-recoverable errors + if (err[i] < 0) + return err[i]; - // 0 = okay, positive values = recoverable error - totalError = std::max(totalError, err[i]); - } - return totalError; - } - - /** - * @brief Fuses two error codes into one - * @details A negative return code indicates a non-recoverable error. Positive - * values indicate recoverable errors and a value of @c 0 indicates - * no error. - * @param [in] curCode Current error code - * @param [in] nextCode Next error code that is fused into the current one - * @return Fused error code summarizing both inputs - */ - inline int updateErrorIndicator(int curCode, int nextCode) - { - if ((curCode < 0) || (nextCode < 0)) - return std::min(curCode, nextCode); - return std::max(curCode, nextCode); + // 0 = okay, positive values = recoverable error + totalError = std::max(totalError, err[i]); } + return totalError; +} - template - inline state_t applyOffset(const state_t& state, unsigned int offset) - { - return state_t{ - state.vecStateY + offset, - (state.vecStateYdot) ? (state.vecStateYdot + offset) : nullptr - }; - } +/** + * @brief Fuses two error codes into one + * @details A negative return code indicates a non-recoverable error. Positive + * values indicate recoverable errors and a value of @c 0 indicates + * no error. + * @param [in] curCode Current error code + * @param [in] nextCode Next error code that is fused into the current one + * @return Fused error code summarizing both inputs + */ +inline int updateErrorIndicator(int curCode, int nextCode) +{ + if ((curCode < 0) || (nextCode < 0)) + return std::min(curCode, nextCode); + return std::max(curCode, nextCode); +} - template <> - inline cadet::AdJacobianParams applyOffset(const cadet::AdJacobianParams& adJac, unsigned int offset) - { - return cadet::AdJacobianParams{ - (adJac.adRes) ? (adJac.adRes + offset) : nullptr, - (adJac.adY) ? (adJac.adY + offset) : nullptr, - adJac.adDirOffset - }; - } +template inline state_t applyOffset(const state_t& state, unsigned int offset) +{ + return state_t{state.vecStateY + offset, (state.vecStateYdot) ? (state.vecStateYdot + offset) : nullptr}; +} - template - inline res_t cubicPoly(const cadet::active& constCoeff, const cadet::active& linCoeff, const cadet::active& quadCoeff, const cadet::active& cubCoeff, double v) - { - return static_cast(constCoeff) + v * (static_cast(linCoeff) + v * (static_cast(quadCoeff) + v * static_cast(cubCoeff))); - } +template <> inline cadet::AdJacobianParams applyOffset(const cadet::AdJacobianParams& adJac, unsigned int offset) +{ + return cadet::AdJacobianParams{(adJac.adRes) ? (adJac.adRes + offset) : nullptr, + (adJac.adY) ? (adJac.adY + offset) : nullptr, adJac.adDirOffset}; +} - template - inline res_t cubicPoly(cadet::active const* constCoeff, cadet::active const* linCoeff, cadet::active const* quadCoeff, cadet::active const* cubCoeff, unsigned int idx, double v) - { - return cubicPoly(constCoeff[idx], linCoeff[idx], quadCoeff[idx], cubCoeff[idx], v); - } +template +inline res_t cubicPoly(const cadet::active& constCoeff, const cadet::active& linCoeff, const cadet::active& quadCoeff, + const cadet::active& cubCoeff, double v) +{ + return static_cast(constCoeff) + + v * (static_cast(linCoeff) + v * (static_cast(quadCoeff) + v * static_cast(cubCoeff))); +} - template - inline res_t cubicPolyDeriv(const cadet::active& linCoeff, const cadet::active& quadCoeff, const cadet::active& cubCoeff, double v) - { - return static_cast(linCoeff) + v * (2.0 * static_cast(quadCoeff) + 3.0 * v * static_cast(cubCoeff)); - } +template +inline res_t cubicPoly(cadet::active const* constCoeff, cadet::active const* linCoeff, cadet::active const* quadCoeff, + cadet::active const* cubCoeff, unsigned int idx, double v) +{ + return cubicPoly(constCoeff[idx], linCoeff[idx], quadCoeff[idx], cubCoeff[idx], v); +} - template - inline res_t cubicPolyDeriv(cadet::active const* linCoeff, cadet::active const* quadCoeff, cadet::active const* cubCoeff, unsigned int idx, double v) - { - return cubicPolyDeriv(linCoeff[idx], quadCoeff[idx], cubCoeff[idx], v); - } +template +inline res_t cubicPolyDeriv(const cadet::active& linCoeff, const cadet::active& quadCoeff, + const cadet::active& cubCoeff, double v) +{ + return static_cast(linCoeff) + + v * (2.0 * static_cast(quadCoeff) + 3.0 * v * static_cast(cubCoeff)); +} +template +inline res_t cubicPolyDeriv(cadet::active const* linCoeff, cadet::active const* quadCoeff, + cadet::active const* cubCoeff, unsigned int idx, double v) +{ + return cubicPolyDeriv(linCoeff[idx], quadCoeff[idx], cubCoeff[idx], v); +} - inline double cubicPoly(const cadet::active& constCoeff, const cadet::active& linCoeff, const cadet::active& quadCoeff, const cadet::active& cubCoeff, double v, unsigned int adDir) - { - return constCoeff.getADValue(adDir) + v * (linCoeff.getADValue(adDir) + v * (quadCoeff.getADValue(adDir) + v * cubCoeff.getADValue(adDir))); - } +inline double cubicPoly(const cadet::active& constCoeff, const cadet::active& linCoeff, const cadet::active& quadCoeff, + const cadet::active& cubCoeff, double v, unsigned int adDir) +{ + return constCoeff.getADValue(adDir) + + v * (linCoeff.getADValue(adDir) + v * (quadCoeff.getADValue(adDir) + v * cubCoeff.getADValue(adDir))); +} - inline double cubicPoly(cadet::active const* constCoeff, cadet::active const* linCoeff, cadet::active const* quadCoeff, cadet::active const* cubCoeff, unsigned int idx, double v, unsigned int adDir) - { - return cubicPoly(constCoeff[idx], linCoeff[idx], quadCoeff[idx], cubCoeff[idx], v, adDir); - } +inline double cubicPoly(cadet::active const* constCoeff, cadet::active const* linCoeff, cadet::active const* quadCoeff, + cadet::active const* cubCoeff, unsigned int idx, double v, unsigned int adDir) +{ + return cubicPoly(constCoeff[idx], linCoeff[idx], quadCoeff[idx], cubCoeff[idx], v, adDir); +} - inline double cubicPolyDeriv(const cadet::active& linCoeff, const cadet::active& quadCoeff, const cadet::active& cubCoeff, double v, unsigned int adDir) - { - return linCoeff.getADValue(adDir) + v * (2.0 * quadCoeff.getADValue(adDir) + 3.0 * v * cubCoeff.getADValue(adDir)); - } +inline double cubicPolyDeriv(const cadet::active& linCoeff, const cadet::active& quadCoeff, + const cadet::active& cubCoeff, double v, unsigned int adDir) +{ + return linCoeff.getADValue(adDir) + v * (2.0 * quadCoeff.getADValue(adDir) + 3.0 * v * cubCoeff.getADValue(adDir)); +} - inline double cubicPolyDeriv(cadet::active const* linCoeff, cadet::active const* quadCoeff, cadet::active const* cubCoeff, unsigned int idx, double v, unsigned int adDir) - { - return cubicPolyDeriv(linCoeff[idx], quadCoeff[idx], cubCoeff[idx], v, adDir); - } +inline double cubicPolyDeriv(cadet::active const* linCoeff, cadet::active const* quadCoeff, + cadet::active const* cubCoeff, unsigned int idx, double v, unsigned int adDir) +{ + return cubicPolyDeriv(linCoeff[idx], quadCoeff[idx], cubCoeff[idx], v, adDir); } +} // namespace diff --git a/src/libcadet/model/ModelSystemImpl-InitialConditions.cpp b/src/libcadet/model/ModelSystemImpl-InitialConditions.cpp index b6d958ce8..85d834dd7 100644 --- a/src/libcadet/model/ModelSystemImpl-InitialConditions.cpp +++ b/src/libcadet/model/ModelSystemImpl-InitialConditions.cpp @@ -23,71 +23,91 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include +#include #endif #include "model/ModelSystemImpl-Helper.hpp" namespace { - struct FullTag {}; - struct LeanTag {}; +struct FullTag +{ +}; +struct LeanTag +{ +}; - template - struct ConsistentInit {}; +template struct ConsistentInit +{ +}; - template <> - struct ConsistentInit +template <> struct ConsistentInit +{ + static inline void state(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, + double* const vecStateY, const cadet::AdJacobianParams& adJac, double errorTol, + cadet::util::ThreadLocalStorage& threadLocalMem) { - static inline void state(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, double* const vecStateY, const cadet::AdJacobianParams& adJac, double errorTol, cadet::util::ThreadLocalStorage& threadLocalMem) - { - model->consistentInitialState(simTime, vecStateY, adJac, errorTol, threadLocalMem); - } + model->consistentInitialState(simTime, vecStateY, adJac, errorTol, threadLocalMem); + } - static inline void timeDerivative(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, double* const res, cadet::util::ThreadLocalStorage& threadLocalMem) - { - model->consistentInitialTimeDerivative(simTime, vecStateY, vecStateYdot, threadLocalMem); - } + static inline void timeDerivative(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, + double const* vecStateY, double* const vecStateYdot, double* const res, + cadet::util::ThreadLocalStorage& threadLocalMem) + { + model->consistentInitialTimeDerivative(simTime, vecStateY, vecStateYdot, threadLocalMem); + } - static inline int residualWithJacobian(cadet::model::ModelSystem& ms, const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, double* const res, double* const temp, - const cadet::AdJacobianParams& adJac) - { - return ms.residualWithJacobian(simTime, simState, res, adJac); - } + static inline int residualWithJacobian(cadet::model::ModelSystem& ms, const cadet::SimulationTime& simTime, + const cadet::ConstSimulationState& simState, double* const res, + double* const temp, const cadet::AdJacobianParams& adJac) + { + return ms.residualWithJacobian(simTime, simState, res, adJac); + } - static inline void parameterSensitivity(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, - std::vector& vecSensYlocal, std::vector& vecSensYdotLocal, cadet::active const* const adRes, cadet::util::ThreadLocalStorage& threadLocalMem) - { - model->consistentInitialSensitivity(simTime, simState, vecSensYlocal, vecSensYdotLocal, adRes, threadLocalMem); - } - }; + static inline void parameterSensitivity(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, + const cadet::ConstSimulationState& simState, + std::vector& vecSensYlocal, std::vector& vecSensYdotLocal, + cadet::active const* const adRes, + cadet::util::ThreadLocalStorage& threadLocalMem) + { + model->consistentInitialSensitivity(simTime, simState, vecSensYlocal, vecSensYdotLocal, adRes, threadLocalMem); + } +}; - template <> - struct ConsistentInit +template <> struct ConsistentInit +{ + static inline void state(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, + double* const vecStateY, const cadet::AdJacobianParams& adJac, double errorTol, + cadet::util::ThreadLocalStorage& threadLocalMem) { - static inline void state(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, double* const vecStateY, const cadet::AdJacobianParams& adJac, double errorTol, cadet::util::ThreadLocalStorage& threadLocalMem) - { - model->leanConsistentInitialState(simTime, vecStateY, adJac, errorTol, threadLocalMem); - } + model->leanConsistentInitialState(simTime, vecStateY, adJac, errorTol, threadLocalMem); + } - static inline void timeDerivative(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, double* const res, cadet::util::ThreadLocalStorage& threadLocalMem) - { - model->leanConsistentInitialTimeDerivative(simTime.t, vecStateY, vecStateYdot, res, threadLocalMem); - } + static inline void timeDerivative(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, + double const* vecStateY, double* const vecStateYdot, double* const res, + cadet::util::ThreadLocalStorage& threadLocalMem) + { + model->leanConsistentInitialTimeDerivative(simTime.t, vecStateY, vecStateYdot, res, threadLocalMem); + } - static inline int residualWithJacobian(cadet::model::ModelSystem& ms, const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, double* const res, double* const temp, - const cadet::AdJacobianParams& adJac) - { - return ms.residualWithJacobian(simTime, simState, temp, adJac); - } + static inline int residualWithJacobian(cadet::model::ModelSystem& ms, const cadet::SimulationTime& simTime, + const cadet::ConstSimulationState& simState, double* const res, + double* const temp, const cadet::AdJacobianParams& adJac) + { + return ms.residualWithJacobian(simTime, simState, temp, adJac); + } - static inline void parameterSensitivity(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, - std::vector& vecSensYlocal, std::vector& vecSensYdotLocal, cadet::active const* const adRes, cadet::util::ThreadLocalStorage& threadLocalMem) - { - model->leanConsistentInitialSensitivity(simTime, simState, vecSensYlocal, vecSensYdotLocal, adRes, threadLocalMem); - } - }; -} + static inline void parameterSensitivity(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, + const cadet::ConstSimulationState& simState, + std::vector& vecSensYlocal, std::vector& vecSensYdotLocal, + cadet::active const* const adRes, + cadet::util::ThreadLocalStorage& threadLocalMem) + { + model->leanConsistentInitialSensitivity(simTime, simState, vecSensYlocal, vecSensYdotLocal, adRes, + threadLocalMem); + } +}; +} // namespace namespace cadet { @@ -96,7 +116,7 @@ namespace model { int ModelSystem::dResDpFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, - const AdJacobianParams& adJac) + const AdJacobianParams& adJac) { BENCH_SCOPE(_timerResidualSens); @@ -112,7 +132,7 @@ int ModelSystem::dResDpFwdWithJacobian(const SimulationTime& simTime, const Cons const unsigned int offset = _dofOffset[i]; _errorIndicator[i] = m->residualSensFwdWithJacobian(simTime, applyOffset(simState, offset), - applyOffset(adJac, offset), _threadLocalStorage); + applyOffset(adJac, offset), _threadLocalStorage); } CADET_PARFOR_END; @@ -159,7 +179,8 @@ void ModelSystem::readInitialCondition(IParameterProvider& paramProvider) IUnitOperation* const m = _models[i]; oss.str(""); - oss << "unit_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << static_cast(m->unitOperationId()); + oss << "unit_" << std::setfill('0') << std::setw(3) << std::setprecision(0) + << static_cast(m->unitOperationId()); const std::string subScope = oss.str(); if (paramProvider.exists(subScope)) @@ -217,7 +238,7 @@ void ModelSystem::solveCouplingDOF(double* const vec) const unsigned int localStride = m->localInletComponentStride(port); for (unsigned int comp = 0; comp < m->numComponents(); ++comp) { - vec[offset + localIndex + comp*localStride] = vec[idxCoupling]; + vec[offset + localIndex + comp * localStride] = vec[idxCoupling]; ++idxCoupling; } } @@ -237,18 +258,19 @@ void ModelSystem::subtractDresConDt(double t, double* dResConDt, double const* v for (unsigned int i = 0; i < _connections.sliceSize(_curSwitchIndex) / 6; ++i) { // Extract current connection - const int uoSource = ptrConn[6*i]; - const int uoDest = ptrConn[6*i + 1]; - const int portSource = ptrConn[6*i + 2]; - const int portDest = ptrConn[6*i + 3]; - const int compSource = ptrConn[6*i + 4]; - const int compDest = ptrConn[6*i + 5]; + const int uoSource = ptrConn[6 * i]; + const int uoDest = ptrConn[6 * i + 1]; + const int portSource = ptrConn[6 * i + 2]; + const int portDest = ptrConn[6 * i + 3]; + const int compSource = ptrConn[6 * i + 4]; + const int compDest = ptrConn[6 * i + 5]; // Obtain index of first connection from uoSource to uoDest unsigned int idx = i; for (unsigned int j = 0; j < i; ++j) { - if ((ptrConn[6*j] == uoSource) && (ptrConn[6*j + 1] == uoDest) && (ptrConn[6*j + 2] == portSource) && (ptrConn[6*j + 3] == portDest)) + if ((ptrConn[6 * j] == uoSource) && (ptrConn[6 * j + 1] == uoDest) && (ptrConn[6 * j + 2] == portSource) && + (ptrConn[6 * j + 3] == portDest)) { idx = j; break; @@ -267,7 +289,9 @@ void ModelSystem::subtractDresConDt(double t, double* dResConDt, double const* v { for (unsigned int j = 0; j < modelSource->numOutletPorts(); ++j) { - const double totInFlow = cubicPoly(_totalInletFlow(uoDest, j), _totalInletFlowLin(uoDest, j), _totalInletFlowQuad(uoDest, j), _totalInletFlowCub(uoDest, j), secT); + const double totInFlow = + cubicPoly(_totalInletFlow(uoDest, j), _totalInletFlowLin(uoDest, j), + _totalInletFlowQuad(uoDest, j), _totalInletFlowCub(uoDest, j), secT); // Ignore ports with incoming flow rate 0 if (totInFlow <= 0.0) @@ -276,7 +300,8 @@ void ModelSystem::subtractDresConDt(double t, double* dResConDt, double const* v const unsigned int outletIndex = modelSource->localOutletComponentIndex(j); const unsigned int outletStride = modelSource->localOutletComponentStride(j); - const double totInFlowOverDt = cubicPolyDeriv(_totalInletFlowLin(uoDest, j), _totalInletFlowQuad(uoDest, j), _totalInletFlowCub(uoDest, j), secT); + const double totInFlowOverDt = cubicPolyDeriv( + _totalInletFlowLin(uoDest, j), _totalInletFlowQuad(uoDest, j), _totalInletFlowCub(uoDest, j), secT); const double inFlow = cubicPoly(ptrRate, ptrRateLin, ptrRateQuad, ptrRateCub, idx, secT); const double inFlowOverDt = cubicPolyDeriv(ptrRateLin, ptrRateQuad, ptrRateCub, idx, secT); @@ -288,14 +313,16 @@ void ModelSystem::subtractDresConDt(double t, double* dResConDt, double const* v // Connect all components with the same flow rate for (unsigned int comp = 0; comp < modelSource->numComponents(); ++comp) { - const unsigned int row = _couplingIdxMap[std::make_tuple(uoDest, j, comp)]; // destination coupling DOF + const unsigned int row = + _couplingIdxMap[std::make_tuple(uoDest, j, comp)]; // destination coupling DOF const unsigned int col = outletIndex + outletStride * comp; dResConDt[row] += dvdt * vecStateY[offset + col]; } } else { - const unsigned int row = _couplingIdxMap[std::make_tuple(uoDest, j, compDest)]; // destination coupling DOF + const unsigned int row = + _couplingIdxMap[std::make_tuple(uoDest, j, compDest)]; // destination coupling DOF const unsigned int col = outletIndex + outletStride * compSource; dResConDt[row] += dvdt * vecStateY[offset + col]; } @@ -303,7 +330,9 @@ void ModelSystem::subtractDresConDt(double t, double* dResConDt, double const* v } else { - const double totInFlow = cubicPoly(_totalInletFlow(uoDest, portDest), _totalInletFlowLin(uoDest, portDest), _totalInletFlowQuad(uoDest, portDest), _totalInletFlowCub(uoDest, portDest), secT); + const double totInFlow = + cubicPoly(_totalInletFlow(uoDest, portDest), _totalInletFlowLin(uoDest, portDest), + _totalInletFlowQuad(uoDest, portDest), _totalInletFlowCub(uoDest, portDest), secT); // Ignore ports with incoming flow rate 0 if (totInFlow <= 0.0) @@ -312,7 +341,9 @@ void ModelSystem::subtractDresConDt(double t, double* dResConDt, double const* v const unsigned int outletIndex = modelSource->localOutletComponentIndex(portSource); const unsigned int outletStride = modelSource->localOutletComponentStride(portSource); - const double totInFlowOverDt = cubicPolyDeriv(_totalInletFlowLin(uoDest, portDest), _totalInletFlowQuad(uoDest, portDest), _totalInletFlowCub(uoDest, portDest), secT); + const double totInFlowOverDt = + cubicPolyDeriv(_totalInletFlowLin(uoDest, portDest), _totalInletFlowQuad(uoDest, portDest), + _totalInletFlowCub(uoDest, portDest), secT); const double inFlow = cubicPoly(ptrRate, ptrRateLin, ptrRateQuad, ptrRateCub, idx, secT); const double inFlowOverDt = cubicPolyDeriv(ptrRateLin, ptrRateQuad, ptrRateCub, idx, secT); @@ -324,14 +355,16 @@ void ModelSystem::subtractDresConDt(double t, double* dResConDt, double const* v // Connect all components with the same flow rate for (unsigned int comp = 0; comp < modelSource->numComponents(); ++comp) { - const unsigned int row = _couplingIdxMap[std::make_tuple(uoDest, portDest, comp)]; // destination coupling DOF + const unsigned int row = + _couplingIdxMap[std::make_tuple(uoDest, portDest, comp)]; // destination coupling DOF const unsigned int col = outletIndex + outletStride * comp; dResConDt[row] += dvdt * vecStateY[offset + col]; } } else { - const unsigned int row = _couplingIdxMap[std::make_tuple(uoDest, portDest, compDest)]; // destination coupling DOF + const unsigned int row = + _couplingIdxMap[std::make_tuple(uoDest, portDest, compDest)]; // destination coupling DOF const unsigned int col = outletIndex + outletStride * compSource; dResConDt[row] += dvdt * vecStateY[offset + col]; } @@ -352,18 +385,19 @@ void ModelSystem::subtractDresConDtDp(double t, unsigned int adDir, double* dRes for (unsigned int i = 0; i < _connections.sliceSize(_curSwitchIndex) / 6; ++i) { // Extract current connection - const int uoSource = ptrConn[6*i]; - const int uoDest = ptrConn[6*i + 1]; - const int portSource = ptrConn[6*i + 2]; - const int portDest = ptrConn[6*i + 3]; - const int compSource = ptrConn[6*i + 4]; - const int compDest = ptrConn[6*i + 5]; + const int uoSource = ptrConn[6 * i]; + const int uoDest = ptrConn[6 * i + 1]; + const int portSource = ptrConn[6 * i + 2]; + const int portDest = ptrConn[6 * i + 3]; + const int compSource = ptrConn[6 * i + 4]; + const int compDest = ptrConn[6 * i + 5]; // Obtain index of first connection from uoSource to uoDest unsigned int idx = i; for (unsigned int j = 0; j < i; ++j) { - if ((ptrConn[6*j] == uoSource) && (ptrConn[6*j + 1] == uoDest) && (ptrConn[6*j + 2] == portSource) && (ptrConn[6*j + 3] == portDest)) + if ((ptrConn[6 * j] == uoSource) && (ptrConn[6 * j + 1] == uoDest) && (ptrConn[6 * j + 2] == portSource) && + (ptrConn[6 * j + 3] == portDest)) { idx = j; break; @@ -382,7 +416,9 @@ void ModelSystem::subtractDresConDtDp(double t, unsigned int adDir, double* dRes { for (unsigned int j = 0; j < modelSource->numOutletPorts(); ++j) { - const double totInFlow = cubicPoly(_totalInletFlow(uoDest, j), _totalInletFlowLin(uoDest, j), _totalInletFlowQuad(uoDest, j), _totalInletFlowCub(uoDest, j), secT, adDir); + const double totInFlow = + cubicPoly(_totalInletFlow(uoDest, j), _totalInletFlowLin(uoDest, j), _totalInletFlowQuad(uoDest, j), + _totalInletFlowCub(uoDest, j), secT, adDir); // Ignore ports with incoming flow rate 0 if (totInFlow <= 0.0) @@ -391,7 +427,9 @@ void ModelSystem::subtractDresConDtDp(double t, unsigned int adDir, double* dRes const unsigned int outletIndex = modelSource->localOutletComponentIndex(j); const unsigned int outletStride = modelSource->localOutletComponentStride(j); - const double totInFlowOverDt = cubicPolyDeriv(_totalInletFlowLin(uoDest, j), _totalInletFlowQuad(uoDest, j), _totalInletFlowCub(uoDest, j), secT, adDir); + const double totInFlowOverDt = + cubicPolyDeriv(_totalInletFlowLin(uoDest, j), _totalInletFlowQuad(uoDest, j), + _totalInletFlowCub(uoDest, j), secT, adDir); const double inFlow = cubicPoly(ptrRate, ptrRateLin, ptrRateQuad, ptrRateCub, idx, secT, adDir); const double inFlowOverDt = cubicPolyDeriv(ptrRateLin, ptrRateQuad, ptrRateCub, idx, secT, adDir); @@ -403,14 +441,16 @@ void ModelSystem::subtractDresConDtDp(double t, unsigned int adDir, double* dRes // Connect all components with the same flow rate for (unsigned int comp = 0; comp < modelSource->numComponents(); ++comp) { - const unsigned int row = _couplingIdxMap[std::make_tuple(uoDest, j, comp)]; // destination coupling DOF + const unsigned int row = + _couplingIdxMap[std::make_tuple(uoDest, j, comp)]; // destination coupling DOF const unsigned int col = outletIndex + outletStride * comp; dResConDt[row] += dvdt * vecStateY[offset + col]; } } else { - const unsigned int row = _couplingIdxMap[std::make_tuple(uoDest, j, compDest)]; // destination coupling DOF + const unsigned int row = + _couplingIdxMap[std::make_tuple(uoDest, j, compDest)]; // destination coupling DOF const unsigned int col = outletIndex + outletStride * compSource; dResConDt[row] += dvdt * vecStateY[offset + col]; } @@ -418,7 +458,9 @@ void ModelSystem::subtractDresConDtDp(double t, unsigned int adDir, double* dRes } else { - const double totInFlow = cubicPoly(_totalInletFlow(uoDest, portDest), _totalInletFlowLin(uoDest, portDest), _totalInletFlowQuad(uoDest, portDest), _totalInletFlowCub(uoDest, portDest), secT, adDir); + const double totInFlow = + cubicPoly(_totalInletFlow(uoDest, portDest), _totalInletFlowLin(uoDest, portDest), + _totalInletFlowQuad(uoDest, portDest), _totalInletFlowCub(uoDest, portDest), secT, adDir); // Ignore ports with incoming flow rate 0 if (totInFlow <= 0.0) @@ -427,7 +469,9 @@ void ModelSystem::subtractDresConDtDp(double t, unsigned int adDir, double* dRes const unsigned int outletIndex = modelSource->localOutletComponentIndex(portSource); const unsigned int outletStride = modelSource->localOutletComponentStride(portSource); - const double totInFlowOverDt = cubicPolyDeriv(_totalInletFlowLin(uoDest, portDest), _totalInletFlowQuad(uoDest, portDest), _totalInletFlowCub(uoDest, portDest), secT, adDir); + const double totInFlowOverDt = + cubicPolyDeriv(_totalInletFlowLin(uoDest, portDest), _totalInletFlowQuad(uoDest, portDest), + _totalInletFlowCub(uoDest, portDest), secT, adDir); const double inFlow = cubicPoly(ptrRate, ptrRateLin, ptrRateQuad, ptrRateCub, idx, secT, adDir); const double inFlowOverDt = cubicPolyDeriv(ptrRateLin, ptrRateQuad, ptrRateCub, idx, secT, adDir); @@ -439,14 +483,16 @@ void ModelSystem::subtractDresConDtDp(double t, unsigned int adDir, double* dRes // Connect all components with the same flow rate for (unsigned int comp = 0; comp < modelSource->numComponents(); ++comp) { - const unsigned int row = _couplingIdxMap[std::make_tuple(uoDest, portDest, comp)]; // destination coupling DOF + const unsigned int row = + _couplingIdxMap[std::make_tuple(uoDest, portDest, comp)]; // destination coupling DOF const unsigned int col = outletIndex + outletStride * comp; dResConDt[row] += dvdt * vecStateY[offset + col]; } } else { - const unsigned int row = _couplingIdxMap[std::make_tuple(uoDest, portDest, compDest)]; // destination coupling DOF + const unsigned int row = + _couplingIdxMap[std::make_tuple(uoDest, portDest, compDest)]; // destination coupling DOF const unsigned int col = outletIndex + outletStride * compSource; dResConDt[row] += dvdt * vecStateY[offset + col]; } @@ -456,7 +502,7 @@ void ModelSystem::subtractDresConDtDp(double t, unsigned int adDir, double* dRes template void ModelSystem::consistentInitialConditionAlgorithm(const SimulationTime& simTime, const SimulationState& simState, - const AdJacobianParams& adJac, double errorTol) + const AdJacobianParams& adJac, double errorTol) { BENCH_SCOPE(_timerConsistentInit); @@ -473,7 +519,8 @@ void ModelSystem::consistentInitialConditionAlgorithm(const SimulationTime& simT const unsigned int offset = _dofOffset[i]; if (!m->hasInlet()) { - ConsistentInit::state(m, simTime, simState.vecStateY + offset, applyOffset(adJac, offset), errorTol, _threadLocalStorage); + ConsistentInit::state(m, simTime, simState.vecStateY + offset, applyOffset(adJac, offset), errorTol, + _threadLocalStorage); } } CADET_PARFOR_END; @@ -499,7 +546,8 @@ void ModelSystem::consistentInitialConditionAlgorithm(const SimulationTime& simT const unsigned int offset = _dofOffset[i]; if (m->hasInlet()) { - ConsistentInit::state(m, simTime, simState.vecStateY + offset, applyOffset(adJac, offset), errorTol, _threadLocalStorage); + ConsistentInit::state(m, simTime, simState.vecStateY + offset, applyOffset(adJac, offset), errorTol, + _threadLocalStorage); } } CADET_PARFOR_END; @@ -523,7 +571,8 @@ void ModelSystem::consistentInitialConditionAlgorithm(const SimulationTime& simT { IUnitOperation* const m = _models[i]; const unsigned int offset = _dofOffset[i]; - ConsistentInit::timeDerivative(m, simTime, simState.vecStateY + offset, simState.vecStateYdot + offset, _tempState + offset, _threadLocalStorage); + ConsistentInit::timeDerivative(m, simTime, simState.vecStateY + offset, simState.vecStateYdot + offset, + _tempState + offset, _threadLocalStorage); } CADET_PARFOR_END; // Zero out the coupling DOFs (provides right hand side of 0 for solveCouplingDOF()) @@ -544,22 +593,24 @@ void ModelSystem::consistentInitialConditionAlgorithm(const SimulationTime& simT } void ModelSystem::consistentInitialConditions(const SimulationTime& simTime, const SimulationState& simState, - const AdJacobianParams& adJac, double errorTol) + const AdJacobianParams& adJac, double errorTol) { consistentInitialConditionAlgorithm(simTime, simState, adJac, errorTol); } -void ModelSystem::consistentInitialSensitivity(const SimulationTime& simTime, - const ConstSimulationState& simState, std::vector& vecSensY, - std::vector& vecSensYdot, active* const adRes, active* const adY) +void ModelSystem::consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, + std::vector& vecSensY, std::vector& vecSensYdot, + active* const adRes, active* const adY) { consistentInitialSensitivityAlgorithm(simTime, simState, vecSensY, vecSensYdot, adRes, adY); } template void ModelSystem::consistentInitialSensitivityAlgorithm(const SimulationTime& simTime, - const ConstSimulationState& simState, std::vector& vecSensY, - std::vector& vecSensYdot, active* const adRes, active* const adY) + const ConstSimulationState& simState, + std::vector& vecSensY, + std::vector& vecSensYdot, active* const adRes, + active* const adY) { BENCH_SCOPE(_timerConsistentInit); @@ -583,7 +634,8 @@ void ModelSystem::consistentInitialSensitivityAlgorithm(const SimulationTime& si vecSensYdotLocal[j] = vecSensYdot[j] + offset; } - ConsistentInit::parameterSensitivity(m, simTime, applyOffset(simState, offset), vecSensYlocal, vecSensYdotLocal, adRes + offset, _threadLocalStorage); + ConsistentInit::parameterSensitivity(m, simTime, applyOffset(simState, offset), vecSensYlocal, + vecSensYdotLocal, adRes + offset, _threadLocalStorage); } } @@ -613,7 +665,8 @@ void ModelSystem::consistentInitialSensitivityAlgorithm(const SimulationTime& si vecSensYdotLocal[j] = vecSensYdot[j] + offset; } - ConsistentInit::parameterSensitivity(m, simTime, applyOffset(simState, offset), vecSensYlocal, vecSensYdotLocal, adRes + offset, _threadLocalStorage); + ConsistentInit::parameterSensitivity(m, simTime, applyOffset(simState, offset), vecSensYlocal, + vecSensYdotLocal, adRes + offset, _threadLocalStorage); } } @@ -628,11 +681,13 @@ void ModelSystem::consistentInitialSensitivityAlgorithm(const SimulationTime& si } else { - ad::adMatrixVectorMultiply(_jacActiveFN[0], simState.vecStateYdot + _dofOffset[0], vsyd + finalOffset, -1.0, 0.0, i); + ad::adMatrixVectorMultiply(_jacActiveFN[0], simState.vecStateYdot + _dofOffset[0], vsyd + finalOffset, -1.0, + 0.0, i); for (unsigned int j = 1; j < _models.size(); ++j) { const unsigned int offset = _dofOffset[j]; - ad::adMatrixVectorMultiply(_jacActiveFN[j], simState.vecStateYdot + offset, vsyd + finalOffset, -1.0, 1.0, i); + ad::adMatrixVectorMultiply(_jacActiveFN[j], simState.vecStateYdot + offset, vsyd + finalOffset, -1.0, + 1.0, i); } } @@ -652,17 +707,18 @@ void ModelSystem::consistentInitialSensitivityAlgorithm(const SimulationTime& si } void ModelSystem::leanConsistentInitialConditions(const SimulationTime& simTime, const SimulationState& simState, - const AdJacobianParams& adJac, double errorTol) + const AdJacobianParams& adJac, double errorTol) { consistentInitialConditionAlgorithm(simTime, simState, adJac, errorTol); } void ModelSystem::leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active* const adRes, active* const adY) + std::vector& vecSensY, std::vector& vecSensYdot, + active* const adRes, active* const adY) { consistentInitialSensitivityAlgorithm(simTime, simState, vecSensY, vecSensYdot, adRes, adY); } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/ModelSystemImpl-LinearSolver.cpp b/src/libcadet/model/ModelSystemImpl-LinearSolver.cpp index a4b3da37d..8dc74d08f 100644 --- a/src/libcadet/model/ModelSystemImpl-LinearSolver.cpp +++ b/src/libcadet/model/ModelSystemImpl-LinearSolver.cpp @@ -19,7 +19,7 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include +#include #endif #include "model/ModelSystemImpl-Helper.hpp" @@ -31,7 +31,7 @@ namespace model { int ModelSystem::linearSolve(double t, double alpha, double outerTol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) + const ConstSimulationState& simState) { if (_linearModelOrdering.sliceSize(_curSwitchIndex) == 0) { @@ -45,8 +45,8 @@ int ModelSystem::linearSolve(double t, double alpha, double outerTol, double* co } } -int ModelSystem::linearSolveSequential(double t, double alpha, double outerTol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) +int ModelSystem::linearSolveSequential(double t, double alpha, double outerTol, double* const rhs, + double const* const weight, const ConstSimulationState& simState) { // TODO: Add early out error checks @@ -71,7 +71,8 @@ int ModelSystem::linearSolveSequential(double t, double alpha, double outerTol, for (std::size_t j = 0; j < _models.size(); ++j) { const unsigned int offset2 = _dofOffset[j]; - _jacFN[j].multiplySubtract(rhs + offset2, rhs + finalOffset, _conDofOffset[idxUnit], _conDofOffset[idxUnit+1]); + _jacFN[j].multiplySubtract(rhs + offset2, rhs + finalOffset, _conDofOffset[idxUnit], + _conDofOffset[idxUnit + 1]); } // Calculate inlet DOF for unit operation based on the coupling conditions. @@ -84,21 +85,22 @@ int ModelSystem::linearSolveSequential(double t, double alpha, double outerTol, const unsigned int localStride = m->localInletComponentStride(port); for (unsigned int comp = 0; comp < m->numComponents(); ++comp) { - rhs[offset + localIndex + comp*localStride] = rhs[idxCoupling]; + rhs[offset + localIndex + comp * localStride] = rhs[idxCoupling]; ++idxCoupling; } } } // Solve unit operation itself - _errorIndicator[idxUnit] = m->linearSolve(t, alpha, outerTol, rhs + offset, weight + offset, applyOffset(simState, offset)); + _errorIndicator[idxUnit] = + m->linearSolve(t, alpha, outerTol, rhs + offset, weight + offset, applyOffset(simState, offset)); } return totalErrorIndicatorFromLocal(_errorIndicator); } -int ModelSystem::linearSolveParallel(double t, double alpha, double outerTol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) +int ModelSystem::linearSolveParallel(double t, double alpha, double outerTol, double* const rhs, + double const* const weight, const ConstSimulationState& simState) { // TODO: Add early out error checks @@ -114,7 +116,8 @@ int ModelSystem::linearSolveParallel(double t, double alpha, double outerTol, do { IUnitOperation* const m = _models[i]; const unsigned int offset = _dofOffset[i]; - _errorIndicator[i] = m->linearSolve(t, alpha, outerTol, rhs + offset, weight + offset, applyOffset(simState, offset)); + _errorIndicator[i] = + m->linearSolve(t, alpha, outerTol, rhs + offset, weight + offset, applyOffset(simState, offset)); } CADET_PARFOR_END; // Solve last row of L with backwards substitution: y_f = b_f - \sum_{i=0}^{N_z} J_{f,i} y_i @@ -183,7 +186,8 @@ int ModelSystem::linearSolveParallel(double t, double alpha, double outerTol, do _jacNF[idxModel].multiplyVector(rhs + finalOffset, _tempState + offset); // Apply N_i^{-1} to tempState_i - const int linSolve = m->linearSolve(t, alpha, outerTol, _tempState + offset, weight + offset, applyOffset(simState, offset)); + const int linSolve = + m->linearSolve(t, alpha, outerTol, _tempState + offset, weight + offset, applyOffset(simState, offset)); _errorIndicator[idxModel] = updateErrorIndicator(_errorIndicator[idxModel], linSolve); // Compute rhs_i = y_i - N_i^{-1} * N_{i,f} * y_f = y_i - tempState_i @@ -208,15 +212,16 @@ S &= J_f - J_{f,0} \, J_0^{-1} \, J_{0,f} - \sum_{p=1}^{N_z}{J_{f,p} \, J_p^{-1} * and @f$ J_{f,i} @f$ for @f$ i = 0, \dots, N_{z} @f$ are sparse. * * The matrix-vector multiplication is executed in parallel as follows: -* -# Compute @f$ J_{f,i} \, J_i^{-1} \, J_{i,f} @f$ independently (in parallel with respect to index @f$ i @f$) +* -# Compute @f$ J_{f,i} \, J_i^{-1} \, J_{i,f} @f$ independently (in parallel with respect to index @f$ i +@f$) * -# Subtract the result from @f$ z @f$ in a critical section to avoid race conditions * * @param [in] x Vector @f$ x @f$ the matrix @f$ S @f$ is multiplied with * @param [out] z Result of the matrix-vector multiplication * @return @c 0 if successful, any other value in case of failure */ -int ModelSystem::schurComplementMatrixVector(double const* x, double* z, double t, double alpha, double outerTol, double const* const weight, - const ConstSimulationState& simState) const +int ModelSystem::schurComplementMatrixVector(double const* x, double* z, double t, double alpha, double outerTol, + double const* const weight, const ConstSimulationState& simState) const { BENCH_SCOPE(_timerMatVec); @@ -238,7 +243,8 @@ int ModelSystem::schurComplementMatrixVector(double const* x, double* z, double _jacNF[idxModel].multiplyVector(x, _tempState + offset); // Apply N_i^{-1} to tempState_i - const int linSolve = m->linearSolve(t, alpha, outerTol, _tempState + offset, weight + offset, applyOffset(simState, offset)); + const int linSolve = + m->linearSolve(t, alpha, outerTol, _tempState + offset, weight + offset, applyOffset(simState, offset)); _errorIndicator[idxModel] = updateErrorIndicator(_errorIndicator[idxModel], linSolve); // Apply J_{f,i} and subtract results from z @@ -254,7 +260,8 @@ int ModelSystem::schurComplementMatrixVector(double const* x, double* z, double } /** - * @brief Multiplies a vector with the full Jacobian of the entire system (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, \dot{y}\right) @f$) + * @brief Multiplies a vector with the full Jacobian of the entire system (i.e., @f$ \frac{\partial F}{\partial + * y}\left(t, y, \dot{y}\right) @f$) * @details Actually, the operation @f$ z = \alpha \frac{\partial F}{\partial y} x + \beta z @f$ is performed. * @param [in] simTime Current simulation time point * @param [in] simState Simulation state vectors @@ -263,7 +270,8 @@ int ModelSystem::schurComplementMatrixVector(double const* x, double* z, double * @param [in] beta Factor @f$ \beta @f$ in front of @f$ z @f$ * @param [in,out] ret Vector @f$ z @f$ which stores the result of the operation */ -void ModelSystem::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret) +void ModelSystem::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret) { for (std::size_t idxModel = 0; idxModel < _models.size(); ++idxModel) { @@ -275,14 +283,16 @@ void ModelSystem::multiplyWithJacobian(const SimulationTime& simTime, const Cons } /** - * @brief Multiplies a vector with the full time derivative Jacobian of the entire system (i.e., @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$) + * @brief Multiplies a vector with the full time derivative Jacobian of the entire system (i.e., @f$ \frac{\partial + * F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$) * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * @param [in] simTime Current simulation time point * @param [in] simState Simulation state vectors * @param [in] yS Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ * @param [in,out] ret Vector @f$ z @f$ which stores the result of the operation */ -void ModelSystem::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double* ret) +void ModelSystem::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double* ret) { for (std::size_t idxModel = 0; idxModel < _models.size(); ++idxModel) { @@ -295,377 +305,382 @@ void ModelSystem::multiplyWithDerivativeJacobian(const SimulationTime& simTime, #ifdef CADET_DEBUG - /** - * @brief Generate full system Jacobian FD and multiplyWithJacobian - * @details During debugging this allows you to generate the full jacobian and verify the jacobian structure - * is what it should be. The system uses FD and multiplyWithJacobian to create the full jacobian. - * Use this function with a debugger and pull the values out of memory to visualize it. - * - * @param [in] simTime Current simulation time point - * @param [in] simState Simulation state vectors - */ - void ModelSystem::genJacobian(const SimulationTime& simTime, const ConstSimulationState& simState) - { - // This method is only for debugging. No point in optimizing it - const unsigned int size = numDofs(); +/** + * @brief Generate full system Jacobian FD and multiplyWithJacobian + * @details During debugging this allows you to generate the full jacobian and verify the jacobian structure + * is what it should be. The system uses FD and multiplyWithJacobian to create the full jacobian. + * Use this function with a debugger and pull the values out of memory to visualize it. + * + * @param [in] simTime Current simulation time point + * @param [in] simState Simulation state vectors + */ +void ModelSystem::genJacobian(const SimulationTime& simTime, const ConstSimulationState& simState) +{ + // This method is only for debugging. No point in optimizing it + const unsigned int size = numDofs(); - // Jacobians are saved in column-major ordering (i.e., each column is added to the array sequentially / columns are stacked together) - std::vector jacobian(size*size, 0.0); - std::vector jacobianDot(size*size, 0.0); + // Jacobians are saved in column-major ordering (i.e., each column is added to the array sequentially / columns are + // stacked together) + std::vector jacobian(size * size, 0.0); + std::vector jacobianDot(size * size, 0.0); - std::vector jacobianFD(size*size, 0.0); - std::vector jacobianFDDot(size*size, 0.0); + std::vector jacobianFD(size * size, 0.0); + std::vector jacobianFDDot(size * size, 0.0); - const double h = 1e-5; + const double h = 1e-5; - std::vector f(size, 0.0); - std::vector fdot(size, 0.0); - std::vector fh(size, 0.0); - std::vector fhdot(size, 0.0); + std::vector f(size, 0.0); + std::vector fdot(size, 0.0); + std::vector fh(size, 0.0); + std::vector fhdot(size, 0.0); - std::vector res(size, 0.0); - std::vector resh(size, 0.0); + std::vector res(size, 0.0); + std::vector resh(size, 0.0); - // create Jacobian - for (unsigned int i = 0; i < size; ++i) - { - // Clear res and resh - std::fill(res.begin(), res.end(), 0.0); - std::fill(resh.begin(), resh.end(), 0.0); + // create Jacobian + for (unsigned int i = 0; i < size; ++i) + { + // Clear res and resh + std::fill(res.begin(), res.end(), 0.0); + std::fill(resh.begin(), resh.end(), 0.0); - // Copy y and yDot - std::copy_n(simState.vecStateY, size, &f[0]); - std::copy_n(simState.vecStateY, size, &fh[0]); + // Copy y and yDot + std::copy_n(simState.vecStateY, size, &f[0]); + std::copy_n(simState.vecStateY, size, &fh[0]); - std::copy_n(simState.vecStateYdot, size, &fdot[0]); - std::copy_n(simState.vecStateYdot, size, &fhdot[0]); + std::copy_n(simState.vecStateYdot, size, &fdot[0]); + std::copy_n(simState.vecStateYdot, size, &fhdot[0]); - // Change ith entry - double stepSize = h; - if (f[i] != 0.0) - stepSize = f[i] * h; + // Change ith entry + double stepSize = h; + if (f[i] != 0.0) + stepSize = f[i] * h; - f[i] -= stepSize / 2; - fh[i] += stepSize / 2; + f[i] -= stepSize / 2; + fh[i] += stepSize / 2; - residual(simTime, ConstSimulationState{&f[0], &fdot[0]}, &res[0]); - residual(simTime, ConstSimulationState{&fh[0], &fhdot[0]}, &resh[0]); + residual(simTime, ConstSimulationState{&f[0], &fdot[0]}, &res[0]); + residual(simTime, ConstSimulationState{&fh[0], &fhdot[0]}, &resh[0]); - for (unsigned int j = 0; j < size; ++j) - { - jacobianFD[i*size + j] = (resh[j] - res[j]) / stepSize; - } + for (unsigned int j = 0; j < size; ++j) + { + jacobianFD[i * size + j] = (resh[j] - res[j]) / stepSize; } + } - // create JacobianDot - for (unsigned int i = 0; i < size; ++i) - { - // Clear res and resh - std::fill(res.begin(), res.end(), 0.0); - std::fill(resh.begin(), resh.end(), 0.0); + // create JacobianDot + for (unsigned int i = 0; i < size; ++i) + { + // Clear res and resh + std::fill(res.begin(), res.end(), 0.0); + std::fill(resh.begin(), resh.end(), 0.0); - // Copy y and yDot - std::copy_n(simState.vecStateY, size, &f[0]); - std::copy_n(simState.vecStateY, size, &fh[0]); + // Copy y and yDot + std::copy_n(simState.vecStateY, size, &f[0]); + std::copy_n(simState.vecStateY, size, &fh[0]); - std::copy_n(simState.vecStateYdot, size, &fdot[0]); - std::copy_n(simState.vecStateYdot, size, &fhdot[0]); + std::copy_n(simState.vecStateYdot, size, &fdot[0]); + std::copy_n(simState.vecStateYdot, size, &fhdot[0]); - // Change ith entry - double stepSize = h; - if (fdot[i] != 0.0) - stepSize = fdot[i] * h; + // Change ith entry + double stepSize = h; + if (fdot[i] != 0.0) + stepSize = fdot[i] * h; - fdot[i] -= stepSize / 2; - fhdot[i] += stepSize / 2; + fdot[i] -= stepSize / 2; + fhdot[i] += stepSize / 2; - residual(simTime, ConstSimulationState{&f[0], &fdot[0]}, &res[0]); - residual(simTime, ConstSimulationState{&fh[0], &fhdot[0]}, &resh[0]); + residual(simTime, ConstSimulationState{&f[0], &fdot[0]}, &res[0]); + residual(simTime, ConstSimulationState{&fh[0], &fhdot[0]}, &resh[0]); - for (unsigned int j = 0; j < size; ++j) - { - jacobianFDDot[i*size + j] = (resh[j] - res[j]) / stepSize; - } + for (unsigned int j = 0; j < size; ++j) + { + jacobianFDDot[i * size + j] = (resh[j] - res[j]) / stepSize; } + } - std::vector unit(size, 0.0); - - for (unsigned int i = 0; i < size; ++i) - { - std::fill(res.begin(), res.end(), 0.0); - // Clear res and resh - unit[i] = 1.0; + std::vector unit(size, 0.0); - multiplyWithJacobian(simTime, simState, unit.data(), 1.0, 0.0, res.data()); - std::copy(res.begin(), res.end(), jacobian.begin() + i * size); + for (unsigned int i = 0; i < size; ++i) + { + std::fill(res.begin(), res.end(), 0.0); + // Clear res and resh + unit[i] = 1.0; - unit[i] = 0.0; - } + multiplyWithJacobian(simTime, simState, unit.data(), 1.0, 0.0, res.data()); + std::copy(res.begin(), res.end(), jacobian.begin() + i * size); - for (unsigned int i = 0; i < size; ++i) - { - std::fill(res.begin(), res.end(), 0.0); - // Clear res and resh - unit[i] = 1.0; + unit[i] = 0.0; + } - multiplyWithDerivativeJacobian(simTime, simState, unit.data(), res.data()); - std::copy(res.begin(), res.end(), jacobianDot.begin() + i * size); + for (unsigned int i = 0; i < size; ++i) + { + std::fill(res.begin(), res.end(), 0.0); + // Clear res and resh + unit[i] = 1.0; - unit[i] = 0.0; - } + multiplyWithDerivativeJacobian(simTime, simState, unit.data(), res.data()); + std::copy(res.begin(), res.end(), jacobianDot.begin() + i * size); - LOG(Debug) << "jacFD = " << log::MatrixPtr(jacobianFD.data(), size, size, true); - LOG(Debug) << "jacFDDot = " << log::MatrixPtr(jacobianFDDot.data(), size, size, true); - LOG(Debug) << "jac = " << log::MatrixPtr(jacobian.data(), size, size, true); - LOG(Debug) << "jacDot = " << log::MatrixPtr(jacobianDot.data(), size, size, true); + unit[i] = 0.0; } - /** - * @brief Generate full system Jacobian with Sensitivities using FD and multiplyWithJacobian - * @details During debugging this allows you to generate the full sensitivity jacobian and verify the jacobian structure - * is what it should be. The system uses FD and multiplyWithJacobian to create the full jacobian. - * Use this function with a debugger and pull the values out of memory to visualize it. - * - * @param [in] t Current time point - * @param [in] simTime Current simulation time point - * @param [in] simState Simulation state vectors - * @param [in] residual vector - * @param [in] yS Sensitivity State Vector - * @param [in] ySdot Sensitivity State Vector - * @param [in] resS Sensitivity residual vector - * @param [in] adRes - * @param [in] tmp1 - * @param [in] tmp2 - * @param [in] tmp3 - */ - void ModelSystem::genJacobian(unsigned int nSens, const SimulationTime& simTime, - const ConstSimulationState& simState, double const* const res, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, - active* const adRes, double* const tmp1, double* const tmp2, double* const tmp3) - { - // This method is only for debugging. Don't bother optimizing it - const unsigned int size = numDofs(); + LOG(Debug) << "jacFD = " << log::MatrixPtr(jacobianFD.data(), size, size, true); + LOG(Debug) << "jacFDDot = " << log::MatrixPtr(jacobianFDDot.data(), size, size, true); + LOG(Debug) << "jac = " << log::MatrixPtr(jacobian.data(), size, size, true); + LOG(Debug) << "jacDot = " << log::MatrixPtr(jacobianDot.data(), size, size, true); +} - // Jacobians are saved in column-major ordering (i.e., each column is added to the array sequentially / columns are stacked together) - std::vector> jacobianFD(nSens, std::vector(size*size)); - std::vector> jacobianFDDot(nSens, std::vector(size*size)); +/** + * @brief Generate full system Jacobian with Sensitivities using FD and multiplyWithJacobian + * @details During debugging this allows you to generate the full sensitivity jacobian and verify the jacobian structure + * is what it should be. The system uses FD and multiplyWithJacobian to create the full jacobian. + * Use this function with a debugger and pull the values out of memory to visualize it. + * + * @param [in] t Current time point + * @param [in] simTime Current simulation time point + * @param [in] simState Simulation state vectors + * @param [in] residual vector + * @param [in] yS Sensitivity State Vector + * @param [in] ySdot Sensitivity State Vector + * @param [in] resS Sensitivity residual vector + * @param [in] adRes + * @param [in] tmp1 + * @param [in] tmp2 + * @param [in] tmp3 + */ +void ModelSystem::genJacobian(unsigned int nSens, const SimulationTime& simTime, const ConstSimulationState& simState, + double const* const res, const std::vector& yS, + const std::vector& ySdot, const std::vector& resS, + active* const adRes, double* const tmp1, double* const tmp2, double* const tmp3) +{ + // This method is only for debugging. Don't bother optimizing it + const unsigned int size = numDofs(); - const double h = 1e-5; + // Jacobians are saved in column-major ordering (i.e., each column is added to the array sequentially / columns are + // stacked together) + std::vector> jacobianFD(nSens, std::vector(size * size)); + std::vector> jacobianFDDot(nSens, std::vector(size * size)); - // -h/2 - std::vector tmp1mh(size, 0.0); - std::vector tmp2mh(size, 0.0); - std::vector tmp3mh(size, 0.0); + const double h = 1e-5; - // h/2 - std::vector tmp1ph(size, 0.0); - std::vector tmp2ph(size, 0.0); - std::vector tmp3ph(size, 0.0); + // -h/2 + std::vector tmp1mh(size, 0.0); + std::vector tmp2mh(size, 0.0); + std::vector tmp3mh(size, 0.0); - std::vector adResmh(size, 0.0); - std::vector adResph(size, 0.0); + // h/2 + std::vector tmp1ph(size, 0.0); + std::vector tmp2ph(size, 0.0); + std::vector tmp3ph(size, 0.0); - std::vector ySmh(nSens); - std::vector ySdotmh(nSens); - std::vector resSmh(nSens); + std::vector adResmh(size, 0.0); + std::vector adResph(size, 0.0); - std::vector ySph(nSens); - std::vector ySdotph(nSens); - std::vector resSph(nSens); + std::vector ySmh(nSens); + std::vector ySdotmh(nSens); + std::vector resSmh(nSens); - std::vector CySmh(nSens); - std::vector CySdotmh(nSens); + std::vector ySph(nSens); + std::vector ySdotph(nSens); + std::vector resSph(nSens); - std::vector CySph(nSens); - std::vector CySdotph(nSens); + std::vector CySmh(nSens); + std::vector CySdotmh(nSens); - // Allocate memory - for (unsigned int j = 0; j < nSens; ++j) - { - ySmh[j] = new double[size]; - ySdotmh[j] = new double[size]; - resSmh[j] = new double[size]; + std::vector CySph(nSens); + std::vector CySdotph(nSens); - ySph[j] = new double[size]; - ySdotph[j] = new double[size]; - resSph[j] = new double[size]; - } + // Allocate memory + for (unsigned int j = 0; j < nSens; ++j) + { + ySmh[j] = new double[size]; + ySdotmh[j] = new double[size]; + resSmh[j] = new double[size]; + ySph[j] = new double[size]; + ySdotph[j] = new double[size]; + resSph[j] = new double[size]; + } - for (unsigned int j = 0; j < nSens; ++j) - { - CySmh[j] = ySmh[j]; - CySdotmh[j] = ySdotmh[j]; - CySph[j] = ySph[j]; - CySdotph[j] = ySdotph[j]; - } + for (unsigned int j = 0; j < nSens; ++j) + { + CySmh[j] = ySmh[j]; + CySdotmh[j] = ySdotmh[j]; + CySph[j] = ySph[j]; + CySdotph[j] = ySdotph[j]; + } - // create Jacobian - for (unsigned int i = 0; i < size; ++i) - { - // need to make copies of yS, ySdot, resS, adRes, tmp1, tmp2, tmp3 + // create Jacobian + for (unsigned int i = 0; i < size; ++i) + { + // need to make copies of yS, ySdot, resS, adRes, tmp1, tmp2, tmp3 - // adRes - std::copy_n(adRes, size, &adResmh[0]); - std::copy_n(adRes, size, &adResph[0]); + // adRes + std::copy_n(adRes, size, &adResmh[0]); + std::copy_n(adRes, size, &adResph[0]); - // tmp1 - std::copy_n(tmp1, size, &tmp1mh[0]); - std::copy_n(tmp1, size, &tmp1ph[0]); + // tmp1 + std::copy_n(tmp1, size, &tmp1mh[0]); + std::copy_n(tmp1, size, &tmp1ph[0]); - // tmp2 - std::copy_n(tmp2, size, &tmp2mh[0]); - std::copy_n(tmp2, size, &tmp2ph[0]); + // tmp2 + std::copy_n(tmp2, size, &tmp2mh[0]); + std::copy_n(tmp2, size, &tmp2ph[0]); - // tmp3 - std::copy_n(tmp3, size, &tmp3mh[0]); - std::copy_n(tmp3, size, &tmp3ph[0]); + // tmp3 + std::copy_n(tmp3, size, &tmp3mh[0]); + std::copy_n(tmp3, size, &tmp3ph[0]); - // Clear sync up - for (unsigned int j = 0; j < nSens; ++j) - { - std::copy_n(yS[j], size, ySmh[j]); - std::copy_n(yS[j], size, ySph[j]); + // Clear sync up + for (unsigned int j = 0; j < nSens; ++j) + { + std::copy_n(yS[j], size, ySmh[j]); + std::copy_n(yS[j], size, ySph[j]); - std::copy_n(ySdot[j], size, ySdotmh[j]); - std::copy_n(ySdot[j], size, ySdotph[j]); + std::copy_n(ySdot[j], size, ySdotmh[j]); + std::copy_n(ySdot[j], size, ySdotph[j]); - std::copy_n(resS[j], size, resSmh[j]); - std::copy_n(resS[j], size, resSph[j]); - } + std::copy_n(resS[j], size, resSmh[j]); + std::copy_n(resS[j], size, resSph[j]); + } - std::vector stepSize(nSens, false); + std::vector stepSize(nSens, false); - // Change ith entry - for (unsigned int j = 0; j < nSens; ++j) + // Change ith entry + for (unsigned int j = 0; j < nSens; ++j) + { + const double val = ySmh[j][i]; + if (val == 0.0) { - const double val = ySmh[j][i]; - if (val == 0.0) - { - ySmh[j][i] -= h / 2; - ySph[j][i] += h / 2; - stepSize[j] = h; - } - else - { - ySmh[j][i] -= val * h / 2; - ySph[j][i] += val * h / 2; - stepSize[j] = val * h; - } + ySmh[j][i] -= h / 2; + ySph[j][i] += h / 2; + stepSize[j] = h; + } + else + { + ySmh[j][i] -= val * h / 2; + ySph[j][i] += val * h / 2; + stepSize[j] = val * h; } + } - // clear jacobian + // clear jacobian - // -h/2 - residualSensFwd(nSens, simTime, simState, res, CySmh, CySdotmh, resSmh, &adResmh[0], &tmp1mh[0], &tmp2mh[0], &tmp3mh[0]); + // -h/2 + residualSensFwd(nSens, simTime, simState, res, CySmh, CySdotmh, resSmh, &adResmh[0], &tmp1mh[0], &tmp2mh[0], + &tmp3mh[0]); - // +h/2 - residualSensFwd(nSens, simTime, simState, res, CySph, CySdotph, resSph, &adResph[0], &tmp1ph[0], &tmp2ph[0], &tmp3ph[0]); + // +h/2 + residualSensFwd(nSens, simTime, simState, res, CySph, CySdotph, resSph, &adResph[0], &tmp1ph[0], &tmp2ph[0], + &tmp3ph[0]); - for (unsigned int sens = 0; sens < nSens; ++sens) + for (unsigned int sens = 0; sens < nSens; ++sens) + { + for (unsigned int j = 0; j < size; ++j) { - for (unsigned int j = 0; j < size; ++j) - { - // Residual is negative so it has to be negated to get the correct jacobian - jacobianFD[sens][i*size + j] = (resSph[sens][j] - resSmh[sens][j]) / stepSize[sens]; - } + // Residual is negative so it has to be negated to get the correct jacobian + jacobianFD[sens][i * size + j] = (resSph[sens][j] - resSmh[sens][j]) / stepSize[sens]; } } + } - //create Jacobian - for (unsigned int i = 0; i < size; ++i) - { - // need to make copies of yS, ySdot, resS, adRes, tmp1, tmp2, tmp3 + // create Jacobian + for (unsigned int i = 0; i < size; ++i) + { + // need to make copies of yS, ySdot, resS, adRes, tmp1, tmp2, tmp3 - // adRes - std::copy_n(adRes, size, &adResmh[0]); - std::copy_n(adRes, size, &adResph[0]); + // adRes + std::copy_n(adRes, size, &adResmh[0]); + std::copy_n(adRes, size, &adResph[0]); - // tmp1 - std::copy_n(tmp1, size, &tmp1mh[0]); - std::copy_n(tmp1, size, &tmp1ph[0]); + // tmp1 + std::copy_n(tmp1, size, &tmp1mh[0]); + std::copy_n(tmp1, size, &tmp1ph[0]); - // tmp2 - std::copy_n(tmp2, size, &tmp2mh[0]); - std::copy_n(tmp2, size, &tmp2ph[0]); + // tmp2 + std::copy_n(tmp2, size, &tmp2mh[0]); + std::copy_n(tmp2, size, &tmp2ph[0]); - // tmp3 - std::copy_n(tmp3, size, &tmp3mh[0]); - std::copy_n(tmp3, size, &tmp3ph[0]); + // tmp3 + std::copy_n(tmp3, size, &tmp3mh[0]); + std::copy_n(tmp3, size, &tmp3ph[0]); - // Clear sync up - for (unsigned int j = 0; j < nSens; ++j) - { - std::copy_n(yS[j], size, ySmh[j]); - std::copy_n(yS[j], size, ySph[j]); + // Clear sync up + for (unsigned int j = 0; j < nSens; ++j) + { + std::copy_n(yS[j], size, ySmh[j]); + std::copy_n(yS[j], size, ySph[j]); - std::copy_n(ySdot[j], size, ySdotmh[j]); - std::copy_n(ySdot[j], size, ySdotph[j]); + std::copy_n(ySdot[j], size, ySdotmh[j]); + std::copy_n(ySdot[j], size, ySdotph[j]); - std::copy_n(resS[j], size, resSmh[j]); - std::copy_n(resS[j], size, resSph[j]); - } + std::copy_n(resS[j], size, resSmh[j]); + std::copy_n(resS[j], size, resSph[j]); + } - std::vector stepSize(nSens, false); + std::vector stepSize(nSens, false); - // Change ith entry - for (unsigned int j = 0; j < nSens; ++j) + // Change ith entry + for (unsigned int j = 0; j < nSens; ++j) + { + const double val = ySdotmh[j][i]; + if (val == 0.0) { - const double val = ySdotmh[j][i]; - if (val == 0.0) - { - ySdotmh[j][i] -= h / 2; - ySdotph[j][i] += h / 2; - stepSize[j] = h; - } - else - { - ySdotmh[j][i] -= val * h / 2; - ySdotph[j][i] += val * h / 2; - stepSize[j] = val * h; - } + ySdotmh[j][i] -= h / 2; + ySdotph[j][i] += h / 2; + stepSize[j] = h; } + else + { + ySdotmh[j][i] -= val * h / 2; + ySdotph[j][i] += val * h / 2; + stepSize[j] = val * h; + } + } - // clear jacobian + // clear jacobian - // -h/2 - residualSensFwd(nSens, simTime, simState, res, CySmh, CySdotmh, resSmh, &adResmh[0], &tmp1mh[0], &tmp2mh[0], &tmp3mh[0]); + // -h/2 + residualSensFwd(nSens, simTime, simState, res, CySmh, CySdotmh, resSmh, &adResmh[0], &tmp1mh[0], &tmp2mh[0], + &tmp3mh[0]); - // +h/2 - residualSensFwd(nSens, simTime, simState, res, CySph, CySdotph, resSph, &adResph[0], &tmp1ph[0], &tmp2ph[0], &tmp3ph[0]); + // +h/2 + residualSensFwd(nSens, simTime, simState, res, CySph, CySdotph, resSph, &adResph[0], &tmp1ph[0], &tmp2ph[0], + &tmp3ph[0]); - for (unsigned int sens = 0; sens < nSens; ++sens) + for (unsigned int sens = 0; sens < nSens; ++sens) + { + for (unsigned int j = 0; j < size; ++j) { - for (unsigned int j = 0; j < size; ++j) - { - //Residual is negative so it has to be negated to get the correct jacobian - jacobianFDDot[sens][i*size + j] = (resSph[sens][j] - resSmh[sens][j]) / stepSize[sens]; - } + // Residual is negative so it has to be negated to get the correct jacobian + jacobianFDDot[sens][i * size + j] = (resSph[sens][j] - resSmh[sens][j]) / stepSize[sens]; } } + } - // Free memory + // Free memory - for (unsigned int j = 0; j < nSens; ++j) - { - delete ySmh[j]; - delete ySdotmh[j]; - delete resSmh[j]; + for (unsigned int j = 0; j < nSens; ++j) + { + delete ySmh[j]; + delete ySdotmh[j]; + delete resSmh[j]; - delete ySph[j]; - delete ySdotph[j]; - delete resSph[j]; - } + delete ySph[j]; + delete ySdotph[j]; + delete resSph[j]; + } - for (unsigned int i = 0; i < nSens; ++i) - { - LOG(Debug) << "jacSens" << i << " = " << log::MatrixPtr(jacobianFD[i].data(), size, size, true); - LOG(Debug) << "jacSensDot" << i << " = " << log::MatrixPtr(jacobianFDDot[i].data(), size, size, true); - } + for (unsigned int i = 0; i < nSens; ++i) + { + LOG(Debug) << "jacSens" << i << " = " << log::MatrixPtr(jacobianFD[i].data(), size, size, true); + LOG(Debug) << "jacSensDot" << i << " = " << log::MatrixPtr(jacobianFDDot[i].data(), size, size, true); } +} #endif -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/ModelSystemImpl-Residual.cpp b/src/libcadet/model/ModelSystemImpl-Residual.cpp index 4b3e9646e..40c1b2dce 100644 --- a/src/libcadet/model/ModelSystemImpl-Residual.cpp +++ b/src/libcadet/model/ModelSystemImpl-Residual.cpp @@ -21,65 +21,73 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include +#include #endif #include "model/ModelSystemImpl-Helper.hpp" namespace { - /** - * @brief Selects either double or active SparseMatrix based on template argument - * @details Helper function that returns either @p a or @p b depending on the template argument. - * @param [in] a SparseMatrix of double elements - * @param [in] b SparseMatrix of active elements - * @tparam selector_t One of @c double or @c active - * @return Either @p a or @p b depending on the template argument - */ - template - const cadet::linalg::SparseMatrix& select(const cadet::linalg::SparseMatrix& a, const cadet::linalg::SparseMatrix& b) - { - cadet_assert(false); - } +/** + * @brief Selects either double or active SparseMatrix based on template argument + * @details Helper function that returns either @p a or @p b depending on the template argument. + * @param [in] a SparseMatrix of double elements + * @param [in] b SparseMatrix of active elements + * @tparam selector_t One of @c double or @c active + * @return Either @p a or @p b depending on the template argument + */ +template +const cadet::linalg::SparseMatrix& select(const cadet::linalg::SparseMatrix& a, + const cadet::linalg::SparseMatrix& b) +{ + cadet_assert(false); +} - template <> - const cadet::linalg::SparseMatrix& select(const cadet::linalg::SparseMatrix& a, const cadet::linalg::SparseMatrix& b) - { - return a; - } +template <> +const cadet::linalg::SparseMatrix& select(const cadet::linalg::SparseMatrix& a, + const cadet::linalg::SparseMatrix& b) +{ + return a; +} - template <> - const cadet::linalg::SparseMatrix& select(const cadet::linalg::SparseMatrix& a, const cadet::linalg::SparseMatrix& b) - { - return b; - } +template <> +const cadet::linalg::SparseMatrix& select( + const cadet::linalg::SparseMatrix& a, const cadet::linalg::SparseMatrix& b) +{ + return b; +} - struct FullTag {}; - struct LeanTag {}; +struct FullTag +{ +}; +struct LeanTag +{ +}; - template - struct ResidualSensCaller {}; +template struct ResidualSensCaller +{ +}; - template <> - struct ResidualSensCaller +template <> struct ResidualSensCaller +{ + static inline int call(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, + const cadet::ConstSimulationState& simState, const cadet::AdJacobianParams& adJac, + cadet::util::ThreadLocalStorage& threadLocalMem) { - static inline int call(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, - const cadet::ConstSimulationState& simState, const cadet::AdJacobianParams& adJac, cadet::util::ThreadLocalStorage& threadLocalMem) - { - return model->residualSensFwdWithJacobian(simTime, simState, adJac, threadLocalMem); - } - }; + return model->residualSensFwdWithJacobian(simTime, simState, adJac, threadLocalMem); + } +}; - template <> - struct ResidualSensCaller +template <> struct ResidualSensCaller +{ + static inline int call(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, + const cadet::ConstSimulationState& simState, const cadet::AdJacobianParams& adJac, + cadet::util::ThreadLocalStorage& threadLocalMem) { - static inline int call(cadet::IUnitOperation* model, const cadet::SimulationTime& simTime, - const cadet::ConstSimulationState& simState, const cadet::AdJacobianParams& adJac, cadet::util::ThreadLocalStorage& threadLocalMem) - { - return model->residualSensFwdAdOnly(simTime, simState, adJac.adRes, threadLocalMem); - } - }; -} + return model->residualSensFwdAdOnly(simTime, simState, adJac.adRes, threadLocalMem); + } +}; +} // namespace namespace cadet { @@ -87,7 +95,8 @@ namespace cadet namespace model { -int ModelSystem::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac) +int ModelSystem::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac) { #ifdef CADET_PARALLELIZE @@ -105,7 +114,8 @@ int ModelSystem::jacobian(const SimulationTime& simTime, const ConstSimulationSt m->setFlowRates(_flowRateIn[i], _flowRateOut[i]); } - _errorIndicator[i] = m->jacobian(simTime, applyOffset(simState, offset), res + offset, applyOffset(adJac, offset), _threadLocalStorage); + _errorIndicator[i] = m->jacobian(simTime, applyOffset(simState, offset), res + offset, + applyOffset(adJac, offset), _threadLocalStorage); } CADET_PARFOR_END; @@ -116,7 +126,9 @@ int ModelSystem::jacobian(const SimulationTime& simTime, const ConstSimulationSt return totalErrorIndicatorFromLocal(_errorIndicator); } -void ModelSystem::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) +void ModelSystem::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac) { // Check if simulation is (re-)starting from the very beginning if (secIdx == 0) @@ -157,14 +169,16 @@ void ModelSystem::notifyDiscontinuousSectionTransition(double t, unsigned int se if (cadet_likely(switchOccurred && !_hasDynamicFlowRates)) { - // Update bottom macro row *after* models have changed their flow directions due to updating their internal velocities + // Update bottom macro row *after* models have changed their flow directions due to updating their internal + // velocities assembleBottomMacroRow(t); } #ifdef CADET_DEBUG int const* ptrConn = _connections[_curSwitchIndex]; - LOG(Debug) << "Switching from valve configuration " << prevSwitch << " to " << _curSwitchIndex << " (sec = " << secIdx << " wrapSec = " << wrapSec << ")"; + LOG(Debug) << "Switching from valve configuration " << prevSwitch << " to " << _curSwitchIndex + << " (sec = " << secIdx << " wrapSec = " << wrapSec << ")"; for (unsigned int i = 0; i < _connections.sliceSize(_curSwitchIndex) / 6; ++i, ptrConn += 6) { // Extract current connection @@ -177,8 +191,9 @@ void ModelSystem::notifyDiscontinuousSectionTransition(double t, unsigned int se // Number of components was already verified so assume they are all correct - LOG(Debug) << "Unit op " << uoSource << " (" << _models[uoSource]->unitOperationName() << ") port " << portSource << " comp " << compSource << " => " - << uoDest << " (" << _models[uoDest]->unitOperationName() << ") port " << portDest << " comp " << compDest; + LOG(Debug) << "Unit op " << uoSource << " (" << _models[uoSource]->unitOperationName() << ") port " + << portSource << " comp " << compSource << " => " << uoDest << " (" + << _models[uoDest]->unitOperationName() << ") port " << portDest << " comp " << compDest; } #endif @@ -224,7 +239,6 @@ void ModelSystem::updateModelFlowRates(double t, unsigned int idxUnit) } } - /** * @brief Updates inlet and outlet flow rates of the given unit operation * @details Updates the corresponding slice of _flowRateIn and _flowRateOut. @@ -260,8 +274,8 @@ void ModelSystem::updateDynamicModelFlowRates(double t, unsigned int idxUnit) } /** -* @brief Calculate inlet and outlet flow rate coefficients for each unit operation in current section -*/ + * @brief Calculate inlet and outlet flow rate coefficients for each unit operation in current section + */ void ModelSystem::calcUnitFlowRateCoefficients() { // Calculate total flow rate for each inlet @@ -286,16 +300,17 @@ void ModelSystem::calcUnitFlowRateCoefficients() for (unsigned int i = 0; i < _connections.sliceSize(_curSwitchIndex) / 6; ++i) { // Extract current connection - const int uoSource = ptrConn[6*i]; - const int uoDest = ptrConn[6*i + 1]; - const int portSource = ptrConn[6*i + 2]; - const int portDest = ptrConn[6*i + 3]; + const int uoSource = ptrConn[6 * i]; + const int uoDest = ptrConn[6 * i + 1]; + const int portSource = ptrConn[6 * i + 2]; + const int portDest = ptrConn[6 * i + 3]; // Check if the same connection has appeared before (with different components) bool skip = false; for (unsigned int j = 0; j < i; ++j) { - if ((ptrConn[6*j] == uoSource) && (ptrConn[6*j + 1] == uoDest) && (ptrConn[6*j + 2] == portSource) && (ptrConn[6*j + 3] == portDest)) + if ((ptrConn[6 * j] == uoSource) && (ptrConn[6 * j + 1] == uoDest) && (ptrConn[6 * j + 2] == portSource) && + (ptrConn[6 * j + 3] == portDest)) { skip = true; break; @@ -376,7 +391,8 @@ void ModelSystem::assembleRightMacroColumn() const unsigned int localInletComponentStride = model->localInletComponentStride(port); for (unsigned int comp = 0; comp < model->numComponents(); ++comp) { - _jacNF[i].addElement(localInletComponentIndex + comp * localInletComponentStride, couplingIdx, -1.0); + _jacNF[i].addElement(localInletComponentIndex + comp * localInletComponentStride, couplingIdx, + -1.0); ++couplingIdx; } } @@ -409,18 +425,19 @@ void ModelSystem::assembleBottomMacroRow(double t) for (unsigned int i = 0; i < _connections.sliceSize(_curSwitchIndex) / 6; ++i) { // Extract current connection - const int uoSource = ptrConn[6*i]; - const int uoDest = ptrConn[6*i + 1]; - const int portSource = ptrConn[6*i + 2]; - const int portDest = ptrConn[6*i + 3]; - const int compSource = ptrConn[6*i + 4]; - const int compDest = ptrConn[6*i + 5]; + const int uoSource = ptrConn[6 * i]; + const int uoDest = ptrConn[6 * i + 1]; + const int portSource = ptrConn[6 * i + 2]; + const int portDest = ptrConn[6 * i + 3]; + const int compSource = ptrConn[6 * i + 4]; + const int compDest = ptrConn[6 * i + 5]; // Obtain index of first connection from uoSource to uoDest unsigned int idx = i; for (unsigned int j = 0; j < i; ++j) { - if ((ptrConn[6*j] == uoSource) && (ptrConn[6*j + 1] == uoDest) && (ptrConn[6*j + 2] == portSource) && (ptrConn[6*j + 3] == portDest)) + if ((ptrConn[6 * j] == uoSource) && (ptrConn[6 * j + 1] == uoDest) && (ptrConn[6 * j + 2] == portSource) && + (ptrConn[6 * j + 3] == portDest)) { idx = j; break; @@ -438,7 +455,9 @@ void ModelSystem::assembleBottomMacroRow(double t) { for (unsigned int j = 0; j < modelSource->numOutletPorts(); ++j) { - const active totInFlow = cubicPoly(_totalInletFlow(uoDest, j), _totalInletFlowLin(uoDest, j), _totalInletFlowQuad(uoDest, j), _totalInletFlowCub(uoDest, j), secT); + const active totInFlow = + cubicPoly(_totalInletFlow(uoDest, j), _totalInletFlowLin(uoDest, j), + _totalInletFlowQuad(uoDest, j), _totalInletFlowCub(uoDest, j), secT); // Ignore ports with incoming flow rate 0 if (totInFlow <= 0.0) @@ -447,7 +466,8 @@ void ModelSystem::assembleBottomMacroRow(double t) const unsigned int outletIndex = modelSource->localOutletComponentIndex(j); const unsigned int outletStride = modelSource->localOutletComponentStride(j); - const active inFlow = -cubicPoly(ptrRate, ptrRateLin, ptrRateQuad, ptrRateCub, idx, secT) / totInFlow; + const active inFlow = + -cubicPoly(ptrRate, ptrRateLin, ptrRateQuad, ptrRateCub, idx, secT) / totInFlow; if (compSource == -1) { @@ -455,14 +475,16 @@ void ModelSystem::assembleBottomMacroRow(double t) // Connect all components with the same flow rate for (unsigned int comp = 0; comp < modelSource->numComponents(); ++comp) { - const unsigned int row = _couplingIdxMap[std::make_tuple(uoDest, j, comp)]; // destination coupling DOF + const unsigned int row = + _couplingIdxMap[std::make_tuple(uoDest, j, comp)]; // destination coupling DOF const unsigned int col = outletIndex + outletStride * comp; _jacActiveFN[uoSource].addElement(row, col, inFlow); } } else { - const unsigned int row = _couplingIdxMap[std::make_tuple(uoDest, j, compDest)]; // destination coupling DOF + const unsigned int row = + _couplingIdxMap[std::make_tuple(uoDest, j, compDest)]; // destination coupling DOF const unsigned int col = outletIndex + outletStride * compSource; _jacActiveFN[uoSource].addElement(row, col, inFlow); } @@ -470,7 +492,9 @@ void ModelSystem::assembleBottomMacroRow(double t) } else { - const active totInFlow = cubicPoly(_totalInletFlow(uoDest, portDest), _totalInletFlowLin(uoDest, portDest), _totalInletFlowQuad(uoDest, portDest), _totalInletFlowCub(uoDest, portDest), secT); + const active totInFlow = + cubicPoly(_totalInletFlow(uoDest, portDest), _totalInletFlowLin(uoDest, portDest), + _totalInletFlowQuad(uoDest, portDest), _totalInletFlowCub(uoDest, portDest), secT); // Ignore ports with incoming flow rate 0 if (totInFlow <= 0.0) @@ -479,21 +503,24 @@ void ModelSystem::assembleBottomMacroRow(double t) const unsigned int outletIndex = modelSource->localOutletComponentIndex(portSource); const unsigned int outletStride = modelSource->localOutletComponentStride(portSource); - const active inFlow = -cubicPoly(ptrRate, ptrRateLin, ptrRateQuad, ptrRateCub, idx, secT) / totInFlow; + const active inFlow = + -cubicPoly(ptrRate, ptrRateLin, ptrRateQuad, ptrRateCub, idx, secT) / totInFlow; if (compSource == -1) { // Connect all components with the same flow rate for (unsigned int comp = 0; comp < modelSource->numComponents(); ++comp) { - const unsigned int row = _couplingIdxMap[std::make_tuple(uoDest, portDest, comp)]; // destination coupling DOF + const unsigned int row = + _couplingIdxMap[std::make_tuple(uoDest, portDest, comp)]; // destination coupling DOF const unsigned int col = outletIndex + outletStride * comp; _jacActiveFN[uoSource].addElement(row, col, inFlow); } } else { - const unsigned int row = _couplingIdxMap[std::make_tuple(uoDest, portDest, compDest)]; // destination coupling DOF + const unsigned int row = + _couplingIdxMap[std::make_tuple(uoDest, portDest, compDest)]; // destination coupling DOF const unsigned int col = outletIndex + outletStride * compSource; _jacActiveFN[uoSource].addElement(row, col, inFlow); } @@ -545,7 +572,7 @@ int ModelSystem::residual(const SimulationTime& simTime, const ConstSimulationSt } int ModelSystem::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, - double* const res, const AdJacobianParams& adJac) + double* const res, const AdJacobianParams& adJac) { BENCH_START(_timerResidual); @@ -564,8 +591,8 @@ int ModelSystem::residualWithJacobian(const SimulationTime& simTime, const Const m->setFlowRates(_flowRateIn[i], _flowRateOut[i]); } - _errorIndicator[i] = m->residualWithJacobian(simTime, applyOffset(simState, offset), - res + offset, applyOffset(adJac, offset), _threadLocalStorage); + _errorIndicator[i] = m->residualWithJacobian(simTime, applyOffset(simState, offset), res + offset, + applyOffset(adJac, offset), _threadLocalStorage); } CADET_PARFOR_END; @@ -580,17 +607,18 @@ int ModelSystem::residualWithJacobian(const SimulationTime& simTime, const Const } /** -* @brief Calculate coupling DOF residual -* @param [in] secIdx Section ID -* @param [in] y State vector -* @param [in] yDot Derivative state vector -* @param [in,out] res Residual vector -* @tparam StateType Type of the state vector -* @tparam ResidualType Type of the residual vector -* @tparam ParamType Type of the parameters -*/ + * @brief Calculate coupling DOF residual + * @param [in] secIdx Section ID + * @param [in] y State vector + * @param [in] yDot Derivative state vector + * @param [in,out] res Residual vector + * @tparam StateType Type of the state vector + * @tparam ResidualType Type of the residual vector + * @tparam ParamType Type of the parameters + */ template -void ModelSystem::residualConnectUnitOps(unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res) CADET_NOEXCEPT +void ModelSystem::residualConnectUnitOps(unsigned int secIdx, StateType const* const y, double const* const yDot, + ResidualType* const res) CADET_NOEXCEPT { // Use connection matrices for the residual const unsigned int finalOffset = _dofOffset.back(); @@ -620,12 +648,14 @@ void ModelSystem::residualConnectUnitOps(unsigned int secIdx, StateType const* c } int ModelSystem::residualSensFwd(unsigned int nSens, const SimulationTime& simTime, - const ConstSimulationState& simState, double const* const res, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, - active* const adRes, double* const tmp1, double* const tmp2, double* const tmp3) + const ConstSimulationState& simState, double const* const res, + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, active* const adRes, double* const tmp1, + double* const tmp2, double* const tmp3) { BENCH_SCOPE(_timerResidualSens); - return residualSensFwdWithJacobianAlgorithm(nSens, simTime, simState, res, yS, ySdot, resS, AdJacobianParams{adRes, nullptr, 0}, tmp1, tmp2, tmp3); + return residualSensFwdWithJacobianAlgorithm(nSens, simTime, simState, res, yS, ySdot, resS, + AdJacobianParams{adRes, nullptr, 0}, tmp1, tmp2, tmp3); } void ModelSystem::multiplyWithMacroJacobian(double const* yS, double alpha, double beta, double* ret) @@ -656,9 +686,9 @@ void ModelSystem::multiplyWithMacroJacobian(double const* yS, double alpha, doub } void ModelSystem::residualSensFwdNorm(unsigned int nSens, const SimulationTime& simTime, - const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, double* const norms, - active* const adRes, double* const tmp) + const ConstSimulationState& simState, const std::vector& yS, + const std::vector& ySdot, double* const norms, active* const adRes, + double* const tmp) { const unsigned int nDOFs = numDofs(); @@ -677,7 +707,8 @@ void ModelSystem::residualSensFwdNorm(unsigned int nSens, const SimulationTime& std::vector tempMem(nDOFs * 2, 0.0); // Evaluate all the sensitivity system residuals at once - residualSensFwd(nSens, simTime, simState, nullptr, yS, ySdot, resPtr, adRes, tmp, tempMem.data(), tempMem.data() + nDOFs); + residualSensFwd(nSens, simTime, simState, nullptr, yS, ySdot, resPtr, adRes, tmp, tempMem.data(), + tempMem.data() + nDOFs); // Calculate norms for (unsigned int i = 0; i < nSens; ++i) @@ -685,24 +716,29 @@ void ModelSystem::residualSensFwdNorm(unsigned int nSens, const SimulationTime& } int ModelSystem::residualSensFwdWithJacobian(unsigned int nSens, const SimulationTime& simTime, - const ConstSimulationState& simState, double const* const res, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, - const AdJacobianParams& adJac, double* const tmp1, double* const tmp2, double* const tmp3) + const ConstSimulationState& simState, double const* const res, + const std::vector& yS, + const std::vector& ySdot, const std::vector& resS, + const AdJacobianParams& adJac, double* const tmp1, double* const tmp2, + double* const tmp3) { - return residualSensFwdWithJacobianAlgorithm(nSens, simTime, simState, res, yS, ySdot, resS, adJac, tmp1, tmp2, tmp3); + return residualSensFwdWithJacobianAlgorithm(nSens, simTime, simState, res, yS, ySdot, resS, adJac, tmp1, tmp2, + tmp3); } template int ModelSystem::residualSensFwdWithJacobianAlgorithm(unsigned int nSens, const SimulationTime& simTime, - const ConstSimulationState& simState, double const* const res, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, - const AdJacobianParams& adJac, double* const tmp1, double* const tmp2, double* const tmp3) + const ConstSimulationState& simState, double const* const res, + const std::vector& yS, + const std::vector& ySdot, + const std::vector& resS, const AdJacobianParams& adJac, + double* const tmp1, double* const tmp2, double* const tmp3) { BENCH_START(_timerResidualSens); const unsigned int nModels = _models.size(); - //Resize yStemp and yStempDot (this should be a noop except for the first time) + // Resize yStemp and yStempDot (this should be a noop except for the first time) _yStemp.resize(nModels); _yStempDot.resize(nModels); _resSTemp.resize(nModels); @@ -731,7 +767,8 @@ int ModelSystem::residualSensFwdWithJacobianAlgorithm(unsigned int nSens, const m->setFlowRates(_flowRateIn[i], _flowRateOut[i]); } - _errorIndicator[i] = ResidualSensCaller::call(m, simTime, applyOffset(simState, offset), applyOffset(adJac, offset), _threadLocalStorage); + _errorIndicator[i] = ResidualSensCaller::call(m, simTime, applyOffset(simState, offset), + applyOffset(adJac, offset), _threadLocalStorage); } CADET_PARFOR_END; // Connect units @@ -759,7 +796,9 @@ int ModelSystem::residualSensFwdWithJacobianAlgorithm(unsigned int nSens, const _resSTemp[i][j] = resS[j] + offset; } - const int intermediateRes = m->residualSensFwdCombine(simTime, applyOffset(simState, offset), _yStemp[i], _yStempDot[i], _resSTemp[i], adJac.adRes + offset, tmp1 + offset, tmp2 + offset, tmp3 + offset); + const int intermediateRes = + m->residualSensFwdCombine(simTime, applyOffset(simState, offset), _yStemp[i], _yStempDot[i], _resSTemp[i], + adJac.adRes + offset, tmp1 + offset, tmp2 + offset, tmp3 + offset); _errorIndicator[i] = updateErrorIndicator(_errorIndicator[i], intermediateRes); } CADET_PARFOR_END; @@ -784,7 +823,7 @@ int ModelSystem::residualSensFwdWithJacobianAlgorithm(unsigned int nSens, const // Directional derivative (dF / dyDot) * sDot (always zero so ignore it) - //The other adRes values have already been taken care of in the unit operations + // The other adRes values have already been taken care of in the unit operations for (unsigned int i = finalOffset; i < numDofs(); ++i) { ptrResS[i] += adJac.adRes[i].getADValue(param); @@ -795,6 +834,6 @@ int ModelSystem::residualSensFwdWithJacobianAlgorithm(unsigned int nSens, const return totalErrorIndicatorFromLocal(_errorIndicator); } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/ModelSystemImpl.cpp b/src/libcadet/model/ModelSystemImpl.cpp index f1efdd753..0392d55d0 100644 --- a/src/libcadet/model/ModelSystemImpl.cpp +++ b/src/libcadet/model/ModelSystemImpl.cpp @@ -31,63 +31,63 @@ namespace { - /** - * @brief Find array index of unit operation with given id - * @details Unit operation index does not need to match index of unit operation in an array. - * @param [in] models List of models - * @param [in] unitOpIdx Unit operation index to look for - * @return Array index of the unit operation identified by unit operation index or invalid array index if the unit operation has not been found - */ - inline unsigned int indexOfUnitOp(const std::vector& models, unsigned int unitOpIdx) +/** + * @brief Find array index of unit operation with given id + * @details Unit operation index does not need to match index of unit operation in an array. + * @param [in] models List of models + * @param [in] unitOpIdx Unit operation index to look for + * @return Array index of the unit operation identified by unit operation index or invalid array index if the unit + * operation has not been found + */ +inline unsigned int indexOfUnitOp(const std::vector& models, unsigned int unitOpIdx) +{ + for (std::size_t i = 0; i < models.size(); ++i) { - for (std::size_t i = 0; i < models.size(); ++i) - { - if (models[i]->unitOperationId() == unitOpIdx) - return i; - } - return models.size(); + if (models[i]->unitOperationId() == unitOpIdx) + return i; } + return models.size(); +} - /** - * @brief Returns whether a given unit operation is a terminal node in the network - * @param [in] conn Connection list - * @param [in] size Number of rows in the connection list - * @param [in] unitOpIdx Index of the unit operation to check - * @return @c true if the unit operation is a terminal node in the network, @c false otherwise - */ - inline bool isTerminal(int const* const conn, unsigned int size, int unitOpIdx) +/** + * @brief Returns whether a given unit operation is a terminal node in the network + * @param [in] conn Connection list + * @param [in] size Number of rows in the connection list + * @param [in] unitOpIdx Index of the unit operation to check + * @return @c true if the unit operation is a terminal node in the network, @c false otherwise + */ +inline bool isTerminal(int const* const conn, unsigned int size, int unitOpIdx) +{ + for (unsigned int i = 0; i < size; ++i) { - for (unsigned int i = 0; i < size; ++i) - { - if (conn[6*i] == unitOpIdx) - return false; - } - return true; + if (conn[6 * i] == unitOpIdx) + return false; } + return true; +} - /** - * @brief Returns whether one or more unit operations have multiple ports - * @param [in] models List of unit operation models - * @return @c true if the list contains at least one unit operation model with multiple ports, @c false otherwise - */ - inline bool hasMultiPortUnits(const std::vector& models) +/** + * @brief Returns whether one or more unit operations have multiple ports + * @param [in] models List of unit operation models + * @return @c true if the list contains at least one unit operation model with multiple ports, @c false otherwise + */ +inline bool hasMultiPortUnits(const std::vector& models) +{ + for (cadet::IUnitOperation const* m : models) { - for (cadet::IUnitOperation const* m : models) - { - if ((m->numInletPorts() > 1) || (m->numOutletPorts() > 1)) - return true; - } - return false; + if ((m->numInletPorts() > 1) || (m->numOutletPorts() > 1)) + return true; } + return false; +} - template - std::string toSciString(const T val, const int prec = 6) - { - std::ostringstream out; - out << std::scientific << std::setprecision(prec) << val; - return out.str(); - } +template std::string toSciString(const T val, const int prec = 6) +{ + std::ostringstream out; + out << std::scientific << std::setprecision(prec) << val; + return out.str(); } +} // namespace namespace cadet { @@ -95,7 +95,9 @@ namespace cadet namespace model { -ModelSystem::ModelSystem() : _jacNF(nullptr), _jacFN(nullptr), _jacActiveFN(nullptr), _curSwitchIndex(0), _tempState(nullptr), _initState(0, 0.0), _initStateDot(0, 0.0) +ModelSystem::ModelSystem() + : _jacNF(nullptr), _jacFN(nullptr), _jacActiveFN(nullptr), _curSwitchIndex(0), _tempState(nullptr), + _initState(0, 0.0), _initStateDot(0, 0.0) { } @@ -117,7 +119,8 @@ void ModelSystem::addModel(IModel* unitOp) { // Check for unique unit operation id if (indexOfUnitOp(_models, unitOp->unitOperationId()) < _models.size()) - throw InvalidParameterException("Cannot add model because of already existing unit operation id " + std::to_string(unitOp->unitOperationId())); + throw InvalidParameterException("Cannot add model because of already existing unit operation id " + + std::to_string(unitOp->unitOperationId())); IUnitOperation* const uo = static_cast(unitOp); _models.push_back(uo); @@ -172,7 +175,8 @@ unsigned int ModelSystem::numModels() const CADET_NOEXCEPT void ModelSystem::removeModel(IModel const* unitOp) { - std::vector::iterator it = std::find(_models.begin(), _models.end(), static_cast(unitOp)); + std::vector::iterator it = + std::find(_models.begin(), _models.end(), static_cast(unitOp)); if (it != _models.end()) _models.erase(it); } @@ -286,7 +290,7 @@ std::tuple ModelSystem::getModelStateOffsets(UnitOpI for (int i = 0; i < _models.size(); ++i) { if (_models[i]->unitOperationId() == unitOp) - return std::make_tuple(_dofOffset[i], _dofOffset[i+1]); + return std::make_tuple(_dofOffset[i], _dofOffset[i + 1]); } return std::make_tuple(-1u, -1u); @@ -303,11 +307,11 @@ bool ModelSystem::usesAD() const CADET_NOEXCEPT } /** -* @brief Create data structures to keep track of entries and locations in the state vector -* @details Three data structures are created. One keeps track of the offsets to each unit operation. -* The second keeps track of the size of each unit operation. The third is mapping from -* unit operation and component index to unique inlet DOF index. -*/ + * @brief Create data structures to keep track of entries and locations in the state vector + * @details Three data structures are created. One keeps track of the offsets to each unit operation. + * The second keeps track of the size of each unit operation. The third is mapping from + * unit operation and component index to unique inlet DOF index. + */ void ModelSystem::rebuildInternalDataStructures() { // Calculate array with DOF offsets @@ -316,7 +320,7 @@ void ModelSystem::rebuildInternalDataStructures() _conDofOffset.clear(); // The additional entry holds the offset for the superstructure - _dofOffset.reserve(_models.size()+1); + _dofOffset.reserve(_models.size() + 1); _dofs.reserve(_models.size() + 1); _conDofOffset.reserve(_models.size() + 1); @@ -337,8 +341,8 @@ void ModelSystem::rebuildInternalDataStructures() _conDofOffset.push_back(conTotalDof); /* - A mapping is required that turns a local model, port index, and component index into the location of the inlet DOF in - the global state vector. Some unit operations do not have inlet DOFs (e.g., inlet unit operation). Hence, + A mapping is required that turns a local model, port index, and component index into the location of the inlet + DOF in the global state vector. Some unit operations do not have inlet DOFs (e.g., inlet unit operation). Hence, a map is constructed which converts local indices into global ones. */ @@ -348,15 +352,15 @@ void ModelSystem::rebuildInternalDataStructures() for (unsigned int i = 0; i < numModels(); ++i) { IUnitOperation const* const model = _models[i]; - - //Only unit operations with an inlet have dedicated inlet DOFs + + // Only unit operations with an inlet have dedicated inlet DOFs if (model->hasInlet()) { for (unsigned int port = 0; port < model->numInletPorts(); ++port) { for (unsigned int comp = 0; comp < model->numComponents(); ++comp) { - _couplingIdxMap.insert({ std::make_tuple(i, port, comp), counter }); + _couplingIdxMap.insert({std::make_tuple(i, port, comp), counter}); ++counter; } } @@ -372,16 +376,16 @@ void ModelSystem::rebuildInternalDataStructures() } /** -* @brief Allocates memory for the superstructure matrices -* @details How many connections each unit has determines how much memory has to be allocated for the coupling matrices. -* This function walks the connections over the entire simulation in order to determine the maximum number of -* connections over the whole simulation which governs the number of entries in the sparse superstructure coupling -* matrices. Finally, the required memory is allocated in the matrices. -*/ + * @brief Allocates memory for the superstructure matrices + * @details How many connections each unit has determines how much memory has to be allocated for the coupling matrices. + * This function walks the connections over the entire simulation in order to determine the maximum number of + * connections over the whole simulation which governs the number of entries in the sparse superstructure + * coupling matrices. Finally, the required memory is allocated in the matrices. + */ void ModelSystem::allocateSuperStructMatrices() { // Step 1: Calculate number of connections per unit operation per valve switch - // We record the number of outgoing connections (i.e., components) which act as + // We record the number of outgoing connections (i.e., components) which act as // sources in the bottom macro-row of the superstructure const unsigned int nModels = numModels(); std::vector sourcesPerUnitOpPerConfig(nModels * _switchSectionIndex.size(), 0u); @@ -401,7 +405,8 @@ void ModelSystem::allocateSuperStructMatrices() if (portSource == -1) { if (compSource == -1) - sourcesPerUnitOpPerConfig[nModels * idx + uoSource] += model->numOutletPorts() * model->numComponents(); + sourcesPerUnitOpPerConfig[nModels * idx + uoSource] += + model->numOutletPorts() * model->numComponents(); else sourcesPerUnitOpPerConfig[nModels * idx + uoSource] += model->numOutletPorts(); } @@ -437,7 +442,8 @@ void ModelSystem::allocateSuperStructMatrices() _jacActiveFN[i].resize(numOutgoing[i]); // Right macro-column - // Each unit operation has inlets equal to numComponents * numInletPorts so long as the unit operation has an inlet + // Each unit operation has inlets equal to numComponents * numInletPorts so long as the unit operation has an + // inlet IUnitOperation const* const model = _models[i]; if (model->hasInlet()) _jacNF[i].resize(model->numComponents() * model->numInletPorts()); @@ -495,7 +501,8 @@ bool ModelSystem::configureModelDiscretization(IParameterProvider& paramProvider delete func; success = false; - LOG(Error) << "Failed to configure external source " << i << " (" << extType << "), source is ignored"; + LOG(Error) << "Failed to configure external source " << i << " (" << extType + << "), source is ignored"; } } else @@ -504,7 +511,8 @@ bool ModelSystem::configureModelDiscretization(IParameterProvider& paramProvider _extFunctions.push_back(nullptr); success = false; - LOG(Error) << "Failed to create external source " << i << " as type " << extType << " is unknown, source is ignored"; + LOG(Error) << "Failed to create external source " << i << " as type " << extType + << " is unknown, source is ignored"; } paramProvider.popScope(); @@ -531,13 +539,13 @@ bool ModelSystem::configureModelDiscretization(IParameterProvider& paramProvider paramProvider.popScope(); // Initialize and configure GMRES for solving the Schur-complement - _gmres.initialize(numCouplingDOF(), maxKrylov, linalg::toOrthogonalization(gsType), maxRestarts); + _gmres.initialize(numCouplingDOF(), maxKrylov, linalg::toOrthogonalization(gsType), maxRestarts); // Allocate tempState vector delete[] _tempState; _tempState = new double[numDofs()]; -// _tempSchur = new double[*std::max_element(_dofs.begin(), _dofs.end())]; + // _tempSchur = new double[*std::max_element(_dofs.begin(), _dofs.end())]; _flowRateIn.reserve(totalNumInletPorts(), _models.size()); _flowRateOut.reserve(totalNumOutletPorts(), _models.size()); @@ -572,7 +580,8 @@ bool ModelSystem::configureModelDiscretization(IParameterProvider& paramProvider LOG(Debug) << "Num units " << _models.size() << " max ID " << maxUnitOperationId() << " total DOF " << numDofs(); LOG(Debug) << "Unit op state vector size: uoDOFs = " << log::VectorPtr(_dofs.data(), _dofs.size()); - LOG(Debug) << "Unit op state vector offsets: uoOffset = " << log::VectorPtr(_dofOffset.data(), _dofOffset.size()); + LOG(Debug) << "Unit op state vector offsets: uoOffset = " + << log::VectorPtr(_dofOffset.data(), _dofOffset.size()); #endif @@ -645,7 +654,7 @@ void ModelSystem::configureSwitches(IParameterProvider& paramProvider) paramProvider.pushScope("connections"); const unsigned int numSwitches = paramProvider.getInt("NSWITCHES"); - + // TODO Improve those very conservative size estimates _switchSectionIndex.clear(); _switchSectionIndex.reserve(numSwitches); @@ -697,7 +706,8 @@ void ModelSystem::configureSwitches(IParameterProvider& paramProvider) _switchSectionIndex.push_back(paramProvider.getInt("SECTION")); if ((i > 0) && (_switchSectionIndex.back() <= _switchSectionIndex[_switchSectionIndex.size() - 2])) - throw InvalidParameterException("SECTION index has to be monotonically increasing (switch " + std::to_string(i) + ")"); + throw InvalidParameterException("SECTION index has to be monotonically increasing (switch " + + std::to_string(i) + ")"); std::vector connFlow = paramProvider.getDoubleArray("CONNECTIONS"); if (!conListHasPorts) @@ -705,12 +715,16 @@ void ModelSystem::configureSwitches(IParameterProvider& paramProvider) if (_hasDynamicFlowRates) { if ((connFlow.size() % 8) != 0) - throw InvalidParameterException("CONNECTIONS matrix has to have 8 columns if CONNECTIONS_INCLUDE_PORTS is disabled and CONNECTIONS_INCLUDE_DYNAMIC_FLOW is enabled"); + throw InvalidParameterException( + "CONNECTIONS matrix has to have 8 columns if CONNECTIONS_INCLUDE_PORTS is disabled and " + "CONNECTIONS_INCLUDE_DYNAMIC_FLOW is enabled"); } else { if ((connFlow.size() % 5) != 0) - throw InvalidParameterException("CONNECTIONS matrix has to have 5 columns if CONNECTIONS_INCLUDE_PORTS is disabled and CONNECTIONS_INCLUDE_DYNAMIC_FLOW is disabled"); + throw InvalidParameterException( + "CONNECTIONS matrix has to have 5 columns if CONNECTIONS_INCLUDE_PORTS is disabled and " + "CONNECTIONS_INCLUDE_DYNAMIC_FLOW is disabled"); } addDefaultPortsToConnectionList(connFlow); @@ -719,7 +733,8 @@ void ModelSystem::configureSwitches(IParameterProvider& paramProvider) if (!_hasDynamicFlowRates) { if ((connFlow.size() % 7) != 0) - throw InvalidParameterException("CONNECTIONS matrix has to have 5 or 7 columns if CONNECTIONS_INCLUDE_DYNAMIC_FLOW is disabled"); + throw InvalidParameterException( + "CONNECTIONS matrix has to have 5 or 7 columns if CONNECTIONS_INCLUDE_DYNAMIC_FLOW is disabled"); addDefaultDynamicFlowRatesToConnectionList(connFlow); } @@ -745,10 +760,14 @@ void ModelSystem::configureSwitches(IParameterProvider& paramProvider) _flowRatesLin.pushBack(frLin[0]); _flowRatesQuad.pushBack(frQuad[0]); _flowRatesCub.pushBack(frCub[0]); - _parameters[makeParamId(flowHash, UnitOpIndep, conn[2], conn[3], conn[0], conn[1], _switchSectionIndex.back())] = _flowRates.back(); - _parameters[makeParamId(flowHashLin, UnitOpIndep, conn[2], conn[3], conn[0], conn[1], _switchSectionIndex.back())] = _flowRatesLin.back(); - _parameters[makeParamId(flowHashQuad, UnitOpIndep, conn[2], conn[3], conn[0], conn[1], _switchSectionIndex.back())] = _flowRatesQuad.back(); - _parameters[makeParamId(flowHashCub, UnitOpIndep, conn[2], conn[3], conn[0], conn[1], _switchSectionIndex.back())] = _flowRatesCub.back(); + _parameters[makeParamId(flowHash, UnitOpIndep, conn[2], conn[3], conn[0], conn[1], + _switchSectionIndex.back())] = _flowRates.back(); + _parameters[makeParamId(flowHashLin, UnitOpIndep, conn[2], conn[3], conn[0], conn[1], + _switchSectionIndex.back())] = _flowRatesLin.back(); + _parameters[makeParamId(flowHashQuad, UnitOpIndep, conn[2], conn[3], conn[0], conn[1], + _switchSectionIndex.back())] = _flowRatesQuad.back(); + _parameters[makeParamId(flowHashCub, UnitOpIndep, conn[2], conn[3], conn[0], conn[1], + _switchSectionIndex.back())] = _flowRatesCub.back(); for (std::size_t j = 1; j < fr.size(); ++j) { _flowRates.pushBackInLastSlice(fr[j]); @@ -760,7 +779,8 @@ void ModelSystem::configureSwitches(IParameterProvider& paramProvider) bool found = false; for (unsigned int k = 0; k < j; ++k) { - if ((conn[6*k] == conn[6*j]) && (conn[6*k+1] == conn[6*j+1]) && (conn[6*k+2] == conn[6*j+2]) && (conn[6*k+3] == conn[6*j+3])) + if ((conn[6 * k] == conn[6 * j]) && (conn[6 * k + 1] == conn[6 * j + 1]) && + (conn[6 * k + 2] == conn[6 * j + 2]) && (conn[6 * k + 3] == conn[6 * j + 3])) { found = true; break; @@ -770,10 +790,14 @@ void ModelSystem::configureSwitches(IParameterProvider& paramProvider) // Only register the first occurrence of a flow parameter if (!found) { - _parameters[makeParamId(flowHash, UnitOpIndep, conn[6*j+2], conn[6*j+3], conn[6*j], conn[6*j+1], _switchSectionIndex.back())] = (_flowRates.back() + j); - _parameters[makeParamId(flowHashLin, UnitOpIndep, conn[6*j+2], conn[6*j+3], conn[6*j], conn[6*j+1], _switchSectionIndex.back())] = (_flowRatesLin.back() + j); - _parameters[makeParamId(flowHashQuad, UnitOpIndep, conn[6*j+2], conn[6*j+3], conn[6*j], conn[6*j+1], _switchSectionIndex.back())] = (_flowRatesQuad.back() + j); - _parameters[makeParamId(flowHashCub, UnitOpIndep, conn[6*j+2], conn[6*j+3], conn[6*j], conn[6*j+1], _switchSectionIndex.back())] = (_flowRatesCub.back() + j); + _parameters[makeParamId(flowHash, UnitOpIndep, conn[6 * j + 2], conn[6 * j + 3], conn[6 * j], + conn[6 * j + 1], _switchSectionIndex.back())] = (_flowRates.back() + j); + _parameters[makeParamId(flowHashLin, UnitOpIndep, conn[6 * j + 2], conn[6 * j + 3], conn[6 * j], + conn[6 * j + 1], _switchSectionIndex.back())] = (_flowRatesLin.back() + j); + _parameters[makeParamId(flowHashQuad, UnitOpIndep, conn[6 * j + 2], conn[6 * j + 3], conn[6 * j], + conn[6 * j + 1], _switchSectionIndex.back())] = (_flowRatesQuad.back() + j); + _parameters[makeParamId(flowHashCub, UnitOpIndep, conn[6 * j + 2], conn[6 * j + 3], conn[6 * j], + conn[6 * j + 1], _switchSectionIndex.back())] = (_flowRatesCub.back() + j); } } } @@ -795,14 +819,16 @@ void ModelSystem::configureSwitches(IParameterProvider& paramProvider) else if (_linearSolutionMode == 2) { // Sequential solution method - - const util::SlicedVector adjList = graph::adjacencyListFromConnectionList(conn.data(), _models.size(), conn.size() / 6); + + const util::SlicedVector adjList = + graph::adjacencyListFromConnectionList(conn.data(), _models.size(), conn.size() / 6); std::vector topoOrder; const bool hasCycles = graph::topologicalSort(adjList, topoOrder); if (hasCycles) { - LOG(Warning) << "Detected cycle in connections of switch " << i << ", reverting to parallel solution method"; + LOG(Warning) << "Detected cycle in connections of switch " << i + << ", reverting to parallel solution method"; _linearModelOrdering.pushBackSlice(0); } else @@ -817,7 +843,7 @@ void ModelSystem::configureSwitches(IParameterProvider& paramProvider) // Auto detect solution method // TODO: Add heuristic for number and complexity of unit operations - + // Simple heuristic: At least 25 models (regardless of complexity) => Parallelize if (_models.size() >= 25) { @@ -827,7 +853,8 @@ void ModelSystem::configureSwitches(IParameterProvider& paramProvider) else { // Less than 25 models => Select depending on existence of cycles - const util::SlicedVector adjList = graph::adjacencyListFromConnectionList(conn.data(), _models.size(), conn.size() / 6); + const util::SlicedVector adjList = + graph::adjacencyListFromConnectionList(conn.data(), _models.size(), conn.size() / 6); std::vector topoOrder; const bool hasCycles = graph::topologicalSort(adjList, topoOrder); @@ -839,7 +866,8 @@ void ModelSystem::configureSwitches(IParameterProvider& paramProvider) else { _linearModelOrdering.pushBackSlice(topoOrder); - LOG(Debug) << "Select sequential solution method for switch " << i << " (no cycles found, less than 6 models)"; + LOG(Debug) << "Select sequential solution method for switch " << i + << " (no cycles found, less than 6 models)"; LOG(Debug) << "Reversed ordering: " << topoOrder; } } @@ -885,7 +913,6 @@ void ModelSystem::addDefaultPortsToConnectionList(std::vector& conList) conList = std::move(newList); } - /** * @brief Add default dynamic flow rates to connection list * @details Adds linear, quadratic, and cubic flow rate coefficients to the list @@ -942,13 +969,16 @@ void ModelSystem::readLinearSolutionMode(IParameterProvider& paramProvider) * to be pre-allocated (same number of rows as @p conn). * @param [out] flowRatesLin Vector with linear flow rate coefficients for each connection in the list. It is assumed * to be pre-allocated (same number of rows as @p conn). - * @param [out] flowRatesQuad Vector with quadratic flow rate coefficients for each connection in the list. It is assumed - * to be pre-allocated (same number of rows as @p conn). + * @param [out] flowRatesQuad Vector with quadratic flow rate coefficients for each connection in the list. It is + * assumed to be pre-allocated (same number of rows as @p conn). * @param [out] flowRatesCub Vector with cubic flow rate coefficients for each connection in the list. It is assumed * to be pre-allocated (same number of rows as @p conn). * @param [in] idxSwitch Index of the valve switch that corresponds to this connection list */ -void ModelSystem::checkConnectionList(const std::vector& conn, std::vector& connOnly, std::vector& flowRates, std::vector& flowRatesLin, std::vector& flowRatesQuad, std::vector& flowRatesCub, unsigned int idxSwitch) const +void ModelSystem::checkConnectionList(const std::vector& conn, std::vector& connOnly, + std::vector& flowRates, std::vector& flowRatesLin, + std::vector& flowRatesQuad, std::vector& flowRatesCub, + unsigned int idxSwitch) const { std::vector totalInflow(_models.size(), 0.0); std::vector totalInflowLin(_models.size(), 0.0); @@ -962,72 +992,114 @@ void ModelSystem::checkConnectionList(const std::vector& conn, std::vect for (std::size_t i = 0; i < conn.size() / 10; ++i) { // Extract current connection - int uoSource = static_cast(conn[10*i]); - int uoDest = static_cast(conn[10*i+1]); - const int portSource = static_cast(conn[10*i+2]); - const int portDest = static_cast(conn[10*i+3]); - const int compSource = static_cast(conn[10*i+4]); - const int compDest = static_cast(conn[10*i+5]); - double fr = conn[10*i + 6]; - double frLin = conn[10*i + 7]; - double frQuad = conn[10*i + 8]; - double frCub = conn[10*i + 9]; + int uoSource = static_cast(conn[10 * i]); + int uoDest = static_cast(conn[10 * i + 1]); + const int portSource = static_cast(conn[10 * i + 2]); + const int portDest = static_cast(conn[10 * i + 3]); + const int compSource = static_cast(conn[10 * i + 4]); + const int compDest = static_cast(conn[10 * i + 5]); + double fr = conn[10 * i + 6]; + double frLin = conn[10 * i + 7]; + double frQuad = conn[10 * i + 8]; + double frCub = conn[10 * i + 9]; if (uoSource < 0) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + "): Source unit operation id has to be at least 0 in connection"); + throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + + std::to_string(i) + + "): Source unit operation id has to be at least 0 in connection"); if (uoDest < 0) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + "): Destination unit operation id has to be at least 0 in connection"); + throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + + std::to_string(i) + + "): Destination unit operation id has to be at least 0 in connection"); // Convert to index uoSource = indexOfUnitOp(_models, uoSource); uoDest = indexOfUnitOp(_models, uoDest); if (static_cast(uoSource) >= _models.size()) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + "): Source unit operation id " + std::to_string(uoSource) + " not found in connection"); + throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + + std::to_string(i) + "): Source unit operation id " + + std::to_string(uoSource) + " not found in connection"); if (static_cast(uoDest) >= _models.size()) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + "): Destination unit operation id " + std::to_string(uoDest) + " not found in connection"); + throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + + std::to_string(i) + "): Destination unit operation id " + + std::to_string(uoDest) + " not found in connection"); // Check if unit operations have inlets and outlets if (!_models[uoSource]->hasOutlet()) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + "): Source unit operation " + std::to_string(_models[uoSource]->unitOperationId()) + " does not have an outlet"); + throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + + std::to_string(i) + "): Source unit operation " + + std::to_string(_models[uoSource]->unitOperationId()) + + " does not have an outlet"); if (!_models[uoDest]->hasInlet()) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + "): Source unit operation " + std::to_string(_models[uoDest]->unitOperationId()) + " does not have an inlet"); + throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + + std::to_string(i) + "): Source unit operation " + + std::to_string(_models[uoDest]->unitOperationId()) + + " does not have an inlet"); // Check port indices if ((portSource >= 0) && (static_cast(portSource) >= _models[uoSource]->numOutletPorts())) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + "): Source port index (" + std::to_string(portSource) + ") exceeds number of outlet ports (" + std::to_string(_models[uoSource]->numOutletPorts()) + ")"); + throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + + std::to_string(i) + "): Source port index (" + std::to_string(portSource) + + ") exceeds number of outlet ports (" + + std::to_string(_models[uoSource]->numOutletPorts()) + ")"); if ((portDest >= 0) && (static_cast(portDest) >= _models[uoDest]->numInletPorts())) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + "): Destination port index (" + std::to_string(portDest) + ") exceeds number of inlet ports (" + std::to_string(_models[uoDest]->numInletPorts()) + ")"); + throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + + std::to_string(i) + "): Destination port index (" + + std::to_string(portDest) + ") exceeds number of inlet ports (" + + std::to_string(_models[uoDest]->numInletPorts()) + ")"); if (((portSource < 0) && (portDest >= 0)) || ((portSource >= 0) && (portDest < 0))) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + "): Only source or destination (not both) are set to connect all ports in connection from unit " - + std::to_string(_models[uoSource]->unitOperationId()) + " to " + std::to_string(_models[uoDest]->unitOperationId())); - - if ((portSource < 0) && (portDest < 0) && (_models[uoSource]->numOutletPorts() != _models[uoDest]->numInletPorts())) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + "): Number of ports not equal when connecting all ports from unit " - + std::to_string(_models[uoSource]->unitOperationId()) + " (" + std::to_string(_models[uoSource]->numOutletPorts()) + " ports) to " + std::to_string(_models[uoDest]->unitOperationId()) + " (" + std::to_string(_models[uoDest]->numInletPorts()) + " ports)"); + throw InvalidParameterException( + "In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + + "): Only source or destination (not both) are set to connect all ports in connection from unit " + + std::to_string(_models[uoSource]->unitOperationId()) + " to " + + std::to_string(_models[uoDest]->unitOperationId())); + + if ((portSource < 0) && (portDest < 0) && + (_models[uoSource]->numOutletPorts() != _models[uoDest]->numInletPorts())) + throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + + std::to_string(i) + + "): Number of ports not equal when connecting all ports from unit " + + std::to_string(_models[uoSource]->unitOperationId()) + " (" + + std::to_string(_models[uoSource]->numOutletPorts()) + " ports) to " + + std::to_string(_models[uoDest]->unitOperationId()) + " (" + + std::to_string(_models[uoDest]->numInletPorts()) + " ports)"); // Check component indices if ((compSource >= 0) && (static_cast(compSource) >= _models[uoSource]->numComponents())) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + "): Source component index (" + std::to_string(compSource) + ") exceeds number of components (" + std::to_string(_models[uoSource]->numComponents()) + ")"); + throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + + std::to_string(i) + "): Source component index (" + + std::to_string(compSource) + ") exceeds number of components (" + + std::to_string(_models[uoSource]->numComponents()) + ")"); if ((compDest >= 0) && (static_cast(compDest) >= _models[uoDest]->numComponents())) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + "): Destination component index (" + std::to_string(compDest) + ") exceeds number of components (" + std::to_string(_models[uoDest]->numComponents()) + ")"); + throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + + std::to_string(i) + "): Destination component index (" + + std::to_string(compDest) + ") exceeds number of components (" + + std::to_string(_models[uoDest]->numComponents()) + ")"); if (((compSource < 0) && (compDest >= 0)) || ((compSource >= 0) && (compDest < 0))) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + "): Only source or destination (not both) are set to connect all components in connection from unit " - + std::to_string(_models[uoSource]->unitOperationId()) + " to " + std::to_string(_models[uoDest]->unitOperationId())); + throw InvalidParameterException( + "In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + + "): Only source or destination (not both) are set to connect all components in connection from unit " + + std::to_string(_models[uoSource]->unitOperationId()) + " to " + + std::to_string(_models[uoDest]->unitOperationId())); + + if ((compSource < 0) && (compDest < 0) && + (_models[uoSource]->numComponents() != _models[uoDest]->numComponents())) + throw InvalidParameterException( + "In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + + "): Number of components not equal when connecting all components from unit " + + std::to_string(_models[uoSource]->unitOperationId()) + " to " + + std::to_string(_models[uoDest]->unitOperationId())); - if ((compSource < 0) && (compDest < 0) && (_models[uoSource]->numComponents() != _models[uoDest]->numComponents())) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + " row " + std::to_string(i) + "): Number of components not equal when connecting all components from unit " - + std::to_string(_models[uoSource]->unitOperationId()) + " to " + std::to_string(_models[uoDest]->unitOperationId())); - // Add connection to index matrix - connOnly[6*i] = uoSource; - connOnly[6*i+1] = uoDest; - connOnly[6*i+2] = portSource; - connOnly[6*i+3] = portDest; - connOnly[6*i+4] = compSource; - connOnly[6*i+5] = compDest; + connOnly[6 * i] = uoSource; + connOnly[6 * i + 1] = uoDest; + connOnly[6 * i + 2] = portSource; + connOnly[6 * i + 3] = portDest; + connOnly[6 * i + 4] = compSource; + connOnly[6 * i + 5] = compDest; // Add flow rate of connection to balance @@ -1035,13 +1107,14 @@ void ModelSystem::checkConnectionList(const std::vector& conn, std::vect bool found = false; for (unsigned int j = 0; j < i; ++j) { - if ((conn[10*j] == uoSource) && (uoDest == conn[10*j+1]) && (conn[10*j+2] == portSource) && (portDest == conn[10*j+3])) + if ((conn[10 * j] == uoSource) && (uoDest == conn[10 * j + 1]) && (conn[10 * j + 2] == portSource) && + (portDest == conn[10 * j + 3])) { // Take flow rate that appears first - fr = conn[10*j+6]; - frLin = conn[10*j+7]; - frQuad = conn[10*j+8]; - frCub = conn[10*j+9]; + fr = conn[10 * j + 6]; + frLin = conn[10 * j + 7]; + frQuad = conn[10 * j + 8]; + frCub = conn[10 * j + 9]; found = true; break; } @@ -1088,19 +1161,29 @@ void ModelSystem::checkConnectionList(const std::vector& conn, std::vect // Check balance const double diff = std::abs(totalInflow[i] - totalOutflow[i]); if ((diff >= 1e-15) || (diff > 1e-15 * std::abs(totalOutflow[i]))) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + "): Flow rate balance is not closed for unit operation " + std::to_string(i) + ", imbalanced by " + toSciString(diff)); + throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + + "): Flow rate balance is not closed for unit operation " + + std::to_string(i) + ", imbalanced by " + toSciString(diff)); const double diffLin = std::abs(totalInflowLin[i] - totalOutflowLin[i]); if ((diffLin >= 1e-15) || (diffLin > 1e-15 * std::abs(totalOutflowLin[i]))) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + "): Linear flow rate coefficient balance is not closed for unit operation " + std::to_string(i) + ", imbalanced by " + toSciString(diffLin)); + throw InvalidParameterException( + "In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + + "): Linear flow rate coefficient balance is not closed for unit operation " + std::to_string(i) + + ", imbalanced by " + toSciString(diffLin)); const double diffQuad = std::abs(totalInflowQuad[i] - totalOutflowQuad[i]); if ((diffQuad >= 1e-15) || (diffQuad > 1e-15 * std::abs(totalOutflowQuad[i]))) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + "): Quadratic flow rate coefficient balance is not closed for unit operation " + std::to_string(i) + ", imbalanced by " + toSciString(diffQuad)); + throw InvalidParameterException( + "In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + + "): Quadratic flow rate coefficient balance is not closed for unit operation " + std::to_string(i) + + ", imbalanced by " + toSciString(diffQuad)); const double diffCub = std::abs(totalInflowCub[i] - totalOutflowCub[i]); if ((diffCub >= 1e-15) || (diffCub > 1e-15 * std::abs(totalOutflowCub[i]))) - throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + "): Cubic flow rate coefficient balance is not closed for unit operation " + std::to_string(i) + ", imbalanced by " + toSciString(diffCub)); + throw InvalidParameterException("In CONNECTIONS matrix (switch " + std::to_string(idxSwitch) + + "): Cubic flow rate coefficient balance is not closed for unit operation " + + std::to_string(i) + ", imbalanced by " + toSciString(diffCub)); } // TODO: Check for conflicting entries @@ -1111,7 +1194,9 @@ std::unordered_map ModelSystem::getAllParameterValues() con { std::unordered_map data; std::transform(_parameters.begin(), _parameters.end(), std::inserter(data, data.end()), - [](const std::pair& p) { return std::make_pair(p.first, static_cast(*p.second)); }); + [](const std::pair& p) { + return std::make_pair(p.first, static_cast(*p.second)); + }); for (IUnitOperation* m : _models) { @@ -1157,8 +1242,7 @@ double ModelSystem::getParameterDouble(const ParameterId& pId) const return static_cast(*it->second); } -template -bool ModelSystem::setParameterImpl(const ParameterId& pId, const ParamType value) +template bool ModelSystem::setParameterImpl(const ParameterId& pId, const ParamType value) { // Filter by unit operation ID for (IUnitOperation* m : _models) @@ -1202,7 +1286,8 @@ bool ModelSystem::setParameter(const ParameterId& pId, double value) // Set value for all flow rates of the same connection (except for components) for (unsigned int i = 0; i < _connections.sliceSize(idxSwitch) / 6; ++i, ptrConn += 6, ++conRates) { - if ((ptrConn[2] != pId.component) || (ptrConn[3] != pId.particleType) || (ptrConn[0] != pId.boundState) || (ptrConn[1] != pId.reaction)) + if ((ptrConn[2] != pId.component) || (ptrConn[3] != pId.particleType) || + (ptrConn[0] != pId.boundState) || (ptrConn[1] != pId.reaction)) continue; conRates->setValue(value); @@ -1248,7 +1333,8 @@ void ModelSystem::setSensitiveParameterValue(const ParameterId& pId, double valu // Set value for all flow rates of the same connection (except for components) for (unsigned int i = 0; i < _connections.sliceSize(idxSwitch) / 6; ++i, ptrConn += 6, ++conRates) { - if ((ptrConn[2] != pId.component) || (ptrConn[3] != pId.particleType) || (ptrConn[0] != pId.boundState) || (ptrConn[1] != pId.reaction)) + if ((ptrConn[2] != pId.component) || (ptrConn[3] != pId.particleType) || + (ptrConn[0] != pId.boundState) || (ptrConn[1] != pId.reaction)) continue; conRates->setValue(value); @@ -1278,7 +1364,8 @@ bool ModelSystem::setSensitiveParameter(const ParameterId& pId, unsigned int adD auto paramHandle = _parameters.find(pId); if (paramHandle != _parameters.end()) { - LOG(Debug) << "Found parameter " << pId << " in ModelSystem: Dir " << adDirection << " is set to " << adValue; + LOG(Debug) << "Found parameter " << pId << " in ModelSystem: Dir " << adDirection << " is set to " + << adValue; // Register parameter and set AD seed / direction _sensParams.insert(paramHandle->second); @@ -1303,7 +1390,8 @@ bool ModelSystem::setSensitiveParameter(const ParameterId& pId, unsigned int adD // Set AD direction for all flow rates of the same connection (except for components) for (unsigned int i = 0; i < _connections.sliceSize(idxSwitch) / 6; ++i, ptrConn += 6, ++conRates) { - if ((ptrConn[2] != pId.component) || (ptrConn[3] != pId.particleType) || (ptrConn[0] != pId.boundState) || (ptrConn[1] != pId.reaction)) + if ((ptrConn[2] != pId.component) || (ptrConn[3] != pId.particleType) || + (ptrConn[0] != pId.boundState) || (ptrConn[1] != pId.reaction)) continue; conRates->setADValue(adDirection, adValue); @@ -1382,7 +1470,7 @@ void ModelSystem::prepareADvectors(const AdJacobianParams& adJac) const void ModelSystem::setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { for (IUnitOperation* m : _models) - m->setSectionTimes(secTimes, secContinuity, nSections); + m->setSectionTimes(secTimes, secContinuity, nSections); for (IExternalFunction* extFun : _extFunctions) extFun->setSectionTimes(secTimes, secContinuity, nSections); @@ -1394,12 +1482,13 @@ void ModelSystem::setSectionTimes(double const* secTimes, bool const* secContinu * These additional DOFs don't get an error tolerance from the user because he shouldn't be * aware of those (implementation detail). This function is responsible for calculating error * tolerances for these additional coupling DOFs. - * + * * @param [in] errorTol Pointer to array of error tolerances for system without coupling DOFs * @param [in] errorTolLength Length of @p errorTol array * @return Vector with error tolerances for additional coupling DOFs */ -std::vector ModelSystem::calculateErrorTolsForAdditionalDofs(double const* errorTol, unsigned int errorTolLength) +std::vector ModelSystem::calculateErrorTolsForAdditionalDofs(double const* errorTol, + unsigned int errorTolLength) { // Return empty vector since we don't have coupling DOFs, yet return std::vector(0, 0.0); @@ -1424,6 +1513,6 @@ void ModelSystem::setupParallelization(unsigned int numThreads) _threadLocalStorage.resize(numThreads, tlsSize); } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/ModelSystemImpl.hpp b/src/libcadet/model/ModelSystemImpl.hpp index 37819020b..634413052 100644 --- a/src/libcadet/model/ModelSystemImpl.hpp +++ b/src/libcadet/model/ModelSystemImpl.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the model system, which connects multiple unit operations. */ @@ -31,7 +31,7 @@ #include #ifdef CADET_PARALLELIZE - #include +#include #endif #include "ParallelSupport.hpp" @@ -53,7 +53,6 @@ namespace model class ModelSystem : public ISimulatableModel { public: - ModelSystem(); virtual ~ModelSystem() CADET_NOEXCEPT; @@ -84,7 +83,9 @@ class ModelSystem : public ISimulatableModel virtual bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper); virtual bool configure(IParameterProvider& paramProvider); - virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac); + virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac); virtual bool configureModel(IParameterProvider& paramProvider, unsigned int unitOpIdx); virtual bool hasParameter(const ParameterId& pId) const; @@ -105,28 +106,32 @@ class ModelSystem : public ISimulatableModel virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res); - virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac); + virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac); - virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac); + virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac); virtual double residualNorm(const SimulationTime& simTime, const ConstSimulationState& simState); - virtual int residualSensFwd(unsigned int nSens, const SimulationTime& simTime, - const ConstSimulationState& simState, double const* const res, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, - active* const adRes, double* const tmp1, double* const tmp2, double* const tmp3); + virtual int residualSensFwd(unsigned int nSens, const SimulationTime& simTime, const ConstSimulationState& simState, + double const* const res, const std::vector& yS, + const std::vector& ySdot, const std::vector& resS, + active* const adRes, double* const tmp1, double* const tmp2, double* const tmp3); virtual int residualSensFwdWithJacobian(unsigned int nSens, const SimulationTime& simTime, - const ConstSimulationState& simState, double const* const res, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, - const AdJacobianParams& adJac, double* const tmp1, double* const tmp2, double* const tmp3); + const ConstSimulationState& simState, double const* const res, + const std::vector& yS, + const std::vector& ySdot, const std::vector& resS, + const AdJacobianParams& adJac, double* const tmp1, double* const tmp2, + double* const tmp3); - virtual void residualSensFwdNorm(unsigned int nSens, const SimulationTime& simTime, - const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, double* const norms, - active* const adRes, double* const tmp); + virtual void residualSensFwdNorm(unsigned int nSens, const SimulationTime& simTime, + const ConstSimulationState& simState, const std::vector& yS, + const std::vector& ySdot, double* const norms, active* const adRes, + double* const tmp); virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState); + const ConstSimulationState& simState); virtual void prepareADvectors(const AdJacobianParams& adJac) const; @@ -135,80 +140,73 @@ class ModelSystem : public ISimulatableModel virtual void initializeSensitivityStates(const std::vector& vecSensY) const; - virtual void consistentInitialConditions(const SimulationTime& simTime, const SimulationState& simState, const AdJacobianParams& adJac, double errorTol); + virtual void consistentInitialConditions(const SimulationTime& simTime, const SimulationState& simState, + const AdJacobianParams& adJac, double errorTol); virtual void consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active* const adRes, active* const adY); - virtual void leanConsistentInitialConditions(const SimulationTime& simTime, const SimulationState& simState, const AdJacobianParams& adJac, double errorTol); + std::vector& vecSensY, std::vector& vecSensYdot, + active* const adRes, active* const adY); + virtual void leanConsistentInitialConditions(const SimulationTime& simTime, const SimulationState& simState, + const AdJacobianParams& adJac, double errorTol); virtual void leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active* const adRes, active* const adY); + std::vector& vecSensY, std::vector& vecSensYdot, + active* const adRes, active* const adY); virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections); virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut); - virtual std::vector calculateErrorTolsForAdditionalDofs(double const* errorTol, unsigned int errorTolLength); + virtual std::vector calculateErrorTolsForAdditionalDofs(double const* errorTol, + unsigned int errorTolLength); virtual void setupParallelization(unsigned int numThreads); #ifdef CADET_BENCHMARK_MODE virtual std::vector benchmarkTimings() const { - return std::vector({ - static_cast(numDofs()), - _timerResidual.totalElapsedTime(), - _timerResidualSens.totalElapsedTime(), - _timerConsistentInit.totalElapsedTime(), - _timerLinearAssemble.totalElapsedTime(), - _timerLinearSolve.totalElapsedTime(), - _timerMatVec.totalElapsedTime(), - static_cast(_gmres.numIterations()) - }); + return std::vector({static_cast(numDofs()), _timerResidual.totalElapsedTime(), + _timerResidualSens.totalElapsedTime(), _timerConsistentInit.totalElapsedTime(), + _timerLinearAssemble.totalElapsedTime(), _timerLinearSolve.totalElapsedTime(), + _timerMatVec.totalElapsedTime(), static_cast(_gmres.numIterations())}); } virtual char const* const* benchmarkDescriptions() const { - static const char* const desc[] = { - "DOFs", - "Residual", - "ResidualSens", - "ConsistentInit", - "LinearAssemble", - "LinearSolve", - "MatVec", - "NumGMRESIter" - }; + static const char* const desc[] = {"DOFs", "Residual", "ResidualSens", "ConsistentInit", + "LinearAssemble", "LinearSolve", "MatVec", "NumGMRESIter"}; return desc; } #endif - void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret); - void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double* ret); + void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, + double alpha, double beta, double* ret); + void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double* ret); #ifdef CADET_DEBUG void genJacobian(const SimulationTime& simTime, const ConstSimulationState& simState); - void genJacobian(unsigned int nSens, const SimulationTime& simTime, - const ConstSimulationState& simState, double const* const res, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, - active* const adRes, double* const tmp1, double* const tmp2, double* const tmp3); + void genJacobian(unsigned int nSens, const SimulationTime& simTime, const ConstSimulationState& simState, + double const* const res, const std::vector& yS, + const std::vector& ySdot, const std::vector& resS, active* const adRes, + double* const tmp1, double* const tmp2, double* const tmp3); #endif protected: - int linearSolveSequential(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState); + const ConstSimulationState& simState); int linearSolveParallel(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState); + const ConstSimulationState& simState); - int schurComplementMatrixVector(double const* x, double* z, double t, double alpha, double outerTol, double const* const weight, - const ConstSimulationState& simState) const; + int schurComplementMatrixVector(double const* x, double* z, double t, double alpha, double outerTol, + double const* const weight, const ConstSimulationState& simState) const; void configureSwitches(IParameterProvider& paramProvider); template - void residualConnectUnitOps(unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res) CADET_NOEXCEPT; - void solveCouplingDOF(double * const vec); + void residualConnectUnitOps(unsigned int secIdx, StateType const* const y, double const* const yDot, + ResidualType* const res) CADET_NOEXCEPT; + void solveCouplingDOF(double* const vec); void multiplyWithMacroJacobian(double const* yS, double alpha, double beta, double* ret); inline void multiplyWithMacroJacobian(double const* yS, double* ret) @@ -216,7 +214,8 @@ class ModelSystem : public ISimulatableModel multiplyWithMacroJacobian(yS, 1.0, 0.0, ret); } - int dResDpFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac); + int dResDpFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac); void readLinearSolutionMode(IParameterProvider& paramProvider); void rebuildInternalDataStructures(); @@ -233,79 +232,98 @@ class ModelSystem : public ISimulatableModel void subtractDresConDt(double t, double* dResConDt, double const* vecStateY); void subtractDresConDtDp(double t, unsigned int adDir, double* dResConDt, double const* vecStateY); - template - bool setParameterImpl(const ParameterId& pId, const ParamType value); + template bool setParameterImpl(const ParameterId& pId, const ParamType value); - void checkConnectionList(const std::vector& conn, std::vector& connOnly, std::vector& flow, std::vector& flowLin, std::vector& flowQuad, std::vector& flowCub, unsigned int idxSwitch) const; + void checkConnectionList(const std::vector& conn, std::vector& connOnly, std::vector& flow, + std::vector& flowLin, std::vector& flowQuad, std::vector& flowCub, + unsigned int idxSwitch) const; template - void consistentInitialConditionAlgorithm(const SimulationTime& simTime, const SimulationState& simState, const AdJacobianParams& adJac, double errorTol); + void consistentInitialConditionAlgorithm(const SimulationTime& simTime, const SimulationState& simState, + const AdJacobianParams& adJac, double errorTol); template void consistentInitialSensitivityAlgorithm(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active* const adRes, active* const adY); + std::vector& vecSensY, std::vector& vecSensYdot, + active* const adRes, active* const adY); template int residualSensFwdWithJacobianAlgorithm(unsigned int nSens, const SimulationTime& simTime, - const ConstSimulationState& simState, double const* const res, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, - const AdJacobianParams& adJac, double* const tmp1, double* const tmp2, double* const tmp3); + const ConstSimulationState& simState, double const* const res, + const std::vector& yS, + const std::vector& ySdot, const std::vector& resS, + const AdJacobianParams& adJac, double* const tmp1, double* const tmp2, + double* const tmp3); /** * @brief Returns the number of coupling DOFs * @details The number of coupling DOFs is given by the total number of inlet DOFs in the system. * @return Number of coupling DOFs */ - inline unsigned int numCouplingDOF() const CADET_NOEXCEPT { return _couplingIdxMap.size(); } + inline unsigned int numCouplingDOF() const CADET_NOEXCEPT + { + return _couplingIdxMap.size(); + } - std::vector _models; //!< Unit operation models + std::vector _models; //!< Unit operation models std::vector _extFunctions; //!< External functions - linalg::SparseMatrix* _jacNF; //!< Jacobian block connecting coupling DOF to inlets - linalg::SparseMatrix* _jacFN; //!< Jacobian block connecting outlets to coupling DOF - linalg::SparseMatrix* _jacActiveFN; //!< Jacobian block connecting outlets to coupling DOF - std::vector _dofOffset; //!< Vector with DOF offsets for each unit operation - std::vector _dofs; //!< Vector with DOF for each unit - std::vector _conDofOffset; //!< Vector with connection DOF offsets for each unit operation - util::SlicedVector _connections; //!< Vector of connection lists for each section - util::SlicedVector _flowRates; //!< Vector of connection flow rates for each section - util::SlicedVector _flowRatesLin; //!< Vector of linear coefficients of connection flow rates for each section - util::SlicedVector _flowRatesQuad; //!< Vector of quadratic coefficients of connection flow rates for each section - util::SlicedVector _flowRatesCub; //!< Vector of cubic coefficients of connection flow rates for each section + linalg::SparseMatrix* _jacNF; //!< Jacobian block connecting coupling DOF to inlets + linalg::SparseMatrix* _jacFN; //!< Jacobian block connecting outlets to coupling DOF + linalg::SparseMatrix* _jacActiveFN; //!< Jacobian block connecting outlets to coupling DOF + std::vector _dofOffset; //!< Vector with DOF offsets for each unit operation + std::vector _dofs; //!< Vector with DOF for each unit + std::vector _conDofOffset; //!< Vector with connection DOF offsets for each unit operation + util::SlicedVector _connections; //!< Vector of connection lists for each section + util::SlicedVector _flowRates; //!< Vector of connection flow rates for each section + util::SlicedVector + _flowRatesLin; //!< Vector of linear coefficients of connection flow rates for each section + util::SlicedVector + _flowRatesQuad; //!< Vector of quadratic coefficients of connection flow rates for each section + util::SlicedVector + _flowRatesCub; //!< Vector of cubic coefficients of connection flow rates for each section std::vector _switchSectionIndex; //!< Holds indices of sections where valves are switched - unsigned int _curSwitchIndex; //!< Current index in _switchSectionIndex list - util::SlicedVector _linearModelOrdering; //!< Dependency-consistent ordering of unit operation models for linear execution (for each switch) - int _linearSolutionMode; //!< Linear solution mode (0: automatic, 1: parallel, 2: sequential) + unsigned int _curSwitchIndex; //!< Current index in _switchSectionIndex list + util::SlicedVector _linearModelOrdering; //!< Dependency-consistent ordering of unit operation models for + //!< linear execution (for each switch) + int _linearSolutionMode; //!< Linear solution mode (0: automatic, 1: parallel, 2: sequential) mutable std::vector _errorIndicator; //!< Storage for return value of unit operation function calls - double* _tempState; //!< Temporary storage for the state vector + double* _tempState; //!< Temporary storage for the state vector util::SlicedVector _totalInletFlow; //!< Total flow rate into each inlet at the current section - util::SlicedVector _totalInletFlowLin; //!< Total linear flow rate coefficient into each inlet at the current section - util::SlicedVector _totalInletFlowQuad; //!< Total quadratic flow rate coefficient into each inlet at the current section - util::SlicedVector _totalInletFlowCub; //!< Total cubic flow rate coefficient into each inlet at the current section + util::SlicedVector + _totalInletFlowLin; //!< Total linear flow rate coefficient into each inlet at the current section + util::SlicedVector + _totalInletFlowQuad; //!< Total quadratic flow rate coefficient into each inlet at the current section + util::SlicedVector + _totalInletFlowCub; //!< Total cubic flow rate coefficient into each inlet at the current section util::SlicedVector _totalOutletFlow; //!< Total flow rate into each outlet at the current section - util::SlicedVector _totalOutletFlowLin; //!< Total linear flow rate coefficient into each outlet at the current section - util::SlicedVector _totalOutletFlowQuad; //!< Total quadratic flow rate coefficient into each outlet at the current section - util::SlicedVector _totalOutletFlowCub; //!< Total cubic flow rate coefficient into each outlet at the current section - util::SlicedVector _flowRateIn; //!< Cache for inlet port flow rates + util::SlicedVector + _totalOutletFlowLin; //!< Total linear flow rate coefficient into each outlet at the current section + util::SlicedVector + _totalOutletFlowQuad; //!< Total quadratic flow rate coefficient into each outlet at the current section + util::SlicedVector + _totalOutletFlowCub; //!< Total cubic flow rate coefficient into each outlet at the current section + util::SlicedVector _flowRateIn; //!< Cache for inlet port flow rates util::SlicedVector _flowRateOut; //!< Cache for outlet port flow rates - bool _hasDynamicFlowRates; //!< Determines whether dynamic flow rates are used or not - double _switchStartTime; //!< Start time of current switch + bool _hasDynamicFlowRates; //!< Determines whether dynamic flow rates are used or not + double _switchStartTime; //!< Start time of current switch - std::vector> _yStemp; //!< Needed to store offsets for unit operations - std::vector> _yStempDot; //!< Needed to store offsets for unit operations - std::vector> _resSTemp; //!< Needed to store offsets for unit operations + std::vector> _yStemp; //!< Needed to store offsets for unit operations + std::vector> _yStempDot; //!< Needed to store offsets for unit operations + std::vector> _resSTemp; //!< Needed to store offsets for unit operations - std::map, unsigned int> _couplingIdxMap; //!< Maps (UnitOpIdx, PortIdx, CompIdx) to local coupling DOF index + std::map, unsigned int> + _couplingIdxMap; //!< Maps (UnitOpIdx, PortIdx, CompIdx) to local coupling DOF index std::unordered_map _parameters; //!< Provides access to all parameters - std::unordered_set _sensParams; //!< Holds all parameters with activated AD directions + std::unordered_set _sensParams; //!< Holds all parameters with activated AD directions linalg::Gmres _gmres; //!< GMRES algorithm for the Schur-complement in linearSolve() - double _schurSafety; //!< Safety factor for Schur-complement solution + double _schurSafety; //!< Safety factor for Schur-complement solution std::vector _inOutModels; //!< Indices of unit operation models in _models that have inlet and outlet - std::vector _initState; //!< Initial state vector + std::vector _initState; //!< Initial state vector std::vector _initStateDot; //!< Initial time derivative state vector util::ThreadLocalStorage _threadLocalStorage; //!< Local storage for each thread @@ -326,4 +344,4 @@ class ModelSystem : public ISimulatableModel } // namespace model } // namespace cadet -#endif // LIBCADET_MODELSYSTEM_IMPL_HPP_ +#endif // LIBCADET_MODELSYSTEM_IMPL_HPP_ diff --git a/src/libcadet/model/ModelUtils.hpp b/src/libcadet/model/ModelUtils.hpp index 146ea5f05..f581aa083 100644 --- a/src/libcadet/model/ModelUtils.hpp +++ b/src/libcadet/model/ModelUtils.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines helper functions for models. */ @@ -119,9 +119,6 @@ inline unsigned int firstNonEmptyBoundStates(unsigned int const* const nBound, u return nStates; } - - - template void getAllParameterValues(std::unordered_map& data, const std::vector& items, bool singleItem) { @@ -141,10 +138,10 @@ void getAllParameterValues(std::unordered_map& data, const { for (T const* bm : items) { - if (!bm) - continue; + if (!bm) + continue; - const std::unordered_map localData = bm->getAllParameterValues(); + const std::unordered_map localData = bm->getAllParameterValues(); for (const std::pair& val : localData) data[val.first] = val.second; } @@ -174,10 +171,10 @@ bool getParameterDouble(const ParameterId& pId, const std::vector& items, bo { for (T* bm : items) { - if (!bm) - continue; + if (!bm) + continue; - active const* const val = bm->getParameter(pId); + active const* const val = bm->getParameter(pId); if (val) { out = static_cast(*val); @@ -190,8 +187,7 @@ bool getParameterDouble(const ParameterId& pId, const std::vector& items, bo return false; } -template -bool hasParameter(const ParameterId& pId, const std::vector& items, bool singleItem) +template bool hasParameter(const ParameterId& pId, const std::vector& items, bool singleItem) { if (items.empty()) return false; @@ -237,7 +233,8 @@ bool setParameter(const ParameterId& pId, param_t value, const std::vector& } template -bool setSensitiveParameterValue(const ParameterId& pId, double value, const std::unordered_set& sensParams, const std::vector& items, bool singleItem) +bool setSensitiveParameterValue(const ParameterId& pId, double value, const std::unordered_set& sensParams, + const std::vector& items, bool singleItem) { if (items.empty()) return false; @@ -258,8 +255,8 @@ bool setSensitiveParameterValue(const ParameterId& pId, double value, const std: { for (T* bm : items) { - if (!bm) - continue; + if (!bm) + continue; active* const val = bm->getParameter(pId); if (val && contains(sensParams, val)) @@ -274,7 +271,8 @@ bool setSensitiveParameterValue(const ParameterId& pId, double value, const std: } template -bool setSensitiveParameter(const ParameterId& pId, unsigned int adDirection, double adValue, std::unordered_set& sensParams, const std::vector& items, bool singleItem) +bool setSensitiveParameter(const ParameterId& pId, unsigned int adDirection, double adValue, + std::unordered_set& sensParams, const std::vector& items, bool singleItem) { if (items.empty()) return false; @@ -314,8 +312,7 @@ bool setSensitiveParameter(const ParameterId& pId, unsigned int adDirection, dou return false; } - } // namespace model } // namespace cadet -#endif // LIBCADET_MODELUTILS_HPP_ +#endif // LIBCADET_MODELUTILS_HPP_ diff --git a/src/libcadet/model/MultiChannelTransportModel-InitialConditions.cpp b/src/libcadet/model/MultiChannelTransportModel-InitialConditions.cpp index d428ee8f6..d81017c9a 100644 --- a/src/libcadet/model/MultiChannelTransportModel-InitialConditions.cpp +++ b/src/libcadet/model/MultiChannelTransportModel-InitialConditions.cpp @@ -27,9 +27,11 @@ namespace cadet namespace model { -int MultiChannelTransportModel::multiplexInitialConditions(const cadet::ParameterId& pId, unsigned int adDirection, double adValue) +int MultiChannelTransportModel::multiplexInitialConditions(const cadet::ParameterId& pId, unsigned int adDirection, + double adValue) { - if (pId.name == hashString("INIT_C") && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep)) + if (pId.name == hashString("INIT_C") && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && + (pId.particleType == ParTypeIndep) && (pId.component != CompIndep)) { if ((pId.reaction == ReactionIndep) && _singleRadiusInitC) { @@ -56,7 +58,8 @@ int MultiChannelTransportModel::multiplexInitialConditions(const cadet::Paramete int MultiChannelTransportModel::multiplexInitialConditions(const cadet::ParameterId& pId, double val, bool checkSens) { - if (pId.name == hashString("INIT_C") && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && (pId.particleType == ParTypeIndep) && (pId.component != CompIndep)) + if (pId.name == hashString("INIT_C") && (pId.section == SectionIndep) && (pId.boundState == BoundStateIndep) && + (pId.particleType == ParTypeIndep) && (pId.component != CompIndep)) { if ((pId.reaction == ReactionIndep) && _singleRadiusInitC) { @@ -96,7 +99,8 @@ void MultiChannelTransportModel::applyInitialCondition(const SimulationState& si if (!_initStateDot.empty()) { std::fill(simState.vecStateYdot, simState.vecStateYdot + idxr.offsetC(), 0.0); - std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), simState.vecStateYdot + idxr.offsetC()); + std::copy(_initStateDot.data(), _initStateDot.data() + numPureDofs(), + simState.vecStateYdot + idxr.offsetC()); } else std::fill(simState.vecStateYdot, simState.vecStateYdot + numDofs(), 0.0); @@ -114,7 +118,8 @@ void MultiChannelTransportModel::applyInitialCondition(const SimulationState& si { // Loop over components in cell for (unsigned comp = 0; comp < _disc.nComp; ++comp) - stateYbulk[col * idxr.strideColAxialCell() + rad * idxr.strideChannelCell() + comp * idxr.strideColComp()] = static_cast(_initC[comp + rad * _disc.nComp]); + stateYbulk[col * idxr.strideColAxialCell() + rad * idxr.strideChannelCell() + + comp * idxr.strideColComp()] = static_cast(_initC[comp + rad * _disc.nComp]); } } } @@ -132,14 +137,16 @@ void MultiChannelTransportModel::readInitialCondition(IParameterProvider& paramP // Check if INIT_STATE contains the full state and its time derivative if (initState.size() >= 2 * numPureDofs()) - _initStateDot = std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); + _initStateDot = + std::vector(initState.begin() + numPureDofs(), initState.begin() + 2 * numPureDofs()); return; } const std::vector initC = paramProvider.getDoubleArray("INIT_C"); _singleRadiusInitC = (initC.size() < _disc.nComp * _disc.nChannel); - if (((initC.size() < _disc.nComp) && _singleRadiusInitC) || ((initC.size() < _disc.nComp * _disc.nChannel) && !_singleRadiusInitC)) + if (((initC.size() < _disc.nComp) && _singleRadiusInitC) || + ((initC.size() < _disc.nComp * _disc.nChannel) && !_singleRadiusInitC)) throw InvalidParameterException("INIT_C does not contain enough values for all components (and radial zones)"); if (!_singleRadiusInitC) @@ -200,7 +207,9 @@ void MultiChannelTransportModel::readInitialCondition(IParameterProvider& paramP * @param [in] errorTol Error tolerance for algebraic equations * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ -void MultiChannelTransportModel::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) +void MultiChannelTransportModel::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); } @@ -250,9 +259,12 @@ void MultiChannelTransportModel::consistentInitialState(const SimulationTime& si * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] vecStateY Consistently initialized state vector - * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent state time derivatives. + * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent + * state time derivatives. */ -void MultiChannelTransportModel::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem) +void MultiChannelTransportModel::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -312,7 +324,9 @@ void MultiChannelTransportModel::consistentInitialTimeDerivative(const Simulatio * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) * @param [in] errorTol Error tolerance for algebraic equations */ -void MultiChannelTransportModel::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) +void MultiChannelTransportModel::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) { consistentInitialState(simTime, vecStateY, adJac, errorTol, threadLocalMem); } @@ -356,10 +370,14 @@ void MultiChannelTransportModel::leanConsistentInitialState(const SimulationTime * * @param [in] t Current time point * @param [in] vecStateY (Lean) consistently initialized state vector - * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time derivatives. - * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during execution of the function. + * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time + * derivatives. + * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during + * execution of the function. */ -void MultiChannelTransportModel::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem) +void MultiChannelTransportModel::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -399,7 +417,8 @@ void MultiChannelTransportModel::initializeSensitivityStates(const std::vector - *
                                        5. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, reaction equilibria). - * Once all @f$ c_i @f$, @f$ c_{p,i} @f$, and @f$ q_i^{(j)} @f$ have been computed, solve for the - * fluxes @f$ j_{f,i} @f$. Let @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at this point, we have - * \f[ \left( \frac{\partial F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, y_0, \dot{y}_0) \right)_{\mathcal{I}_a} = 0. \f]
                                        6. - *
                                        7. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. - * However, because of the algebraic equations, we need additional conditions to fully determine + * The process follows closely the one of consistentInitialConditions() and, in fact, is a linearized version + * of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ + * are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them + * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial + * \dot{y}_0}{\partial p}. @f$
                                          1. Solve all algebraic equations in the model (e.g., quasi-stationary isotherms, + * reaction equilibria). Once all @f$ c_i @f$, @f$ c_{p,i} @f$, and @f$ q_i^{(j)} @f$ have been computed, solve for the + * fluxes @f$ j_{f,i} @f$. Let @f$ \mathcal{I}_a @f$ be the index set of algebraic equations, then, at + * this point, we have \f[ \left( \frac{\partial F}{\partial y}(t, y_0, \dot{y}_0) s + \frac{\partial F}{\partial p}(t, + * y_0, \dot{y}_0) \right)_{\mathcal{I}_a} = 0. \f]
                                          2. Compute the time derivatives of the sensitivity @f$ \dot{s} + * @f$ such that the differential equations hold. However, because of the algebraic equations, we need additional + * conditions to fully determine * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the - * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting system - * has a similar structure as the system Jacobian. + * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting + * system has a similar structure as the system Jacobian. * @f[ \begin{align} * \left[\begin{array}{c|ccc|c} * \dot{J}_0 & & & & \\ @@ -443,8 +463,9 @@ void MultiChannelTransportModel::initializeSensitivityStates(const std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +void MultiChannelTransportModel::consistentInitialSensitivity( + const SimulationTime& simTime, const ConstSimulationState& simState, std::vector& vecSensY, + std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -494,20 +516,20 @@ void MultiChannelTransportModel::consistentInitialSensitivity(const SimulationTi * @brief Computes approximately / partially consistent initial values and time derivatives of sensitivity subsystems * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] and initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$, * the sensitivity system for a parameter @f$ p @f$ reads - * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of this linear DAE, @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ - * have to be consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the time - * derivative \f$ \dot{s}_0 \f$ such that they are consistent. + * \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) + * \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of this linear DAE, @f$ s_0 = + * \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p} @f$ have to be + * consistent with the sensitivity DAE. This functions updates the initial sensitivity\f$ s_0 \f$ and overwrites the + * time derivative \f$ \dot{s}_0 \f$ such that they are consistent. * - * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized version of it. - * This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ are - * related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by differentiating them - * with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = \frac{\partial \dot{y}_0}{\partial p}. @f$ - *
                                              - *
                                            1. Keep state and time derivative vectors as they are (i.e., do not solve algebraic equations). - * Only solve for the fluxes @f$ j_{f,i} @f$ (only linear equations).
                                            2. - *
                                            3. Compute the time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. - * However, because of the algebraic equations, we need additional conditions to fully determine + * The process follows closely the one of leanConsistentInitialConditions() and, in fact, is a linearized + * version of it. This is necessary because the initial conditions of the sensitivity system \f$ s_0 \f$ and \f$ + * \dot{s}_0 \f$ are related to the initial conditions \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ of the original DAE by + * differentiating them with respect to @f$ p @f$: @f$ s_0 = \frac{\partial y_0}{\partial p} @f$ and @f$ \dot{s}_0 = + * \frac{\partial \dot{y}_0}{\partial p}. @f$
                                              1. Keep state and time derivative vectors as they are (i.e., do not + * solve algebraic equations). Only solve for the fluxes @f$ j_{f,i} @f$ (only linear equations).
                                              2. Compute the + * time derivatives of the sensitivity @f$ \dot{s} @f$ such that the differential equations hold. However, because of + * the algebraic equations, we need additional conditions to fully determine * @f$ \dot{s}@f$. By differentiating the algebraic equations with respect to time, we get the * missing linear equations (recall that the sensitivity vector @f$ s @f$ is fixed). The resulting * equations are stated below: @@ -521,8 +543,9 @@ void MultiChannelTransportModel::consistentInitialSensitivity(const SimulationTi * where @f$ \dot{J}_0 @f$ denotes the bulk block Jacobian with respect to @f$ \dot{y}@f$. * * Let @f$ \mathcal{I}_d @f$ denote the index set of differential equations. - * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] - * which is 0 for algebraic equations (@f$ -\frac{\partial^2 F}{\partial t \partial p}@f$, to be more precise). + * The right hand side of the linear system is given by @f[ -\frac{\partial F}{\partial y}(t, y, \dot{y}) s - + * \frac{\partial F}{\partial p}(t, y, \dot{y}), @f] which is 0 for algebraic equations (@f$ -\frac{\partial^2 + * F}{\partial t \partial p}@f$, to be more precise). * * The linear system is solved by backsubstitution. First, the bulk block is solved. * Then, the equations for the fluxes @f$ j_f @f$ are solved by substituting in the solution of the @@ -536,8 +559,9 @@ void MultiChannelTransportModel::consistentInitialSensitivity(const SimulationTi * @param [in] adRes Pointer to residual vector of AD datatypes with parameter sensitivities * @todo Decrease amount of allocated memory by partially using temporary vectors (state and Schur complement) */ -void MultiChannelTransportModel::leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) +void MultiChannelTransportModel::leanConsistentInitialSensitivity( + const SimulationTime& simTime, const ConstSimulationState& simState, std::vector& vecSensY, + std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerConsistentInit); @@ -568,6 +592,6 @@ void MultiChannelTransportModel::leanConsistentInitialSensitivity(const Simulati } } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/MultiChannelTransportModel-LinearSolver.cpp b/src/libcadet/model/MultiChannelTransportModel-LinearSolver.cpp index 848a00bd7..d0da153bf 100644 --- a/src/libcadet/model/MultiChannelTransportModel-LinearSolver.cpp +++ b/src/libcadet/model/MultiChannelTransportModel-LinearSolver.cpp @@ -24,21 +24,22 @@ namespace model /** * @brief Computes the solution of the linear system involving the system Jacobian - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the - * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) \f$, - * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p rhs. + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + * b \f] has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the point + * \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) \f$, may help + * with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p rhs. * * @param [in] t Current time point * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) * @param [in] outerTol Error tolerance for the solution of the linear system from outer Newton iteration * @param [in,out] rhs On entry the right hand side of the linear equation system, on exit the solution * @param [in] weight Vector with error weights - * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is evaluated + * @param [in] simState State of the simulation (state vector and its time derivatives) at which the Jacobian is + * evaluated * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ -int MultiChannelTransportModel::linearSolve(double t, double alpha, double outerTol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) +int MultiChannelTransportModel::linearSolve(double t, double alpha, double outerTol, double* const rhs, + double const* const weight, const ConstSimulationState& simState) { BENCH_SCOPE(_timerLinearSolve); @@ -68,7 +69,8 @@ int MultiChannelTransportModel::linearSolve(double t, double alpha, double outer // ==== Step 2: Solve diagonal Jacobian blocks J_i to get y_i = J_i^{-1} b_i // The result is stored in rhs (in-place solution) - const bool result = _convDispOp.solveDiscretizedJacobian(rhs + idxr.offsetC(), weight + idxr.offsetC(), nullptr, outerTol); + const bool result = + _convDispOp.solveDiscretizedJacobian(rhs + idxr.offsetC(), weight + idxr.offsetC(), nullptr, outerTol); if (cadet_unlikely(!result)) { LOG(Error) << "Solve() failed for bulk block"; @@ -78,6 +80,6 @@ int MultiChannelTransportModel::linearSolve(double t, double alpha, double outer return 0; } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/MultiChannelTransportModel.cpp b/src/libcadet/model/MultiChannelTransportModel.cpp index 5ac9b2913..8bb0aa07f 100644 --- a/src/libcadet/model/MultiChannelTransportModel.cpp +++ b/src/libcadet/model/MultiChannelTransportModel.cpp @@ -34,13 +34,16 @@ #include "ParallelSupport.hpp" #ifdef CADET_PARALLELIZE - #include +#include #endif namespace { -cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvider& paramProvider, std::unordered_map& parameters, std::vector& values, const std::string& name, unsigned int nAxial, unsigned int nChannel, unsigned int nParType, cadet::UnitOpIdx uoi) +cadet::model::MultiplexMode readAndRegisterMultiplexParam( + cadet::IParameterProvider& paramProvider, std::unordered_map& parameters, + std::vector& values, const std::string& name, unsigned int nAxial, unsigned int nChannel, + unsigned int nParType, cadet::UnitOpIdx uoi) { cadet::model::MultiplexMode mode = cadet::model::MultiplexMode::Independent; readScalarParameterOrArray(values, paramProvider, name, 1); @@ -51,25 +54,33 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi { mode = cadet::model::MultiplexMode::Independent; if (values.size() != nParType) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nParType) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be " + std::to_string(nParType) + + ")"); } else if (modeConfig == 1) { mode = cadet::model::MultiplexMode::Radial; if (values.size() != nChannel * nParType) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nChannel * nParType) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be " + + std::to_string(nChannel * nParType) + ")"); } else if (modeConfig == 2) { mode = cadet::model::MultiplexMode::Axial; if (values.size() != nAxial * nParType) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nAxial * nParType) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be " + + std::to_string(nAxial * nParType) + ")"); } else if (modeConfig == 3) { mode = cadet::model::MultiplexMode::AxialRadial; if (values.size() != nAxial * nChannel * nParType) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nAxial * nChannel * nParType) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be " + + std::to_string(nAxial * nChannel * nParType) + ")"); } } else @@ -83,230 +94,244 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi else if (values.size() == nChannel * nAxial * nParType) mode = cadet::model::MultiplexMode::AxialRadial; else - throw cadet::InvalidParameterException("Could not infer multiplex mode of field " + name + ", set " + name + "_MULTIPLEX or change number of elements"); + throw cadet::InvalidParameterException("Could not infer multiplex mode of field " + name + ", set " + name + + "_MULTIPLEX or change number of elements"); } const cadet::StringHash nameHash = cadet::hashStringRuntime(name); switch (mode) { - case cadet::model::MultiplexMode::Independent: - { - std::vector p(nAxial * nChannel * nParType); - for (unsigned int s = 0; s < nAxial * nChannel; ++s) - std::copy(values.begin(), values.end(), p.begin() + s * nParType); + case cadet::model::MultiplexMode::Independent: { + std::vector p(nAxial * nChannel * nParType); + for (unsigned int s = 0; s < nAxial * nChannel; ++s) + std::copy(values.begin(), values.end(), p.begin() + s * nParType); - values = std::move(p); + values = std::move(p); - for (unsigned int s = 0; s < nParType; ++s) - parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, s, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep)] = &values[s]; - } - break; - case cadet::model::MultiplexMode::Radial: - { - std::vector p(nAxial * nChannel * nParType); - for (unsigned int s = 0; s < nAxial; ++s) - std::copy(values.begin(), values.end(), p.begin() + s * nParType * nChannel); + for (unsigned int s = 0; s < nParType; ++s) + parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, s, cadet::BoundStateIndep, + cadet::ReactionIndep, cadet::SectionIndep)] = &values[s]; + } + break; + case cadet::model::MultiplexMode::Radial: { + std::vector p(nAxial * nChannel * nParType); + for (unsigned int s = 0; s < nAxial; ++s) + std::copy(values.begin(), values.end(), p.begin() + s * nParType * nChannel); - values = std::move(p); + values = std::move(p); - for (unsigned int s = 0; s < nChannel; ++s) - { - for (unsigned int i = 0; i < nParType; ++i) - parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, i, cadet::BoundStateIndep, s, cadet::SectionIndep)] = &values[s * nParType + i]; - } - } - break; - case cadet::model::MultiplexMode::Axial: - { - std::vector p(nAxial * nChannel * nParType); - for (unsigned int i = 0; i < nAxial; ++i) - { - for (unsigned int j = 0; j < nChannel; ++j) - std::copy(values.begin() + i * nParType, values.begin() + (i+1) * nParType, p.begin() + i * nChannel * nParType + j * nParType); - } - - values = std::move(p); - - for (unsigned int s = 0; s < nAxial; ++s) - { - for (unsigned int i = 0; i < nParType; ++i) - parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, i, cadet::BoundStateIndep, cadet::ReactionIndep, s)] = &values[s * nParType * nChannel + i]; - } - } - break; - case cadet::model::MultiplexMode::AxialRadial: - cadet::registerParam3DArray(parameters, values, [=](bool multi, unsigned int ax, unsigned int rad, unsigned int pt) { return cadet::makeParamId(nameHash, uoi, cadet::CompIndep, pt, cadet::BoundStateIndep, rad, ax); }, nParType, nChannel); - break; - case cadet::model::MultiplexMode::RadialSection: - case cadet::model::MultiplexMode::Component: - case cadet::model::MultiplexMode::ComponentRadial: - case cadet::model::MultiplexMode::ComponentRadialSection: - case cadet::model::MultiplexMode::ComponentSection: - case cadet::model::MultiplexMode::Section: - case cadet::model::MultiplexMode::Type: - case cadet::model::MultiplexMode::ComponentType: - case cadet::model::MultiplexMode::ComponentSectionType: - cadet_assert(false); - break; + for (unsigned int s = 0; s < nChannel; ++s) + { + for (unsigned int i = 0; i < nParType; ++i) + parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, i, cadet::BoundStateIndep, s, + cadet::SectionIndep)] = &values[s * nParType + i]; + } + } + break; + case cadet::model::MultiplexMode::Axial: { + std::vector p(nAxial * nChannel * nParType); + for (unsigned int i = 0; i < nAxial; ++i) + { + for (unsigned int j = 0; j < nChannel; ++j) + std::copy(values.begin() + i * nParType, values.begin() + (i + 1) * nParType, + p.begin() + i * nChannel * nParType + j * nParType); + } + + values = std::move(p); + + for (unsigned int s = 0; s < nAxial; ++s) + { + for (unsigned int i = 0; i < nParType; ++i) + parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, i, cadet::BoundStateIndep, + cadet::ReactionIndep, s)] = &values[s * nParType * nChannel + i]; + } + } + break; + case cadet::model::MultiplexMode::AxialRadial: + cadet::registerParam3DArray( + parameters, values, + [=](bool multi, unsigned int ax, unsigned int rad, unsigned int pt) { + return cadet::makeParamId(nameHash, uoi, cadet::CompIndep, pt, cadet::BoundStateIndep, rad, ax); + }, + nParType, nChannel); + break; + case cadet::model::MultiplexMode::RadialSection: + case cadet::model::MultiplexMode::Component: + case cadet::model::MultiplexMode::ComponentRadial: + case cadet::model::MultiplexMode::ComponentRadialSection: + case cadet::model::MultiplexMode::ComponentSection: + case cadet::model::MultiplexMode::Section: + case cadet::model::MultiplexMode::Type: + case cadet::model::MultiplexMode::ComponentType: + case cadet::model::MultiplexMode::ComponentSectionType: + cadet_assert(false); + break; } return mode; } -bool multiplexParameterValue(const cadet::ParameterId& pId, cadet::StringHash nameHash, cadet::model::MultiplexMode mode, std::vector& data, unsigned int nAxial, unsigned int nChannel, unsigned int nParType, double value, std::unordered_set const* sensParams) +bool multiplexParameterValue(const cadet::ParameterId& pId, cadet::StringHash nameHash, + cadet::model::MultiplexMode mode, std::vector& data, unsigned int nAxial, + unsigned int nChannel, unsigned int nParType, double value, + std::unordered_set const* sensParams) { if (pId.name != nameHash) return false; switch (mode) { - case cadet::model::MultiplexMode::Independent: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + case cadet::model::MultiplexMode::Independent: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.particleType])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.particleType])) + return false; - for (unsigned int i = 0; i < nAxial * nChannel; ++i) - data[i * nParType + pId.particleType].setValue(value); + for (unsigned int i = 0; i < nAxial * nChannel; ++i) + data[i * nParType + pId.particleType].setValue(value); - return true; - } - case cadet::model::MultiplexMode::Radial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction == cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Radial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction == cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.reaction * nParType + pId.particleType])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.reaction * nParType + pId.particleType])) + return false; - for (unsigned int i = 0; i < nAxial; ++i) - data[i * nChannel * nParType + pId.reaction * nParType + pId.particleType].setValue(value); + for (unsigned int i = 0; i < nAxial; ++i) + data[i * nChannel * nParType + pId.reaction * nParType + pId.particleType].setValue(value); - return true; - } - case cadet::model::MultiplexMode::Axial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Axial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.section * nParType * nChannel + pId.particleType])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.section * nParType * nChannel + pId.particleType])) + return false; - for (unsigned int i = 0; i < nChannel; ++i) - data[pId.section * nParType * nChannel + i * nParType + pId.particleType].setValue(value); + for (unsigned int i = 0; i < nChannel; ++i) + data[pId.section * nParType * nChannel + i * nParType + pId.particleType].setValue(value); - return true; - } - case cadet::model::MultiplexMode::AxialRadial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction == cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::AxialRadial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction == cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.section * nParType * nChannel + pId.reaction * nParType + pId.particleType])) - return false; + if (sensParams && + !cadet::contains(*sensParams, + &data[pId.section * nParType * nChannel + pId.reaction * nParType + pId.particleType])) + return false; - data[pId.section * nParType * nChannel + pId.reaction * nParType + pId.particleType].setValue(value); + data[pId.section * nParType * nChannel + pId.reaction * nParType + pId.particleType].setValue(value); - return true; - } - case cadet::model::MultiplexMode::RadialSection: - case cadet::model::MultiplexMode::Component: - case cadet::model::MultiplexMode::ComponentRadial: - case cadet::model::MultiplexMode::ComponentRadialSection: - case cadet::model::MultiplexMode::ComponentSection: - case cadet::model::MultiplexMode::Section: - case cadet::model::MultiplexMode::Type: - case cadet::model::MultiplexMode::ComponentType: - case cadet::model::MultiplexMode::ComponentSectionType: - cadet_assert(false); - break; + return true; + } + case cadet::model::MultiplexMode::RadialSection: + case cadet::model::MultiplexMode::Component: + case cadet::model::MultiplexMode::ComponentRadial: + case cadet::model::MultiplexMode::ComponentRadialSection: + case cadet::model::MultiplexMode::ComponentSection: + case cadet::model::MultiplexMode::Section: + case cadet::model::MultiplexMode::Type: + case cadet::model::MultiplexMode::ComponentType: + case cadet::model::MultiplexMode::ComponentSectionType: + cadet_assert(false); + break; } return false; } -bool multiplexParameterAD(const cadet::ParameterId& pId, cadet::StringHash nameHash, cadet::model::MultiplexMode mode, std::vector& data, unsigned int nAxial, unsigned int nChannel, unsigned int nParType, unsigned int adDirection, double adValue, std::unordered_set& sensParams) +bool multiplexParameterAD(const cadet::ParameterId& pId, cadet::StringHash nameHash, cadet::model::MultiplexMode mode, + std::vector& data, unsigned int nAxial, unsigned int nChannel, + unsigned int nParType, unsigned int adDirection, double adValue, + std::unordered_set& sensParams) { if (pId.name != nameHash) return false; switch (mode) { - case cadet::model::MultiplexMode::Independent: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + case cadet::model::MultiplexMode::Independent: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.particleType]); + sensParams.insert(&data[pId.particleType]); - for (unsigned int i = 0; i < nAxial * nChannel; ++i) - data[i * nParType + pId.particleType].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nAxial * nChannel; ++i) + data[i * nParType + pId.particleType].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::Radial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction == cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Radial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction == cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.reaction * nParType + pId.particleType]); + sensParams.insert(&data[pId.reaction * nParType + pId.particleType]); - for (unsigned int i = 0; i < nAxial; ++i) - data[i * nChannel * nParType + pId.reaction * nParType + pId.particleType].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nAxial; ++i) + data[i * nChannel * nParType + pId.reaction * nParType + pId.particleType].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::Axial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Axial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.section * nParType * nChannel + pId.particleType]); + sensParams.insert(&data[pId.section * nParType * nChannel + pId.particleType]); - for (unsigned int i = 0; i < nChannel; ++i) - data[pId.section * nParType * nChannel + i * nParType + pId.particleType].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nChannel; ++i) + data[pId.section * nParType * nChannel + i * nParType + pId.particleType].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::AxialRadial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction == cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::AxialRadial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction == cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.section * nParType * nChannel + pId.reaction * nParType + pId.particleType]); - data[pId.section * nParType * nChannel + pId.reaction * nParType + pId.particleType].setADValue(adDirection, adValue); + sensParams.insert(&data[pId.section * nParType * nChannel + pId.reaction * nParType + pId.particleType]); + data[pId.section * nParType * nChannel + pId.reaction * nParType + pId.particleType].setADValue(adDirection, + adValue); - return true; - } - case cadet::model::MultiplexMode::RadialSection: - case cadet::model::MultiplexMode::Component: - case cadet::model::MultiplexMode::ComponentRadial: - case cadet::model::MultiplexMode::ComponentRadialSection: - case cadet::model::MultiplexMode::ComponentSection: - case cadet::model::MultiplexMode::Section: - case cadet::model::MultiplexMode::Type: - case cadet::model::MultiplexMode::ComponentType: - case cadet::model::MultiplexMode::ComponentSectionType: - cadet_assert(false); - break; + return true; + } + case cadet::model::MultiplexMode::RadialSection: + case cadet::model::MultiplexMode::Component: + case cadet::model::MultiplexMode::ComponentRadial: + case cadet::model::MultiplexMode::ComponentRadialSection: + case cadet::model::MultiplexMode::ComponentSection: + case cadet::model::MultiplexMode::Section: + case cadet::model::MultiplexMode::Type: + case cadet::model::MultiplexMode::ComponentType: + case cadet::model::MultiplexMode::ComponentSectionType: + cadet_assert(false); + break; } return false; } - -} // namespace - +} // namespace namespace cadet { @@ -314,10 +339,10 @@ namespace cadet namespace model { -MultiChannelTransportModel::MultiChannelTransportModel(UnitOpIdx unitOpIdx) : UnitOperationBase(unitOpIdx), - _dynReactionBulk(nullptr), _jacInlet(), - _analyticJac(true), _jacobianAdDirs(0), _factorizeJacobian(false), _tempState(nullptr), - _initC(0), _singleRadiusInitC(true), _initState(0), _initStateDot(0) +MultiChannelTransportModel::MultiChannelTransportModel(UnitOpIdx unitOpIdx) + : UnitOperationBase(unitOpIdx), _dynReactionBulk(nullptr), _jacInlet(), _analyticJac(true), _jacobianAdDirs(0), + _factorizeJacobian(false), _tempState(nullptr), _initC(0), _singleRadiusInitC(true), _initState(0), + _initStateDot(0) { } @@ -340,7 +365,6 @@ unsigned int MultiChannelTransportModel::numPureDofs() const CADET_NOEXCEPT return _disc.nCol * _disc.nChannel * _disc.nComp; } - bool MultiChannelTransportModel::usesAD() const CADET_NOEXCEPT { #ifdef CADET_CHECK_ANALYTIC_JACOBIAN @@ -352,7 +376,8 @@ bool MultiChannelTransportModel::usesAD() const CADET_NOEXCEPT #endif } -bool MultiChannelTransportModel::configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper) +bool MultiChannelTransportModel::configureModelDiscretization(IParameterProvider& paramProvider, + const IConfigHelper& helper) { // ==== Read discretization _disc.nComp = paramProvider.getInt("NCOMP"); @@ -361,11 +386,11 @@ bool MultiChannelTransportModel::configureModelDiscretization(IParameterProvider paramProvider.pushScope("discretization"); _disc.nCol = paramProvider.getInt("NCOL"); - if(_disc.nChannel < 1) + if (_disc.nChannel < 1) throw InvalidParameterException("NCHANNEL must be > 0"); - // Determine whether analytic Jacobian should be used but don't set it right now. - // We need to setup Jacobian matrices first. + // Determine whether analytic Jacobian should be used but don't set it right now. + // We need to setup Jacobian matrices first. #ifndef CADET_CHECK_ANALYTIC_JACOBIAN const bool analyticJac = paramProvider.getBool("USE_ANALYTIC_JACOBIAN"); #else @@ -402,13 +427,15 @@ bool MultiChannelTransportModel::configureModelDiscretization(IParameterProvider if (_dynReactionBulk->usesParamProviderInDiscretizationConfig()) paramProvider.pushScope("reaction_bulk"); - reactionConfSuccess = _dynReactionBulk->configureModelDiscretization(paramProvider, _disc.nComp, nullptr, nullptr); + reactionConfSuccess = + _dynReactionBulk->configureModelDiscretization(paramProvider, _disc.nComp, nullptr, nullptr); if (_dynReactionBulk->usesParamProviderInDiscretizationConfig()) paramProvider.popScope(); } - const bool transportSuccess = _convDispOp.configureModelDiscretization(paramProvider, helper, _disc.nComp, _disc.nCol, _disc.nChannel, _dynReactionBulk); + const bool transportSuccess = _convDispOp.configureModelDiscretization( + paramProvider, helper, _disc.nComp, _disc.nCol, _disc.nChannel, _dynReactionBulk); // Setup the memory for tempState based on state vector _tempState = new double[numDofs()]; @@ -425,9 +452,18 @@ bool MultiChannelTransportModel::configure(IParameterProvider& paramProvider) // Add parameters to map // Register initial conditions parameters - registerParam1DArray(_parameters, _initC, [=](bool multi, unsigned int comp) { return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep); }); + registerParam1DArray(_parameters, _initC, [=](bool multi, unsigned int comp) { + return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep); + }); if (_disc.nChannel > 1) - registerParam2DArray(_parameters, _initC, [=](bool multi, unsigned int rad, unsigned int comp) { return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, rad, SectionIndep); }, _disc.nComp); + registerParam2DArray( + _parameters, _initC, + [=](bool multi, unsigned int rad, unsigned int comp) { + return makeParamId(hashString("INIT_C"), _unitOpIdx, comp, ParTypeIndep, BoundStateIndep, rad, + SectionIndep); + }, + _disc.nComp); // Reconfigure reaction model bool dynReactionConfSuccess = true; @@ -441,7 +477,6 @@ bool MultiChannelTransportModel::configure(IParameterProvider& paramProvider) return transportSuccess && dynReactionConfSuccess; } - unsigned int MultiChannelTransportModel::threadLocalMemorySize() const CADET_NOEXCEPT { LinearMemorySizer lms; @@ -473,7 +508,9 @@ void MultiChannelTransportModel::useAnalyticJacobian(const bool analyticJac) #endif } -void MultiChannelTransportModel::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) +void MultiChannelTransportModel::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac) { Indexer idxr(_disc); @@ -525,7 +562,6 @@ void MultiChannelTransportModel::reportSolutionStructure(ISolutionRecorder& reco recorder.unitOperationStructure(_unitOpIdx, *this, expr); } - unsigned int MultiChannelTransportModel::requiredADdirs() const CADET_NOEXCEPT { #ifndef CADET_CHECK_ANALYTIC_JACOBIAN @@ -565,21 +601,26 @@ void MultiChannelTransportModel::extractJacobianFromAD(active const* const adRes * @param [in] adRes Residual vector of AD datatypes with band compressed seed vectors * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) */ -void MultiChannelTransportModel::checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const +void MultiChannelTransportModel::checkAnalyticJacobianAgainstAd(active const* const adRes, + unsigned int adDirOffset) const { } #endif -int MultiChannelTransportModel::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem) +int MultiChannelTransportModel::residual(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); // Evaluate residual do not compute Jacobian or parameter sensitivities - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } -int MultiChannelTransportModel::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int MultiChannelTransportModel::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); @@ -588,10 +629,14 @@ int MultiChannelTransportModel::jacobian(const SimulationTime& simTime, const Co if (_analyticJac) return residual(simTime, simState, res, adJac, threadLocalMem, true, false); else - return residualWithJacobian(simTime, ConstSimulationState{ simState.vecStateY, nullptr }, nullptr, adJac, threadLocalMem); + return residualWithJacobian(simTime, ConstSimulationState{simState.vecStateY, nullptr}, nullptr, adJac, + threadLocalMem); } -int MultiChannelTransportModel::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int MultiChannelTransportModel::residualWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidual); @@ -599,8 +644,10 @@ int MultiChannelTransportModel::residualWithJacobian(const SimulationTime& simTi return residual(simTime, simState, res, adJac, threadLocalMem, true, false); } -int MultiChannelTransportModel::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, - const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity) +int MultiChannelTransportModel::residual(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, + bool paramSensitivity) { if (updateJacobian) { @@ -611,7 +658,8 @@ int MultiChannelTransportModel::residual(const SimulationTime& simTime, const Co { if (paramSensitivity) { - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -620,7 +668,8 @@ int MultiChannelTransportModel::residual(const SimulationTime& simTime, const Co return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } else { @@ -635,9 +684,11 @@ int MultiChannelTransportModel::residual(const SimulationTime& simTime, const Co // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -660,15 +711,18 @@ int MultiChannelTransportModel::residual(const SimulationTime& simTime, const Co // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, + simState.vecStateYdot, adJac.adRes, threadLocalMem); // Only do comparison if we have a residuals vector (which is not always the case) if (res) { // Evaluate with analytical Jacobian which is stored in the band matrices - retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); // Compare AD with anaytic Jacobian checkAnalyticJacobianAgainstAd(adJac.adRes, adJac.adDirOffset); @@ -688,7 +742,8 @@ int MultiChannelTransportModel::residual(const SimulationTime& simTime, const Co // @todo Check if this is necessary ad::resetAd(adJac.adRes, numDofs()); - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem); // Copy AD residuals to original residuals vector if (res) @@ -697,12 +752,15 @@ int MultiChannelTransportModel::residual(const SimulationTime& simTime, const Co return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem); } } template -int MultiChannelTransportModel::residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem) +int MultiChannelTransportModel::residualImpl(double t, unsigned int secIdx, StateType const* const y, + double const* const yDot, ResidualType* const res, + util::ThreadLocalStorage& threadLocalMem) { // Handle inlet DOFs, which are simply copied to res for (unsigned int i = 0; i < _disc.nComp * _disc.nChannel; ++i) @@ -721,7 +779,8 @@ int MultiChannelTransportModel::residualImpl(double t, unsigned int secIdx, Stat ResidualType* resC = res + idxr.offsetC(); LinearBufferAllocator tlmAlloc = threadLocalMem.get(); - for (unsigned int colCell = 0; colCell < _disc.nCol * _disc.nChannel; ++colCell, yC += idxr.strideChannelCell(), resC += idxr.strideChannelCell()) + for (unsigned int colCell = 0; colCell < _disc.nCol * _disc.nChannel; + ++colCell, yC += idxr.strideChannelCell(), resC += idxr.strideChannelCell()) { const unsigned int axialCell = colCell / _disc.nChannel; const unsigned int channelCell = colCell % _disc.nChannel; @@ -733,14 +792,19 @@ int MultiChannelTransportModel::residualImpl(double t, unsigned int secIdx, Stat if (wantJac) { // static_cast should be sufficient here, but this statement is also analyzed when wantJac = false - _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(yC), -1.0, _convDispOp.jacobian().row(colCell * idxr.strideChannelCell()), tlmAlloc); + _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(yC), -1.0, + _convDispOp.jacobian().row(colCell * idxr.strideChannelCell()), + tlmAlloc); } } return 0; } -int MultiChannelTransportModel::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int MultiChannelTransportModel::residualSensFwdWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidualSens); @@ -749,17 +813,23 @@ int MultiChannelTransportModel::residualSensFwdWithJacobian(const SimulationTime return residual(simTime, simState, nullptr, adJac, threadLocalMem, true, true); } -int MultiChannelTransportModel::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem) +int MultiChannelTransportModel::residualSensFwdAdOnly(const SimulationTime& simTime, + const ConstSimulationState& simState, active* const adRes, + util::ThreadLocalStorage& threadLocalMem) { BENCH_SCOPE(_timerResidualSens); // Evaluate residual for all parameters using AD in vector mode - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adRes, threadLocalMem); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, adRes, threadLocalMem); } -int MultiChannelTransportModel::residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3) +int MultiChannelTransportModel::residualSensFwdCombine(const SimulationTime& simTime, + const ConstSimulationState& simState, + const std::vector& yS, + const std::vector& ySdot, + const std::vector& resS, active const* adRes, + double* const tmp1, double* const tmp2, double* const tmp3) { BENCH_SCOPE(_timerResidualSens); @@ -799,11 +869,12 @@ int MultiChannelTransportModel::residualSensFwdCombine(const SimulationTime& sim } /** - * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, \dot{y}\right) @f$) + * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, + * \dot{y}\right) @f$) * @details Actually, the operation @f$ z = \alpha \frac{\partial F}{\partial y} x + \beta z @f$ is performed. * - * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ once - * before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. + * Note that residual() or one of its cousins has to be called with the requested point @f$ (t, y, \dot{y}) @f$ + * once before calling multiplyWithJacobian() as this implementation ignores the given @f$ (t, y, \dot{y}) @f$. * @param [in] simTime Current simulation time point * @param [in] simState Simulation state vectors * @param [in] yS Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial y} @f$ @@ -811,7 +882,9 @@ int MultiChannelTransportModel::residualSensFwdCombine(const SimulationTime& sim * @param [in] beta Factor @f$ \beta @f$ in front of @f$ z @f$ * @param [in,out] ret Vector @f$ z @f$ which stores the result of the operation */ -void MultiChannelTransportModel::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret) +void MultiChannelTransportModel::multiplyWithJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, double const* yS, + double alpha, double beta, double* ret) { Indexer idxr(_disc); @@ -828,7 +901,8 @@ void MultiChannelTransportModel::multiplyWithJacobian(const SimulationTime& simT } /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ + * with a given vector * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). * @param [in] simTime Current simulation time point @@ -836,7 +910,9 @@ void MultiChannelTransportModel::multiplyWithJacobian(const SimulationTime& simT * @param [in] sDot Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ * @param [out] ret Vector @f$ z @f$ which stores the result of the operation */ -void MultiChannelTransportModel::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret) +void MultiChannelTransportModel::multiplyWithDerivativeJacobian(const SimulationTime& simTime, + const ConstSimulationState& simState, + double const* sDot, double* ret) { _convDispOp.multiplyWithDerivativeJacobian(simTime, sDot, ret); @@ -934,7 +1010,6 @@ bool MultiChannelTransportModel::setSensitiveParameter(const ParameterId& pId, u return UnitOperationBase::setSensitiveParameter(pId, adDirection, adValue); } - int MultiChannelTransportModel::Exporter::writeMobilePhase(double* buffer) const { const int blockSize = numMobilePhaseDofs(); @@ -977,12 +1052,15 @@ int MultiChannelTransportModel::Exporter::writeOutlet(double* buffer) const return _disc.nComp * _disc.nChannel; } -void registerMultiChannelTransportModel(std::unordered_map>& models) +void registerMultiChannelTransportModel( + std::unordered_map>& models) { - models[MultiChannelTransportModel::identifier()] = [](UnitOpIdx uoId, IParameterProvider&) { return new MultiChannelTransportModel(uoId); }; + models[MultiChannelTransportModel::identifier()] = [](UnitOpIdx uoId, IParameterProvider&) { + return new MultiChannelTransportModel(uoId); + }; models["MCT"] = [](UnitOpIdx uoId, IParameterProvider&) { return new MultiChannelTransportModel(uoId); }; } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/MultiChannelTransportModel.hpp b/src/libcadet/model/MultiChannelTransportModel.hpp index efd7139ec..85760fca6 100644 --- a/src/libcadet/model/MultiChannelTransportModel.hpp +++ b/src/libcadet/model/MultiChannelTransportModel.hpp @@ -46,8 +46,8 @@ class IDynamicReactionModel; * @details * * @f[\begin{align} - \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 c_i}{\partial z^2} -\end{align} @f] + \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 +c_i}{\partial z^2} \end{align} @f] * Danckwerts boundary conditions (see @cite Danckwerts1953) @f[ \begin{align} u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partial z}(t,0) \\ @@ -58,7 +58,6 @@ u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partia class MultiChannelTransportModel : public UnitOperationBase { public: - MultiChannelTransportModel(UnitOpIdx unitOpIdx); virtual ~MultiChannelTransportModel() CADET_NOEXCEPT; @@ -67,59 +66,104 @@ class MultiChannelTransportModel : public UnitOperationBase virtual bool usesAD() const CADET_NOEXCEPT; virtual unsigned int requiredADdirs() const CADET_NOEXCEPT; - virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT { return _unitOpIdx; } - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } + virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT + { + return _unitOpIdx; + } + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } virtual void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT; - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return _disc.nChannel; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return _disc.nChannel; } - virtual bool canAccumulate() const CADET_NOEXCEPT { return false; } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return _disc.nChannel; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return _disc.nChannel; + } + virtual bool canAccumulate() const CADET_NOEXCEPT + { + return false; + } - static const char* identifier() { return "MULTI_CHANNEL_TRANSPORT"; } - virtual const char* unitOperationName() const CADET_NOEXCEPT { return identifier(); } + static const char* identifier() + { + return "MULTI_CHANNEL_TRANSPORT"; + } + virtual const char* unitOperationName() const CADET_NOEXCEPT + { + return identifier(); + } virtual bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper); virtual bool configure(IParameterProvider& paramProvider); - virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac); + virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac); virtual void useAnalyticJacobian(const bool analyticJac); virtual void reportSolution(ISolutionRecorder& recorder, double const* const solution) const; virtual void reportSolutionStructure(ISolutionRecorder& recorder) const; - virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem); - virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem); + virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3); + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3); virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState); + const ConstSimulationState& simState); virtual void prepareADvectors(const AdJacobianParams& adJac) const; virtual void applyInitialCondition(const SimulationState& simState) const; virtual void readInitialCondition(IParameterProvider& paramProvider); - virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); virtual void initializeSensitivityStates(const std::vector& vecSensY) const; virtual void consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem); virtual void leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual bool hasInlet() const CADET_NOEXCEPT { return true; } - virtual bool hasOutlet() const CADET_NOEXCEPT { return true; } + virtual bool hasInlet() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasOutlet() const CADET_NOEXCEPT + { + return true; + } virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT; virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT; @@ -127,14 +171,19 @@ class MultiChannelTransportModel : public UnitOperationBase virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT; virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size); - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { } + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) + { + } virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut); - virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret); - virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret); + virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret); + virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* sDot, double* ret); - inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double* ret) + inline void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double* ret) { multiplyWithJacobian(simTime, simState, yS, 1.0, 0.0, ret); } @@ -148,38 +197,29 @@ class MultiChannelTransportModel : public UnitOperationBase #ifdef CADET_BENCHMARK_MODE virtual std::vector benchmarkTimings() const { - return std::vector({ - static_cast(numDofs()), - _timerResidual.totalElapsedTime(), - _timerResidualSens.totalElapsedTime(), - _timerConsistentInit.totalElapsedTime(), - _timerLinearSolve.totalElapsedTime(), - _timerFactorize.totalElapsedTime() - }); + return std::vector({static_cast(numDofs()), _timerResidual.totalElapsedTime(), + _timerResidualSens.totalElapsedTime(), _timerConsistentInit.totalElapsedTime(), + _timerLinearSolve.totalElapsedTime(), _timerFactorize.totalElapsedTime()}); } virtual char const* const* benchmarkDescriptions() const { - static const char* const desc[] = { - "DOFs", - "Residual", - "ResidualSens", - "ConsistentInit", - "LinearSolve", - "Factorize" - }; + static const char* const desc[] = {"DOFs", "Residual", "ResidualSens", + "ConsistentInit", "LinearSolve", "Factorize"}; return desc; } #endif protected: - class Indexer; - int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity); + int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, + bool paramSensitivity); template - int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); + int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, + ResidualType* const res, util::ThreadLocalStorage& threadLocalMem); void extractJacobianFromAD(active const* const adRes, unsigned int adDirOffset); @@ -194,20 +234,21 @@ class MultiChannelTransportModel : public UnitOperationBase struct Discretization { - unsigned int nComp; //!< Number of components - unsigned int nCol; //!< Number of column cells + unsigned int nComp; //!< Number of components + unsigned int nCol; //!< Number of column cells unsigned int nChannel; //!< Number of channels }; Discretization _disc; //!< Discretization info -// IExternalFunction* _extFun; //!< External function (owned by library user) + // IExternalFunction* _extFun; //!< External function (owned by library user) - parts::MultiChannelConvectionDispersionOperator _convDispOp; //!< Convection dispersion operator for interstitial volume transport + parts::MultiChannelConvectionDispersionOperator + _convDispOp; //!< Convection dispersion operator for interstitial volume transport IDynamicReactionModel* _dynReactionBulk; //!< Dynamic reactions in the bulk volume linalg::DoubleSparseMatrix _jacInlet; //!< Jacobian inlet DOF block matrix connects inlet DOFs to first bulk cells - bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used + bool _analyticJac; //!< Determines whether AD or analytic Jacobians are used unsigned int _jacobianAdDirs; //!< Number of AD seed vectors required for Jacobian computation bool _factorizeJacobian; //!< Determines whether the Jacobian needs to be factorized @@ -217,7 +258,7 @@ class MultiChannelTransportModel : public UnitOperationBase std::vector _initC; //!< Liquid bulk phase initial conditions bool _singleRadiusInitC; - std::vector _initState; //!< Initial conditions for state vector if given + std::vector _initState; //!< Initial conditions for state vector if given std::vector _initStateDot; //!< Initial conditions for time derivative BENCH_TIMER(_timerResidual) @@ -229,23 +270,51 @@ class MultiChannelTransportModel : public UnitOperationBase class Indexer { public: - Indexer(const Discretization& disc) : _disc(disc) { } + Indexer(const Discretization& disc) : _disc(disc) + { + } // Strides - inline int strideColAxialCell() const CADET_NOEXCEPT { return static_cast(_disc.nComp) * static_cast(_disc.nChannel); } - inline int strideChannelCell() const CADET_NOEXCEPT { return static_cast(_disc.nComp); } - inline int strideColComp() const CADET_NOEXCEPT { return 1; } + inline int strideColAxialCell() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp) * static_cast(_disc.nChannel); + } + inline int strideChannelCell() const CADET_NOEXCEPT + { + return static_cast(_disc.nComp); + } + inline int strideColComp() const CADET_NOEXCEPT + { + return 1; + } // Offsets - inline int offsetC() const CADET_NOEXCEPT { return _disc.nComp * _disc.nChannel; } + inline int offsetC() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nChannel; + } // Return pointer to first element of state variable in state vector - template inline real_t* c(real_t* const data) const { return data + offsetC(); } - template inline real_t const* c(real_t const* const data) const { return data + offsetC(); } + template inline real_t* c(real_t* const data) const + { + return data + offsetC(); + } + template inline real_t const* c(real_t const* const data) const + { + return data + offsetC(); + } // Return specific variable in state vector - template inline real_t& c(real_t* const data, unsigned int col, unsigned int rad, unsigned int comp) const { return data[offsetC() + comp + col * strideColAxialCell() + rad * strideChannelCell()]; } - template inline const real_t& c(real_t const* const data, unsigned int col, unsigned int rad, unsigned int comp) const { return data[offsetC() + comp + col * strideColAxialCell() + rad * strideChannelCell()]; } + template + inline real_t& c(real_t* const data, unsigned int col, unsigned int rad, unsigned int comp) const + { + return data[offsetC() + comp + col * strideColAxialCell() + rad * strideChannelCell()]; + } + template + inline const real_t& c(real_t const* const data, unsigned int col, unsigned int rad, unsigned int comp) const + { + return data[offsetC() + comp + col * strideColAxialCell() + rad * strideChannelCell()]; + } protected: const Discretization& _disc; @@ -254,41 +323,127 @@ class MultiChannelTransportModel : public UnitOperationBase class Exporter : public ISolutionExporter { public: - - Exporter(const Discretization& disc, const MultiChannelTransportModel& model, double const* data) : _disc(disc), _idx(disc), _model(model), _data(data) { } + Exporter(const Discretization& disc, const MultiChannelTransportModel& model, double const* data) + : _disc(disc), _idx(disc), _model(model), _data(data) + { + } Exporter(const Discretization&& disc, const MultiChannelTransportModel& model, double const* data) = delete; - virtual bool hasParticleFlux() const CADET_NOEXCEPT { return false; } - virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT { return false; } - virtual bool hasSolidPhase() const CADET_NOEXCEPT { return false; } - virtual bool hasVolume() const CADET_NOEXCEPT { return false; } - virtual bool isParticleLumped() const CADET_NOEXCEPT { return false; } - virtual bool hasPrimaryExtent() const CADET_NOEXCEPT { return true; } - - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _disc.nComp; } - virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT { return _disc.nCol; } - virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT { return _disc.nChannel; } - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return _disc.nChannel; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return _disc.nChannel; } - virtual unsigned int numParticleTypes() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT { return _disc.nComp * _disc.nCol * _disc.nChannel; } - virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT { return 0; } + virtual bool hasParticleFlux() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasSolidPhase() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasVolume() const CADET_NOEXCEPT + { + return false; + } + virtual bool isParticleLumped() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasPrimaryExtent() const CADET_NOEXCEPT + { + return true; + } + + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _disc.nComp; + } + virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT + { + return _disc.nCol; + } + virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT + { + return _disc.nChannel; + } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return _disc.nChannel; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return _disc.nChannel; + } + virtual unsigned int numParticleTypes() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT + { + return _disc.nComp * _disc.nCol * _disc.nChannel; + } + virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT + { + return 0; + } virtual int writeMobilePhase(double* buffer) const; - virtual int writeSolidPhase(double* buffer) const { return 0; } - virtual int writeParticleMobilePhase(double* buffer) const { return 0; } - virtual int writeSolidPhase(unsigned int parType, double* buffer) const { return 0; } - virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const { return 0; } - virtual int writeParticleFlux(double* buffer) const { return 0; } - virtual int writeParticleFlux(unsigned int parType, double* buffer) const { return 0; } - virtual int writeVolume(double* buffer) const { return 0; } + virtual int writeSolidPhase(double* buffer) const + { + return 0; + } + virtual int writeParticleMobilePhase(double* buffer) const + { + return 0; + } + virtual int writeSolidPhase(unsigned int parType, double* buffer) const + { + return 0; + } + virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const + { + return 0; + } + virtual int writeParticleFlux(double* buffer) const + { + return 0; + } + virtual int writeParticleFlux(unsigned int parType, double* buffer) const + { + return 0; + } + virtual int writeVolume(double* buffer) const + { + return 0; + } virtual int writeInlet(unsigned int port, double* buffer) const; virtual int writeInlet(double* buffer) const; virtual int writeOutlet(unsigned int port, double* buffer) const; @@ -307,7 +462,10 @@ class MultiChannelTransportModel : public UnitOperationBase coords[i] = static_cast(i); return _disc.nChannel; } - virtual int writeParticleCoordinates(unsigned int parType, double* coords) const { return 0; } + virtual int writeParticleCoordinates(unsigned int parType, double* coords) const + { + return 0; + } protected: const Discretization& _disc; @@ -320,4 +478,4 @@ class MultiChannelTransportModel : public UnitOperationBase } // namespace model } // namespace cadet -#endif // LIBCADET_MULTICHANNELMODEL_HPP_ +#endif // LIBCADET_MULTICHANNELMODEL_HPP_ diff --git a/src/libcadet/model/OutletModel.cpp b/src/libcadet/model/OutletModel.cpp index fd15da49d..7ea546cd1 100644 --- a/src/libcadet/model/OutletModel.cpp +++ b/src/libcadet/model/OutletModel.cpp @@ -28,13 +28,11 @@ #include "AdUtils.hpp" - inline void residual(double const* y, unsigned int nComp, double* res) { std::copy(y, y + nComp, res); } - namespace cadet { @@ -75,7 +73,9 @@ bool OutletModel::configure(IParameterProvider& paramProvider) return true; } -void OutletModel::setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { } +void OutletModel::setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) +{ +} std::unordered_map OutletModel::getAllParameterValues() const { @@ -125,8 +125,14 @@ unsigned int OutletModel::numSensParams() const return 0; } -void OutletModel::useAnalyticJacobian(const bool analyticJac) { } -void OutletModel::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) { } +void OutletModel::useAnalyticJacobian(const bool analyticJac) +{ +} +void OutletModel::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac) +{ +} void OutletModel::reportSolution(ISolutionRecorder& recorder, double const* const solution) const { @@ -146,7 +152,9 @@ unsigned int OutletModel::requiredADdirs() const CADET_NOEXCEPT return 0; } -void OutletModel::prepareADvectors(const AdJacobianParams& adJac) const { } +void OutletModel::prepareADvectors(const AdJacobianParams& adJac) const +{ +} void OutletModel::applyInitialCondition(const SimulationState& simState) const { @@ -154,30 +162,36 @@ void OutletModel::applyInitialCondition(const SimulationState& simState) const std::fill(simState.vecStateYdot, simState.vecStateYdot + _nComp, 0.0); } -void OutletModel::readInitialCondition(IParameterProvider& paramProvider) { } +void OutletModel::readInitialCondition(IParameterProvider& paramProvider) +{ +} -int OutletModel::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int OutletModel::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) { // Jacobian is always identity ::residual(simState.vecStateY, _nComp, res); return 0; } -int OutletModel::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem) +int OutletModel::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem) { ::residual(simState.vecStateY, _nComp, res); return 0; } -int OutletModel::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, - double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int OutletModel::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { // Jacobian is always identity ::residual(simState.vecStateY, _nComp, res); return 0; } -int OutletModel::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem) +int OutletModel::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem) { for (unsigned int i = 0; i < _nComp; ++i) adRes[i] = simState.vecStateY[i]; @@ -185,9 +199,10 @@ int OutletModel::residualSensFwdAdOnly(const SimulationTime& simTime, const Cons return 0; } -int OutletModel::residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3) +int OutletModel::residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3) { // Directional derivative (dF / dy) * s does nothing since dF / dy = I (identity) for (std::size_t param = 0; param < resS.size(); ++param) @@ -199,7 +214,8 @@ int OutletModel::residualSensFwdCombine(const SimulationTime& simTime, const Con return 0; } -int OutletModel::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int OutletModel::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) { for (unsigned int i = 0; i < _nComp; ++i) adJac.adRes[i] = simState.vecStateY[i]; @@ -207,21 +223,26 @@ int OutletModel::residualSensFwdWithJacobian(const SimulationTime& simTime, cons return 0; } -void OutletModel::initializeSensitivityStates(const std::vector& vecSensY) const { } +void OutletModel::initializeSensitivityStates(const std::vector& vecSensY) const +{ +} void OutletModel::consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem) { // Nothing to do here as inlet DOFs are initialized by ModelSystem } void OutletModel::leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem) { // Nothing to do here as inlet DOFs are initialized by ModelSystem } -void OutletModel::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret) +void OutletModel::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret) { // dF / dy = I (identity matrix) for (unsigned int i = 0; i < _nComp; ++i) @@ -230,12 +251,12 @@ void OutletModel::multiplyWithJacobian(const SimulationTime& simTime, const Cons } } -void OutletModel::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret) +void OutletModel::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* sDot, double* ret) { std::fill_n(ret, numDofs(), 0.0); } - int OutletModel::Exporter::writeInlet(unsigned int port, double* buffer) const { cadet_assert(port == 0); @@ -262,12 +283,12 @@ int OutletModel::Exporter::writeOutlet(double* buffer) const return _nComp; } - -void registerOutletModel(std::unordered_map>& models) +void registerOutletModel( + std::unordered_map>& models) { models[OutletModel::identifier()] = [](UnitOpIdx uoId, IParameterProvider&) { return new OutletModel(uoId); }; } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/OutletModel.hpp b/src/libcadet/model/OutletModel.hpp index 105bbd1be..941ae7761 100644 --- a/src/libcadet/model/OutletModel.hpp +++ b/src/libcadet/model/OutletModel.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the outlet model. */ @@ -44,7 +44,6 @@ namespace model class OutletModel : public IUnitOperation { public: - OutletModel(UnitOpIdx unitOpIdx); virtual ~OutletModel() CADET_NOEXCEPT; @@ -53,20 +52,45 @@ class OutletModel : public IUnitOperation virtual bool usesAD() const CADET_NOEXCEPT; virtual unsigned int requiredADdirs() const CADET_NOEXCEPT; - virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT { return _unitOpIdx; } - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _nComp; } - virtual void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT { } - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 0; } - virtual bool canAccumulate() const CADET_NOEXCEPT { return true; } + virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT + { + return _unitOpIdx; + } + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _nComp; + } + virtual void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT + { + } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 0; + } + virtual bool canAccumulate() const CADET_NOEXCEPT + { + return true; + } - static const char* identifier() { return "OUTLET"; } - virtual const char* unitOperationName() const CADET_NOEXCEPT { return identifier(); } + static const char* identifier() + { + return "OUTLET"; + } + virtual const char* unitOperationName() const CADET_NOEXCEPT + { + return identifier(); + } virtual bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper); virtual bool configure(IParameterProvider& paramProvider); - virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac); - + virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac); + virtual std::unordered_map getAllParameterValues() const; virtual bool hasParameter(const ParameterId& pId) const; virtual double getParameterDouble(const ParameterId& pId) const; @@ -86,114 +110,270 @@ class OutletModel : public IUnitOperation virtual void reportSolution(ISolutionRecorder& recorder, double const* const solution) const; virtual void reportSolutionStructure(ISolutionRecorder& recorder) const; - virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem); - - virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - - virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3); - - - // linearSolve and assembleAndPrepareDAEJacobian are null operations since there are only inlet DOFs, which are treated by ModelSystem + virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem); + + virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + + virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3); + + // linearSolve and assembleAndPrepareDAEJacobian are null operations since there are only inlet DOFs, which are + // treated by ModelSystem virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) { return 0; } + const ConstSimulationState& simState) + { + return 0; + } virtual void prepareADvectors(const AdJacobianParams& adJac) const; virtual void applyInitialCondition(const SimulationState& simState) const; virtual void readInitialCondition(IParameterProvider& paramProvider); - virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) { } - virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem) { } + virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) + { + } + virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem) + { + } virtual void consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); virtual void initializeSensitivityStates(const std::vector& vecSensY) const; - virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) { } - virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem) { } + virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) + { + } + virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem) + { + } virtual void leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) { } + virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) + { + } - virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret); - virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret); + virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret); + virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* sDot, double* ret); - virtual bool hasInlet() const CADET_NOEXCEPT { return true; } - virtual bool hasOutlet() const CADET_NOEXCEPT { return false; } + virtual bool hasInlet() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasOutlet() const CADET_NOEXCEPT + { + return false; + } - virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT { return 0; } - virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT { return 0; } - virtual unsigned int localInletComponentIndex(unsigned int port) const CADET_NOEXCEPT { return 0; } - virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT { return 1; } + virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int localInletComponentIndex(unsigned int port) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT + { + return 1; + } virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections); - virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut) { } + virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut) + { + } - virtual unsigned int threadLocalMemorySize() const CADET_NOEXCEPT { return 0; } + virtual unsigned int threadLocalMemorySize() const CADET_NOEXCEPT + { + return 0; + } #ifdef CADET_BENCHMARK_MODE - virtual std::vector benchmarkTimings() const { return std::vector(0); } - virtual char const* const* benchmarkDescriptions() const { return nullptr; } + virtual std::vector benchmarkTimings() const + { + return std::vector(0); + } + virtual char const* const* benchmarkDescriptions() const + { + return nullptr; + } #endif protected: - UnitOpIdx _unitOpIdx; //!< Unit operation index - unsigned int _nComp; //!< Number of components + unsigned int _nComp; //!< Number of components class Exporter : public ISolutionExporter { public: - - Exporter(unsigned int nComp, double const* data) : _data(data), _nComp(nComp) { } - - virtual bool hasParticleFlux() const CADET_NOEXCEPT { return false; } - virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT { return false; } - virtual bool hasSolidPhase() const CADET_NOEXCEPT { return false; } - virtual bool hasVolume() const CADET_NOEXCEPT { return false; } - virtual bool isParticleLumped() const CADET_NOEXCEPT { return false; } - virtual bool hasPrimaryExtent() const CADET_NOEXCEPT { return false; } - - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _nComp; } - virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numParticleTypes() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return 0; } - virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT { return 0; } - - virtual int writeMobilePhase(double* buffer) const { return 0; } - virtual int writeSolidPhase(double* buffer) const { return 0; } - virtual int writeParticleMobilePhase(double* buffer) const { return 0; } - virtual int writeSolidPhase(unsigned int parType, double* buffer) const { return 0; } - virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const { return 0; } - virtual int writeParticleFlux(double* buffer) const { return 0; } - virtual int writeParticleFlux(unsigned int parType, double* buffer) const { return 0; } - virtual int writeVolume(double* buffer) const { return 0; } + Exporter(unsigned int nComp, double const* data) : _data(data), _nComp(nComp) + { + } + + virtual bool hasParticleFlux() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasSolidPhase() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasVolume() const CADET_NOEXCEPT + { + return false; + } + virtual bool isParticleLumped() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasPrimaryExtent() const CADET_NOEXCEPT + { + return false; + } + + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _nComp; + } + virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numParticleTypes() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT + { + return 0; + } + + virtual int writeMobilePhase(double* buffer) const + { + return 0; + } + virtual int writeSolidPhase(double* buffer) const + { + return 0; + } + virtual int writeParticleMobilePhase(double* buffer) const + { + return 0; + } + virtual int writeSolidPhase(unsigned int parType, double* buffer) const + { + return 0; + } + virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const + { + return 0; + } + virtual int writeParticleFlux(double* buffer) const + { + return 0; + } + virtual int writeParticleFlux(unsigned int parType, double* buffer) const + { + return 0; + } + virtual int writeVolume(double* buffer) const + { + return 0; + } virtual int writeInlet(unsigned int port, double* buffer) const; virtual int writeInlet(double* buffer) const; virtual int writeOutlet(unsigned int port, double* buffer) const; virtual int writeOutlet(double* buffer) const; - virtual int writePrimaryCoordinates(double* coords) const { return 0; } - virtual int writeSecondaryCoordinates(double* coords) const { return 0; } - virtual int writeParticleCoordinates(unsigned int parType, double* coords) const { return 0; } + virtual int writePrimaryCoordinates(double* coords) const + { + return 0; + } + virtual int writeSecondaryCoordinates(double* coords) const + { + return 0; + } + virtual int writeParticleCoordinates(unsigned int parType, double* coords) const + { + return 0; + } protected: double const* const _data; @@ -204,4 +384,4 @@ class OutletModel : public IUnitOperation } // namespace model } // namespace cadet -#endif // LIBCADET_OUTLETMODEL_HPP_ +#endif // LIBCADET_OUTLETMODEL_HPP_ diff --git a/src/libcadet/model/ParameterDependence.hpp b/src/libcadet/model/ParameterDependence.hpp index 2d56056b2..d288432a5 100644 --- a/src/libcadet/model/ParameterDependence.hpp +++ b/src/libcadet/model/ParameterDependence.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines parameter dependence interfaces. */ @@ -49,8 +49,9 @@ namespace model class IParameterStateDependence { public: - - virtual ~IParameterStateDependence() CADET_NOEXCEPT { } + virtual ~IParameterStateDependence() CADET_NOEXCEPT + { + } /** * @brief Returns the name of the parameter dependence @@ -81,42 +82,46 @@ class IParameterStateDependence * @brief Sets the number of components in the model * @details This function is called prior to configure() by the underlying model. * It can only be called once. Model parameters are configured by configure(). - * + * * @param [in] paramProvider Parameter provider * @param [in] nComp Number of components * @param [in] nBound Array of size @p nComp which contains the number of bound states for each component - * @param [in] boundOffset Array of size @p nComp with offsets to the first bound state of each component beginning from the solid phase + * @param [in] boundOffset Array of size @p nComp with offsets to the first bound state of each component beginning + * from the solid phase */ - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) = 0; + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) = 0; /** - * @brief Configures the model by extracting all non-structural parameters (e.g., model parameters) from the given @p paramProvider + * @brief Configures the model by extracting all non-structural parameters (e.g., model parameters) from the given + * @p paramProvider * @details The scope of the cadet::IParameterProvider is left unchanged on return. - * + * * The structure of the model is left unchanged, that is, the number of degrees of * freedom stays the same (e.g., number of bound states is left unchanged). Only * true (non-structural) model parameters are read and changed. - * + * * This function may only be called if configureModelDiscretization() has been called * in the past. Contrary to configureModelDiscretization(), it can be called multiple * times. - * + * * @param [in] paramProvider Parameter provider * @param [in] unitOpIdx Index of the unit operation this parameter dependence belongs to * @param [in] parTypeIdx Index of the particle type this parameter dependence belongs to * @param [in] name Name of the parameter * @return @c true if the configuration was successful, otherwise @c false */ - virtual bool configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, const std::string& name) = 0; + virtual bool configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + const std::string& name) = 0; /** - * @brief Returns the maximum number of non-zero Jacobian elements per row in the liquid cell + * @brief Returns the maximum number of non-zero Jacobian elements per row in the liquid cell * @return Maximum number of non-zero Jacobian elements per row in liquid cell */ virtual int jacobianElementsPerRowLiquid() const CADET_NOEXCEPT = 0; /** - * @brief Returns the maximum number of non-zero Jacobian elements per row in the combined cell + * @brief Returns the maximum number of non-zero Jacobian elements per row in the combined cell * @return Maximum number of non-zero Jacobian elements per row in combined cell */ virtual int jacobianElementsPerRowCombined() const CADET_NOEXCEPT = 0; @@ -137,7 +142,7 @@ class IParameterStateDependence /** * @brief Sets a parameter value * @details The parameter identified by its unique parameter is set to the given value. - * + * * @param [in] pId ParameterId that identifies the parameter uniquely * @param [in] value Value of the parameter * @return @c true if the parameter has been successfully set to the given value, @@ -157,15 +162,18 @@ class IParameterStateDependence /** * @brief Evaluates the parameter of component @p comp in one liquid phase cell * @details This function is called simultaneously from multiple threads. - * - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] param Value of the unmodified parameter * @param [in] y Pointer to first component in the current cell * @param [in] comp Index of the component the parameter belongs to (or @c -1 if independent of components) * @return Actual parameter value */ - virtual active liquidParameter(const ColumnPosition& colPos, const active& param, active const* y, int comp) const = 0; - virtual active liquidParameter(const ColumnPosition& colPos, const active& param, double const* y, int comp) const = 0; + virtual active liquidParameter(const ColumnPosition& colPos, const active& param, active const* y, + int comp) const = 0; + virtual active liquidParameter(const ColumnPosition& colPos, const active& param, double const* y, + int comp) const = 0; virtual active liquidParameter(const ColumnPosition& colPos, double param, active const* y, int comp) const = 0; virtual double liquidParameter(const ColumnPosition& colPos, double param, double const* y, int comp) const = 0; @@ -174,40 +182,45 @@ class IParameterStateDependence * @details Adds the Jacobian of the parameter dependence for the given component and liquid * phase cell to the full Jacobian. The Jacobian is premultiplied with a given * factor. That is, this function realizes - * \f[ \begin{align} + * \f[ \begin{align} * J = J + \gamma J_P * \end{align} \f] * where @f$ J_P @f$ is the Jacobian of the parameter dependence. - * + * * This function is called simultaneously from multiple threads. * It can be left out (empty implementation) if AD is used to evaluate the Jacobian. * - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] param Value of the unmodified parameter * @param [in] y Pointer to first component in the current cell * @param [in] comp Index of the component the parameter belongs to (or @c -1 if independent of components) * @param [in] factor Factor @f$ \gamma @f$ * @param [in] offset Diaognal offset in the row iterator * @param [in] row Row index - * @param [in,out] jac Row iterator pointing to the row of component @p comp in the underlying matrix that stores the Jacobian + * @param [in,out] jac Row iterator pointing to the row of component @p comp in the underlying matrix that stores + * the Jacobian */ - virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, linalg::BandMatrix::RowIterator jac) const = 0; - virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, linalg::DenseBandedRowIterator jac) const = 0; + virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, + double factor, int offset, linalg::BandMatrix::RowIterator jac) const = 0; + virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, + double factor, int offset, linalg::DenseBandedRowIterator jac) const = 0; /** * @brief Adds the analytical Jacobian of the parameter dependence for one liquid phase cell * @details Adds the Jacobian of the parameter dependence for the given component and liquid * phase cell to the full Jacobian. The Jacobian is premultiplied with a given * factor. That is, this function realizes - * \f[ \begin{align} + * \f[ \begin{align} * J = J + \gamma J_P * \end{align} \f] * where @f$ J_P @f$ is the Jacobian of the parameter dependence. - * + * * This function is called simultaneously from multiple threads. * It can be left out (empty implementation) if AD is used to evaluate the Jacobian. * - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] param Value of the unmodified parameter * @param [in] y Pointer to first component in the current cell * @param [in] comp Index of the component the parameter belongs to (or @c -1 if independent of components) @@ -216,79 +229,98 @@ class IParameterStateDependence * @param [in] row Row index * @param [in,out] jac Jacobian */ - virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const = 0; + virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, + double factor, int offset, int row, + linalg::DoubleSparseMatrix& jac) const = 0; /** * @brief Evaluates the parameter of component @p comp in the liquid phase of one combined phase cell * @details This function is called simultaneously from multiple threads. - * - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] param Value of the unmodified parameter * @param [in] yLiquid Pointer to first component in the current cell's liquid phase * @param [in] ySolid Pointer to first component in the current cell's solid phase * @param [in] comp Index of the component the parameter belongs to (or @c -1 if independent of components) * @return Actual parameter value */ - virtual active combinedParameterLiquid(const ColumnPosition& colPos, const active& param, active const* yLiquid, active const* ySolid, int comp) const = 0; - virtual active combinedParameterLiquid(const ColumnPosition& colPos, const active& param, double const* yLiquid, double const* ySolid, int comp) const = 0; - virtual active combinedParameterLiquid(const ColumnPosition& colPos, double param, active const* yLiquid, active const* ySolid, int comp) const = 0; - virtual double combinedParameterLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp) const = 0; + virtual active combinedParameterLiquid(const ColumnPosition& colPos, const active& param, active const* yLiquid, + active const* ySolid, int comp) const = 0; + virtual active combinedParameterLiquid(const ColumnPosition& colPos, const active& param, double const* yLiquid, + double const* ySolid, int comp) const = 0; + virtual active combinedParameterLiquid(const ColumnPosition& colPos, double param, active const* yLiquid, + active const* ySolid, int comp) const = 0; + virtual double combinedParameterLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp) const = 0; /** * @brief Evaluates the parameter of component @p comp in the solid phase of one combined phase cell * @details This function is called simultaneously from multiple threads. - * - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] param Value of the unmodified parameter * @param [in] yLiquid Pointer to first component in the current cell's liquid phase * @param [in] ySolid Pointer to first component in the current cell's solid phase * @param [in] bnd Index of the bound state the parameter belongs to (or @c -1 if independent of bound states) * @return Actual parameter value */ - virtual active combinedParameterSolid(const ColumnPosition& colPos, const active& param, active const* yLiquid, active const* ySolid, int bnd) const = 0; - virtual active combinedParameterSolid(const ColumnPosition& colPos, const active& param, double const* yLiquid, double const* ySolid, int bnd) const = 0; - virtual active combinedParameterSolid(const ColumnPosition& colPos, double param, active const* yLiquid, active const* ySolid, int bnd) const = 0; - virtual double combinedParameterSolid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd) const = 0; + virtual active combinedParameterSolid(const ColumnPosition& colPos, const active& param, active const* yLiquid, + active const* ySolid, int bnd) const = 0; + virtual active combinedParameterSolid(const ColumnPosition& colPos, const active& param, double const* yLiquid, + double const* ySolid, int bnd) const = 0; + virtual active combinedParameterSolid(const ColumnPosition& colPos, double param, active const* yLiquid, + active const* ySolid, int bnd) const = 0; + virtual double combinedParameterSolid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd) const = 0; /** * @brief Adds the analytical Jacobian of the parameter dependence for the liquid phase in one combined phase cell * @details Adds the Jacobian of the parameter dependence for the given component and combined * phase cell to the full Jacobian. The Jacobian is premultiplied with a given * factor. That is, this function realizes - * \f[ \begin{align} + * \f[ \begin{align} * J = J + \gamma J_P * \end{align} \f] * where @f$ J_P @f$ is the Jacobian of the parameter dependence. - * + * * This function is called simultaneously from multiple threads. * It can be left out (empty implementation) if AD is used to evaluate the Jacobian. * - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] param Value of the unmodified parameter * @param [in] yLiquid Pointer to first component in the current cell's liquid phase * @param [in] ySolid Pointer to first component in the current cell's solid phase * @param [in] comp Index of the component the parameter belongs to (or @c -1 if independent of components) * @param [in] factor Factor @f$ \gamma @f$ * @param [in] offset Diaognal offset in the row iterator - * @param [in,out] jac Row iterator pointing to the row of component @p comp in the underlying matrix that stores the Jacobian + * @param [in,out] jac Row iterator pointing to the row of component @p comp in the underlying matrix that stores + * the Jacobian */ - virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, linalg::BandMatrix::RowIterator jac) const = 0; - virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, linalg::DenseBandedRowIterator jac) const = 0; + virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp, double factor, int offset, + linalg::BandMatrix::RowIterator jac) const = 0; + virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp, double factor, int offset, + linalg::DenseBandedRowIterator jac) const = 0; /** * @brief Adds the analytical Jacobian of the parameter dependence for the liquid phase in one combined phase cell * @details Adds the Jacobian of the parameter dependence for the given component and combined * phase cell to the full Jacobian. The Jacobian is premultiplied with a given * factor. That is, this function realizes - * \f[ \begin{align} + * \f[ \begin{align} * J = J + \gamma J_P * \end{align} \f] * where @f$ J_P @f$ is the Jacobian of the parameter dependence. - * + * * This function is called simultaneously from multiple threads. * It can be left out (empty implementation) if AD is used to evaluate the Jacobian. * - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] param Value of the unmodified parameter * @param [in] yLiquid Pointer to first component in the current cell's liquid phase * @param [in] ySolid Pointer to first component in the current cell's solid phase @@ -298,47 +330,56 @@ class IParameterStateDependence * @param [in] row Row index * @param [in,out] jac Jacobian */ - virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const = 0; + virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp, double factor, int offset, int row, + linalg::DoubleSparseMatrix& jac) const = 0; /** * @brief Adds the analytical Jacobian of the parameter dependence for the solid phase in one combined phase cell * @details Adds the Jacobian of the parameter dependence for the given bound state and combined * phase cell to the full Jacobian. The Jacobian is premultiplied with a given * factor. That is, this function realizes - * \f[ \begin{align} + * \f[ \begin{align} * J = J + \gamma J_P * \end{align} \f] * where @f$ J_P @f$ is the Jacobian of the parameter dependence. - * + * * This function is called simultaneously from multiple threads. * It can be left out (empty implementation) if AD is used to evaluate the Jacobian. * - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] param Value of the unmodified parameter * @param [in] yLiquid Pointer to first component in the current cell's liquid phase * @param [in] ySolid Pointer to first component in the current cell's solid phase * @param [in] bnd Index of the bound state the parameter belongs to (or @c -1 if independent of bound states) * @param [in] factor Factor @f$ \gamma @f$ * @param [in] offset Diaognal offset in the row iterator - * @param [in,out] jac Row iterator pointing to the row of bound state @p bnd in the underlying matrix that stores the Jacobian + * @param [in,out] jac Row iterator pointing to the row of bound state @p bnd in the underlying matrix that stores + * the Jacobian */ - virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, linalg::BandMatrix::RowIterator jac) const = 0; - virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, linalg::DenseBandedRowIterator jac) const = 0; + virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd, double factor, int offset, + linalg::BandMatrix::RowIterator jac) const = 0; + virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd, double factor, int offset, + linalg::DenseBandedRowIterator jac) const = 0; /** * @brief Adds the analytical Jacobian of the parameter dependence for the solid phase in one combined phase cell * @details Adds the Jacobian of the parameter dependence for the given bound state and combined * phase cell to the full Jacobian. The Jacobian is premultiplied with a given * factor. That is, this function realizes - * \f[ \begin{align} + * \f[ \begin{align} * J = J + \gamma J_P * \end{align} \f] * where @f$ J_P @f$ is the Jacobian of the parameter dependence. - * + * * This function is called simultaneously from multiple threads. * It can be left out (empty implementation) if AD is used to evaluate the Jacobian. * - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] param Value of the unmodified parameter * @param [in] yLiquid Pointer to first component in the current cell's liquid phase * @param [in] ySolid Pointer to first component in the current cell's solid phase @@ -348,7 +389,9 @@ class IParameterStateDependence * @param [in] row Row index * @param [in,out] jac Jacobian */ - virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const = 0; + virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd, double factor, int offset, int row, + linalg::DoubleSparseMatrix& jac) const = 0; protected: }; @@ -360,8 +403,9 @@ class IParameterStateDependence class IParameterParameterDependence { public: - - virtual ~IParameterParameterDependence() CADET_NOEXCEPT { } + virtual ~IParameterParameterDependence() CADET_NOEXCEPT + { + } /** * @brief Returns the name of the parameter dependence @@ -392,23 +436,24 @@ class IParameterParameterDependence * @brief Configures the parameter dependence * @details This function is called prior to configure() by the underlying model. * It can only be called once. Model parameters are configured by configure(). - * + * * @param [in] paramProvider Parameter provider */ virtual bool configureModelDiscretization(IParameterProvider& paramProvider) = 0; /** - * @brief Configures the model by extracting all non-structural parameters (e.g., model parameters) from the given @p paramProvider + * @brief Configures the model by extracting all non-structural parameters (e.g., model parameters) from the given + * @p paramProvider * @details The scope of the cadet::IParameterProvider is left unchanged on return. - * + * * The structure of the model is left unchanged, that is, the number of degrees of * freedom stays the same (e.g., number of bound states is left unchanged). Only * true (non-structural) model parameters are read and changed. - * + * * This function may only be called if configureModelDiscretization() has been called * in the past. Contrary to configureModelDiscretization(), it can be called multiple * times. - * + * * @param [in] paramProvider Parameter provider * @param [in] unitOpIdx Index of the unit operation this parameter dependence belongs to * @param [in] parTypeIdx Index of the particle type this parameter dependence belongs to @@ -416,7 +461,8 @@ class IParameterParameterDependence * @param [in] name Name of the parameter * @return @c true if the configuration was successful, otherwise @c false */ - virtual bool configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, BoundStateIdx bndIdx, const std::string& name) = 0; + virtual bool configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + BoundStateIdx bndIdx, const std::string& name) = 0; /** * @brief Checks whether a given parameter exists @@ -434,7 +480,7 @@ class IParameterParameterDependence /** * @brief Sets a parameter value * @details The parameter identified by its unique parameter is set to the given value. - * + * * @param [in] pId ParameterId that identifies the parameter uniquely * @param [in] value Value of the parameter * @return @c true if the parameter has been successfully set to the given value, @@ -454,56 +500,68 @@ class IParameterParameterDependence /** * @brief Evaluates the parameter * @details This function is called simultaneously from multiple threads. - * + * * @param [in] model Model that owns this parameter dependence - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] comp Index of the component the parameter belongs to (or @c -1 if independent of components) - * @param [in] parType Index of the particle type the parameter belongs to (or @c -1 if independent of particle types) + * @param [in] parType Index of the particle type the parameter belongs to (or @c -1 if independent of particle + * types) * @param [in] bnd Index of the bound state the parameter belongs to (or @c -1 if independent of bound states) * @return Actual parameter value */ - virtual double getValue(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd) const = 0; + virtual double getValue(const IModel& model, const ColumnPosition& colPos, int comp, int parType, + int bnd) const = 0; /** * @brief Evaluates the parameter * @details This function is called simultaneously from multiple threads. - * + * * @param [in] model Model that owns this parameter dependence - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] comp Index of the component the parameter belongs to (or @c -1 if independent of components) - * @param [in] parType Index of the particle type the parameter belongs to (or @c -1 if independent of particle types) + * @param [in] parType Index of the particle type the parameter belongs to (or @c -1 if independent of particle + * types) * @param [in] bnd Index of the bound state the parameter belongs to (or @c -1 if independent of bound states) * @return Actual parameter value */ - virtual active getValueActive(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd) const = 0; + virtual active getValueActive(const IModel& model, const ColumnPosition& colPos, int comp, int parType, + int bnd) const = 0; /** * @brief Evaluates the parameter * @details This function is called simultaneously from multiple threads. - * + * * @param [in] model Model that owns this parameter dependence - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] comp Index of the component the parameter belongs to (or @c -1 if independent of components) - * @param [in] parType Index of the particle type the parameter belongs to (or @c -1 if independent of particle types) + * @param [in] parType Index of the particle type the parameter belongs to (or @c -1 if independent of particle + * types) * @param [in] bnd Index of the bound state the parameter belongs to (or @c -1 if independent of bound states) * @param [in] val Additional parameter-dependent value * @return Actual parameter value */ - virtual double getValue(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, double val) const = 0; + virtual double getValue(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, + double val) const = 0; /** * @brief Evaluates the parameter * @details This function is called simultaneously from multiple threads. - * + * * @param [in] model Model that owns this parameter dependence - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] comp Index of the component the parameter belongs to (or @c -1 if independent of components) - * @param [in] parType Index of the particle type the parameter belongs to (or @c -1 if independent of particle types) + * @param [in] parType Index of the particle type the parameter belongs to (or @c -1 if independent of particle + * types) * @param [in] bnd Index of the bound state the parameter belongs to (or @c -1 if independent of bound states) * @param [in] val Additional parameter-dependent value * @return Actual parameter value */ - virtual active getValue(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, const active& val) const = 0; + virtual active getValue(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, + const active& val) const = 0; protected: }; @@ -511,4 +569,4 @@ class IParameterParameterDependence } // namespace model } // namespace cadet -#endif // LIBCADET_PARAMDEPINTERFACE_HPP_ +#endif // LIBCADET_PARAMDEPINTERFACE_HPP_ diff --git a/src/libcadet/model/ParameterMultiplexing.cpp b/src/libcadet/model/ParameterMultiplexing.cpp index d78373d93..16fe23649 100644 --- a/src/libcadet/model/ParameterMultiplexing.cpp +++ b/src/libcadet/model/ParameterMultiplexing.cpp @@ -23,25 +23,33 @@ namespace cadet namespace model { -bool readAndRegisterMultiplexTypeParam(IParameterProvider& paramProvider, std::unordered_map& parameters, std::vector& values, const std::string& name, unsigned int nParType, UnitOpIdx uoi) +bool readAndRegisterMultiplexTypeParam(IParameterProvider& paramProvider, + std::unordered_map& parameters, + std::vector& values, const std::string& name, unsigned int nParType, + UnitOpIdx uoi) { const bool singleValue = readScalarParameterOrArray(values, paramProvider, name, nParType); const StringHash nameHash = hashStringRuntime(name); if (singleValue) - parameters[makeParamId(nameHash, uoi, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &values[0]; + parameters[makeParamId(nameHash, uoi, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = + &values[0]; else - registerParam1DArray(parameters, values, [=](bool multi, unsigned int type) { return makeParamId(nameHash, uoi, CompIndep, type, BoundStateIndep, ReactionIndep, SectionIndep); }); + registerParam1DArray(parameters, values, [=](bool multi, unsigned int type) { + return makeParamId(nameHash, uoi, CompIndep, type, BoundStateIndep, ReactionIndep, SectionIndep); + }); return singleValue; } -bool multiplexTypeParameterValue(const ParameterId& pId, StringHash nameHash, bool mode, std::vector& data, double value, std::unordered_set const* sensParams) +bool multiplexTypeParameterValue(const ParameterId& pId, StringHash nameHash, bool mode, std::vector& data, + double value, std::unordered_set const* sensParams) { if (!mode || (pId.name != nameHash)) return false; - if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) + if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) return false; if (sensParams && !contains(*sensParams, &data[0])) return false; @@ -52,12 +60,14 @@ bool multiplexTypeParameterValue(const ParameterId& pId, StringHash nameHash, bo return true; } -bool multiplexTypeParameterAD(const ParameterId& pId, StringHash nameHash, bool mode, std::vector& data, unsigned int adDirection, double adValue, std::unordered_set& sensParams) +bool multiplexTypeParameterAD(const ParameterId& pId, StringHash nameHash, bool mode, std::vector& data, + unsigned int adDirection, double adValue, std::unordered_set& sensParams) { if (!mode || (pId.name != nameHash)) return false; - if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) + if ((pId.section != SectionIndep) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) return false; sensParams.insert(&data[0]); @@ -67,9 +77,10 @@ bool multiplexTypeParameterAD(const ParameterId& pId, StringHash nameHash, bool return true; } - - -MultiplexMode readAndRegisterMultiplexCompTypeSecParam(IParameterProvider& paramProvider, std::unordered_map& parameters, std::vector& values, const std::string& name, unsigned int nParType, unsigned int nComp, UnitOpIdx uoi) +MultiplexMode readAndRegisterMultiplexCompTypeSecParam(IParameterProvider& paramProvider, + std::unordered_map& parameters, + std::vector& values, const std::string& name, + unsigned int nParType, unsigned int nComp, UnitOpIdx uoi) { MultiplexMode mode = MultiplexMode::Independent; readScalarParameterOrArray(values, paramProvider, name, 1); @@ -80,25 +91,31 @@ MultiplexMode readAndRegisterMultiplexCompTypeSecParam(IParameterProvider& param { mode = MultiplexMode::Component; if (values.size() != nComp) - throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nComp) + ")"); + throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + + "_MULTIPLEX (should be " + std::to_string(nComp) + ")"); } else if (modeConfig == 1) { mode = MultiplexMode::ComponentSection; if ((values.size() % nComp) != 0) - throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be positive multiple of " + std::to_string(nComp) + ")"); + throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + + "_MULTIPLEX (should be positive multiple of " + std::to_string(nComp) + + ")"); } else if (modeConfig == 2) { mode = MultiplexMode::ComponentType; if (values.size() != nComp * nParType) - throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nComp * nParType) + ")"); + throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + + "_MULTIPLEX (should be " + std::to_string(nComp * nParType) + ")"); } else if (modeConfig == 3) { mode = MultiplexMode::ComponentSectionType; if ((values.size() % (nComp * nParType)) != 0) - throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be positive multiple of " + std::to_string(nComp * nParType) + ")"); + throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + + "_MULTIPLEX (should be positive multiple of " + + std::to_string(nComp * nParType) + ")"); } } else @@ -112,171 +129,181 @@ MultiplexMode readAndRegisterMultiplexCompTypeSecParam(IParameterProvider& param else if ((values.size() % nComp) == 0) mode = MultiplexMode::ComponentSection; else - throw InvalidParameterException("Could not infer multiplex mode of field " + name + ", set " + name + "_MULTIPLEX or change number of elements"); + throw InvalidParameterException("Could not infer multiplex mode of field " + name + ", set " + name + + "_MULTIPLEX or change number of elements"); } const StringHash nameHash = hashStringRuntime(name); switch (mode) { - case MultiplexMode::Component: - { - std::vector p(nComp * nParType); - for (unsigned int s = 0; s < nParType; ++s) - std::copy(values.begin(), values.end(), p.begin() + s * nComp); + case MultiplexMode::Component: { + std::vector p(nComp * nParType); + for (unsigned int s = 0; s < nParType; ++s) + std::copy(values.begin(), values.end(), p.begin() + s * nComp); - values = std::move(p); + values = std::move(p); - for (unsigned int s = 0; s < nComp; ++s) - parameters[makeParamId(nameHash, uoi, s, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &values[s]; - } - break; - case MultiplexMode::ComponentSection: + for (unsigned int s = 0; s < nComp; ++s) + parameters[makeParamId(nameHash, uoi, s, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = + &values[s]; + } + break; + case MultiplexMode::ComponentSection: { + std::vector p(values.size() * nParType); + for (std::size_t s = 0; s < values.size() / nComp; ++s) + { + for (unsigned int pt = 0; pt < nParType; ++pt) { - std::vector p(values.size() * nParType); - for (std::size_t s = 0; s < values.size() / nComp; ++s) - { - for (unsigned int pt = 0; pt < nParType; ++pt) - { - std::copy(values.begin() + s * nComp, values.begin() + (s+1) * nComp, p.begin() + s * nParType * nComp + pt * nComp); - } - } + std::copy(values.begin() + s * nComp, values.begin() + (s + 1) * nComp, + p.begin() + s * nParType * nComp + pt * nComp); + } + } - values = std::move(p); + values = std::move(p); - for (std::size_t s = 0; s < values.size() / nComp; ++s) - { - for (unsigned int i = 0; i < nComp; ++i) - parameters[makeParamId(nameHash, uoi, i, ParTypeIndep, BoundStateIndep, ReactionIndep, s)] = &values[s * nParType * nComp + i]; - } - } - break; - case MultiplexMode::ComponentType: - case MultiplexMode::ComponentSectionType: - registerParam3DArray(parameters, values, [=](bool multi, unsigned int sec, unsigned int pt, unsigned int comp) { return makeParamId(nameHash, uoi, comp, pt, BoundStateIndep, ReactionIndep, multi ? sec : SectionIndep); }, nComp, nParType); - break; - case MultiplexMode::RadialSection: - case MultiplexMode::Independent: - case MultiplexMode::ComponentRadial: - case MultiplexMode::ComponentRadialSection: - case MultiplexMode::Axial: - case MultiplexMode::Section: - case MultiplexMode::Type: - case MultiplexMode::Radial: - case MultiplexMode::AxialRadial: - cadet_assert(false); - break; + for (std::size_t s = 0; s < values.size() / nComp; ++s) + { + for (unsigned int i = 0; i < nComp; ++i) + parameters[makeParamId(nameHash, uoi, i, ParTypeIndep, BoundStateIndep, ReactionIndep, s)] = + &values[s * nParType * nComp + i]; + } + } + break; + case MultiplexMode::ComponentType: + case MultiplexMode::ComponentSectionType: + registerParam3DArray( + parameters, values, + [=](bool multi, unsigned int sec, unsigned int pt, unsigned int comp) { + return makeParamId(nameHash, uoi, comp, pt, BoundStateIndep, ReactionIndep, multi ? sec : SectionIndep); + }, + nComp, nParType); + break; + case MultiplexMode::RadialSection: + case MultiplexMode::Independent: + case MultiplexMode::ComponentRadial: + case MultiplexMode::ComponentRadialSection: + case MultiplexMode::Axial: + case MultiplexMode::Section: + case MultiplexMode::Type: + case MultiplexMode::Radial: + case MultiplexMode::AxialRadial: + cadet_assert(false); + break; } return mode; } -bool multiplexCompTypeSecParameterValue(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, std::vector& data, unsigned int nParType, unsigned int nComp, double value, std::unordered_set const* sensParams) +bool multiplexCompTypeSecParameterValue(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, + std::vector& data, unsigned int nParType, unsigned int nComp, + double value, std::unordered_set const* sensParams) { if (pId.name != nameHash) return false; switch (mode) { - case MultiplexMode::Component: - { - if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState != BoundStateIndep) - || (pId.reaction != ReactionIndep) || (pId.section != SectionIndep)) - return false; + case MultiplexMode::Component: { + if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep) || (pId.section != SectionIndep)) + return false; - if (sensParams && !contains(*sensParams, &data[pId.component])) - return false; + if (sensParams && !contains(*sensParams, &data[pId.component])) + return false; - for (unsigned int i = 0; i < nParType; ++i) - data[i * nComp + pId.component].setValue(value); + for (unsigned int i = 0; i < nParType; ++i) + data[i * nComp + pId.component].setValue(value); - return true; - } - case MultiplexMode::ComponentSection: - { - if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState != BoundStateIndep) - || (pId.reaction != ReactionIndep) || (pId.section == SectionIndep)) - return false; + return true; + } + case MultiplexMode::ComponentSection: { + if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep) || (pId.section == SectionIndep)) + return false; - if (sensParams && !contains(*sensParams, &data[pId.section * nComp * nParType + pId.component])) - return false; + if (sensParams && !contains(*sensParams, &data[pId.section * nComp * nParType + pId.component])) + return false; - for (unsigned int i = 0; i < nParType; ++i) - data[i * nComp + pId.section * nComp * nParType + pId.component].setValue(value); + for (unsigned int i = 0; i < nParType; ++i) + data[i * nComp + pId.section * nComp * nParType + pId.component].setValue(value); - return true; - } - case MultiplexMode::ComponentType: - case MultiplexMode::ComponentSectionType: - case MultiplexMode::RadialSection: - case MultiplexMode::Independent: - case MultiplexMode::ComponentRadial: - case MultiplexMode::ComponentRadialSection: - case MultiplexMode::Axial: - case MultiplexMode::Section: - case MultiplexMode::Type: - case MultiplexMode::Radial: - case MultiplexMode::AxialRadial: - cadet_assert(false); - break; + return true; + } + case MultiplexMode::ComponentType: + case MultiplexMode::ComponentSectionType: + case MultiplexMode::RadialSection: + case MultiplexMode::Independent: + case MultiplexMode::ComponentRadial: + case MultiplexMode::ComponentRadialSection: + case MultiplexMode::Axial: + case MultiplexMode::Section: + case MultiplexMode::Type: + case MultiplexMode::Radial: + case MultiplexMode::AxialRadial: + cadet_assert(false); + break; } return false; } -bool multiplexCompTypeSecParameterAD(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, std::vector& data, unsigned int nParType, unsigned int nComp, unsigned int adDirection, double adValue, std::unordered_set& sensParams) +bool multiplexCompTypeSecParameterAD(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, + std::vector& data, unsigned int nParType, unsigned int nComp, + unsigned int adDirection, double adValue, std::unordered_set& sensParams) { if (pId.name != nameHash) return false; switch (mode) { - case MultiplexMode::Component: - { - if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState != BoundStateIndep) - || (pId.reaction != ReactionIndep) || (pId.section != SectionIndep)) - return false; + case MultiplexMode::Component: { + if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep) || (pId.section != SectionIndep)) + return false; - sensParams.insert(&data[pId.component]); + sensParams.insert(&data[pId.component]); - for (unsigned int i = 0; i < nParType; ++i) - data[i * nComp + pId.component].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nParType; ++i) + data[i * nComp + pId.component].setADValue(adDirection, adValue); - return true; - } - case MultiplexMode::ComponentSection: - { - if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState != BoundStateIndep) - || (pId.reaction != ReactionIndep) || (pId.section == SectionIndep)) - return false; + return true; + } + case MultiplexMode::ComponentSection: { + if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState != BoundStateIndep) || + (pId.reaction != ReactionIndep) || (pId.section == SectionIndep)) + return false; - sensParams.insert(&data[pId.section * nComp * nParType + pId.component]); + sensParams.insert(&data[pId.section * nComp * nParType + pId.component]); - for (unsigned int i = 0; i < nParType; ++i) - data[i * nComp + pId.section * nComp * nParType + pId.component].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nParType; ++i) + data[i * nComp + pId.section * nComp * nParType + pId.component].setADValue(adDirection, adValue); - return true; - } - case MultiplexMode::ComponentType: - case MultiplexMode::ComponentSectionType: - case MultiplexMode::RadialSection: - case MultiplexMode::Independent: - case MultiplexMode::ComponentRadial: - case MultiplexMode::ComponentRadialSection: - case MultiplexMode::Axial: - case MultiplexMode::Section: - case MultiplexMode::Type: - case MultiplexMode::Radial: - case MultiplexMode::AxialRadial: - cadet_assert(false); - break; + return true; + } + case MultiplexMode::ComponentType: + case MultiplexMode::ComponentSectionType: + case MultiplexMode::RadialSection: + case MultiplexMode::Independent: + case MultiplexMode::ComponentRadial: + case MultiplexMode::ComponentRadialSection: + case MultiplexMode::Axial: + case MultiplexMode::Section: + case MultiplexMode::Type: + case MultiplexMode::Radial: + case MultiplexMode::AxialRadial: + cadet_assert(false); + break; } return false; } - - -MultiplexMode readAndRegisterMultiplexBndCompTypeSecParam(IParameterProvider& paramProvider, std::unordered_map& parameters, std::vector& values, const std::string& name, unsigned int nParType, unsigned int nComp, unsigned int const* strideBound, unsigned int const* nBound, UnitOpIdx uoi) +MultiplexMode readAndRegisterMultiplexBndCompTypeSecParam(IParameterProvider& paramProvider, + std::unordered_map& parameters, + std::vector& values, const std::string& name, + unsigned int nParType, unsigned int nComp, + unsigned int const* strideBound, unsigned int const* nBound, + UnitOpIdx uoi) { const unsigned int nTotalBound = strideBound[nParType]; if (nTotalBound == 0) @@ -291,25 +318,31 @@ MultiplexMode readAndRegisterMultiplexBndCompTypeSecParam(IParameterProvider& pa { mode = MultiplexMode::Component; if (values.size() != strideBound[0]) - throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(strideBound[0]) + ")"); + throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + + "_MULTIPLEX (should be " + std::to_string(strideBound[0]) + ")"); } else if (modeConfig == 1) { mode = MultiplexMode::ComponentSection; if ((values.size() % strideBound[0]) != 0) - throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be positive multiple of " + std::to_string(strideBound[0]) + ")"); + throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + + "_MULTIPLEX (should be positive multiple of " + + std::to_string(strideBound[0]) + ")"); } else if (modeConfig == 2) { mode = MultiplexMode::ComponentType; if (values.size() != nTotalBound) - throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nTotalBound) + ")"); + throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + + "_MULTIPLEX (should be " + std::to_string(nTotalBound) + ")"); } else if (modeConfig == 3) { mode = MultiplexMode::ComponentSectionType; if ((values.size() % nTotalBound) != 0) - throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be positive multiple of " + std::to_string(nTotalBound) + ")"); + throw InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + + "_MULTIPLEX (should be positive multiple of " + + std::to_string(nTotalBound) + ")"); } } else @@ -323,88 +356,91 @@ MultiplexMode readAndRegisterMultiplexBndCompTypeSecParam(IParameterProvider& pa else if ((values.size() % strideBound[0]) == 0) mode = MultiplexMode::ComponentSection; else - throw InvalidParameterException("Could not infer multiplex mode of field " + name + ", set " + name + "_MULTIPLEX or change number of elements"); + throw InvalidParameterException("Could not infer multiplex mode of field " + name + ", set " + name + + "_MULTIPLEX or change number of elements"); } const StringHash nameHash = hashStringRuntime(name); switch (mode) { - case MultiplexMode::Component: - { - std::vector p(nTotalBound); - for (unsigned int s = 0; s < nParType; ++s) - std::copy(values.begin(), values.end(), p.begin() + s * strideBound[0]); + case MultiplexMode::Component: { + std::vector p(nTotalBound); + for (unsigned int s = 0; s < nParType; ++s) + std::copy(values.begin(), values.end(), p.begin() + s * strideBound[0]); - values = std::move(p); + values = std::move(p); - unsigned int idx = 0; - for (unsigned int c = 0; c < nComp; ++c) - { - for (unsigned int s = 0; s < nBound[c]; ++s, ++idx) - parameters[makeParamId(nameHash, uoi, c, ParTypeIndep, s, ReactionIndep, SectionIndep)] = &values[idx]; - } - } - break; - case MultiplexMode::ComponentSection: + unsigned int idx = 0; + for (unsigned int c = 0; c < nComp; ++c) + { + for (unsigned int s = 0; s < nBound[c]; ++s, ++idx) + parameters[makeParamId(nameHash, uoi, c, ParTypeIndep, s, ReactionIndep, SectionIndep)] = &values[idx]; + } + } + break; + case MultiplexMode::ComponentSection: { + std::vector p(values.size() / strideBound[0] * nTotalBound); + for (std::size_t s = 0; s < values.size() / strideBound[0]; ++s) + { + for (unsigned int pt = 0; pt < nParType; ++pt) { - std::vector p(values.size() / strideBound[0] * nTotalBound); - for (std::size_t s = 0; s < values.size() / strideBound[0]; ++s) - { - for (unsigned int pt = 0; pt < nParType; ++pt) - { - std::copy(values.begin() + s * strideBound[0], values.begin() + (s+1) * strideBound[0], p.begin() + s * nTotalBound + pt * strideBound[0]); - } - } + std::copy(values.begin() + s * strideBound[0], values.begin() + (s + 1) * strideBound[0], + p.begin() + s * nTotalBound + pt * strideBound[0]); + } + } - values = std::move(p); + values = std::move(p); - for (std::size_t sec = 0; sec < values.size() / strideBound[0]; ++sec) - { - unsigned int idx = nTotalBound * sec; - for (unsigned int c = 0; c < nComp; ++c) - { - for (unsigned int s = 0; s < nBound[c]; ++s, ++idx) - parameters[makeParamId(nameHash, uoi, c, ParTypeIndep, s, ReactionIndep, sec)] = &values[idx]; - } - } + for (std::size_t sec = 0; sec < values.size() / strideBound[0]; ++sec) + { + unsigned int idx = nTotalBound * sec; + for (unsigned int c = 0; c < nComp; ++c) + { + for (unsigned int s = 0; s < nBound[c]; ++s, ++idx) + parameters[makeParamId(nameHash, uoi, c, ParTypeIndep, s, ReactionIndep, sec)] = &values[idx]; } - break; - case MultiplexMode::ComponentType: - case MultiplexMode::ComponentSectionType: + } + } + break; + case MultiplexMode::ComponentType: + case MultiplexMode::ComponentSectionType: { + unsigned int idx = 0; + const bool multiSec = (values.size() != nTotalBound); + for (std::size_t sec = 0; sec < values.size() / nTotalBound; ++sec) + { + for (unsigned int type = 0; type < nParType; ++type) { - unsigned int idx = 0; - const bool multiSec = (values.size() != nTotalBound); - for (std::size_t sec = 0; sec < values.size() / nTotalBound; ++sec) + for (unsigned int c = 0; c < nComp; ++c) { - for (unsigned int type = 0; type < nParType; ++type) - { - for (unsigned int c = 0; c < nComp; ++c) - { - for (unsigned int s = 0; s < nBound[type * nComp + c]; ++s, ++idx) - parameters[makeParamId(nameHash, uoi, c, type, s, ReactionIndep, multiSec ? sec : SectionIndep)] = &values[idx]; - } - } + for (unsigned int s = 0; s < nBound[type * nComp + c]; ++s, ++idx) + parameters[makeParamId(nameHash, uoi, c, type, s, ReactionIndep, + multiSec ? sec : SectionIndep)] = &values[idx]; } } - break; - case MultiplexMode::RadialSection: - case MultiplexMode::Independent: - case MultiplexMode::ComponentRadial: - case MultiplexMode::ComponentRadialSection: - case MultiplexMode::Axial: - case MultiplexMode::Section: - case MultiplexMode::Type: - case MultiplexMode::Radial: - case MultiplexMode::AxialRadial: - cadet_assert(false); - break; + } + } + break; + case MultiplexMode::RadialSection: + case MultiplexMode::Independent: + case MultiplexMode::ComponentRadial: + case MultiplexMode::ComponentRadialSection: + case MultiplexMode::Axial: + case MultiplexMode::Section: + case MultiplexMode::Type: + case MultiplexMode::Radial: + case MultiplexMode::AxialRadial: + cadet_assert(false); + break; } return mode; } -bool multiplexBndCompTypeSecParameterValue(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, std::vector& data, - unsigned int nParType, unsigned int nComp, unsigned int const* strideBound, unsigned int const* nBound, unsigned int const* boundOffset, double value, std::unordered_set const* sensParams) +bool multiplexBndCompTypeSecParameterValue(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, + std::vector& data, unsigned int nParType, unsigned int nComp, + unsigned int const* strideBound, unsigned int const* nBound, + unsigned int const* boundOffset, double value, + std::unordered_set const* sensParams) { if (pId.name != nameHash) return false; @@ -414,54 +450,58 @@ bool multiplexBndCompTypeSecParameterValue(const ParameterId& pId, StringHash na switch (mode) { - case MultiplexMode::Component: - { - if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState == BoundStateIndep) - || (pId.reaction != ReactionIndep) || (pId.section != SectionIndep)) - return false; + case MultiplexMode::Component: { + if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState == BoundStateIndep) || + (pId.reaction != ReactionIndep) || (pId.section != SectionIndep)) + return false; - if (sensParams && !contains(*sensParams, &data[boundOffset[pId.component] + pId.boundState])) - return false; + if (sensParams && !contains(*sensParams, &data[boundOffset[pId.component] + pId.boundState])) + return false; - for (unsigned int i = 0; i < nParType; ++i) - data[boundOffset[pId.component] + pId.boundState + i * strideBound[0]].setValue(value); + for (unsigned int i = 0; i < nParType; ++i) + data[boundOffset[pId.component] + pId.boundState + i * strideBound[0]].setValue(value); - return true; - } - case MultiplexMode::ComponentSection: - { - if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState == BoundStateIndep) - || (pId.reaction != ReactionIndep) || (pId.section == SectionIndep)) - return false; + return true; + } + case MultiplexMode::ComponentSection: { + if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState == BoundStateIndep) || + (pId.reaction != ReactionIndep) || (pId.section == SectionIndep)) + return false; - if (sensParams && !contains(*sensParams, &data[pId.section * strideBound[nParType] + boundOffset[pId.component] + pId.boundState])) - return false; + if (sensParams && + !contains(*sensParams, + &data[pId.section * strideBound[nParType] + boundOffset[pId.component] + pId.boundState])) + return false; - for (unsigned int i = 0; i < nParType; ++i) - data[pId.section * strideBound[nParType] + boundOffset[pId.component] + pId.boundState + i * strideBound[0]].setValue(value); + for (unsigned int i = 0; i < nParType; ++i) + data[pId.section * strideBound[nParType] + boundOffset[pId.component] + pId.boundState + i * strideBound[0]] + .setValue(value); - return true; - } - case MultiplexMode::ComponentType: - case MultiplexMode::ComponentSectionType: - case MultiplexMode::RadialSection: - case MultiplexMode::Independent: - case MultiplexMode::ComponentRadial: - case MultiplexMode::ComponentRadialSection: - case MultiplexMode::Axial: - case MultiplexMode::Section: - case MultiplexMode::Type: - case MultiplexMode::Radial: - case MultiplexMode::AxialRadial: - cadet_assert(false); - break; + return true; + } + case MultiplexMode::ComponentType: + case MultiplexMode::ComponentSectionType: + case MultiplexMode::RadialSection: + case MultiplexMode::Independent: + case MultiplexMode::ComponentRadial: + case MultiplexMode::ComponentRadialSection: + case MultiplexMode::Axial: + case MultiplexMode::Section: + case MultiplexMode::Type: + case MultiplexMode::Radial: + case MultiplexMode::AxialRadial: + cadet_assert(false); + break; } return false; } -bool multiplexBndCompTypeSecParameterAD(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, std::vector& data, - unsigned int nParType, unsigned int nComp, unsigned int const* strideBound, unsigned int const* nBound, unsigned int const* boundOffset, unsigned int adDirection, double adValue, std::unordered_set& sensParams) +bool multiplexBndCompTypeSecParameterAD(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, + std::vector& data, unsigned int nParType, unsigned int nComp, + unsigned int const* strideBound, unsigned int const* nBound, + unsigned int const* boundOffset, unsigned int adDirection, double adValue, + std::unordered_set& sensParams) { if (pId.name != nameHash) return false; @@ -471,50 +511,49 @@ bool multiplexBndCompTypeSecParameterAD(const ParameterId& pId, StringHash nameH switch (mode) { - case MultiplexMode::Component: - { - if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState == BoundStateIndep) - || (pId.reaction != ReactionIndep) || (pId.section != SectionIndep)) - return false; + case MultiplexMode::Component: { + if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState == BoundStateIndep) || + (pId.reaction != ReactionIndep) || (pId.section != SectionIndep)) + return false; - sensParams.insert(&data[boundOffset[pId.component] + pId.boundState]); + sensParams.insert(&data[boundOffset[pId.component] + pId.boundState]); - for (unsigned int i = 0; i < nParType; ++i) - data[boundOffset[pId.component] + pId.boundState + i * strideBound[0]].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nParType; ++i) + data[boundOffset[pId.component] + pId.boundState + i * strideBound[0]].setADValue(adDirection, adValue); - return true; - } - case MultiplexMode::ComponentSection: - { - if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState == BoundStateIndep) - || (pId.reaction != ReactionIndep) || (pId.section == SectionIndep)) - return false; + return true; + } + case MultiplexMode::ComponentSection: { + if ((pId.component == CompIndep) || (pId.particleType != ParTypeIndep) || (pId.boundState == BoundStateIndep) || + (pId.reaction != ReactionIndep) || (pId.section == SectionIndep)) + return false; - sensParams.insert(&data[pId.section * strideBound[nParType] + boundOffset[pId.component] + pId.boundState]); + sensParams.insert(&data[pId.section * strideBound[nParType] + boundOffset[pId.component] + pId.boundState]); - for (unsigned int i = 0; i < nParType; ++i) - data[pId.section * strideBound[nParType] + boundOffset[pId.component] + pId.boundState + i * strideBound[0]].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nParType; ++i) + data[pId.section * strideBound[nParType] + boundOffset[pId.component] + pId.boundState + i * strideBound[0]] + .setADValue(adDirection, adValue); - return true; - } - case MultiplexMode::ComponentType: - case MultiplexMode::ComponentSectionType: - case MultiplexMode::RadialSection: - case MultiplexMode::Independent: - case MultiplexMode::ComponentRadial: - case MultiplexMode::ComponentRadialSection: - case MultiplexMode::Axial: - case MultiplexMode::Section: - case MultiplexMode::Type: - case MultiplexMode::Radial: - case MultiplexMode::AxialRadial: - cadet_assert(false); - break; + return true; + } + case MultiplexMode::ComponentType: + case MultiplexMode::ComponentSectionType: + case MultiplexMode::RadialSection: + case MultiplexMode::Independent: + case MultiplexMode::ComponentRadial: + case MultiplexMode::ComponentRadialSection: + case MultiplexMode::Axial: + case MultiplexMode::Section: + case MultiplexMode::Type: + case MultiplexMode::Radial: + case MultiplexMode::AxialRadial: + cadet_assert(false); + break; } return false; } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/ParameterMultiplexing.hpp b/src/libcadet/model/ParameterMultiplexing.hpp index b8d5582f4..32af06b04 100644 --- a/src/libcadet/model/ParameterMultiplexing.hpp +++ b/src/libcadet/model/ParameterMultiplexing.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides helper functions for parameter multiplexing */ @@ -34,249 +34,290 @@ class IParameterProvider; namespace model { - enum class MultiplexMode : int - { - Independent, - Radial, - RadialSection, - Component, - ComponentRadial, - ComponentRadialSection, - ComponentSection, - Section, - Axial, - AxialRadial, - Type, - ComponentType, - ComponentSectionType - }; +enum class MultiplexMode : int +{ + Independent, + Radial, + RadialSection, + Component, + ComponentRadial, + ComponentRadialSection, + ComponentSection, + Section, + Axial, + AxialRadial, + Type, + ComponentType, + ComponentSectionType +}; - /** - * @brief Checks whether the given multiplexing mode is section-dependent - * @param [in] mode MultiplexMode to check - * @return @c true if the mode is section-dependent, otherwise @c false - */ - inline bool isSectionDependent(MultiplexMode mode) CADET_NOEXCEPT - { - return (mode == MultiplexMode::RadialSection) || (mode == MultiplexMode::ComponentRadialSection) || (mode == MultiplexMode::ComponentSection) || (mode == MultiplexMode::Section) || (mode == MultiplexMode::ComponentSectionType); - } +/** + * @brief Checks whether the given multiplexing mode is section-dependent + * @param [in] mode MultiplexMode to check + * @return @c true if the mode is section-dependent, otherwise @c false + */ +inline bool isSectionDependent(MultiplexMode mode) CADET_NOEXCEPT +{ + return (mode == MultiplexMode::RadialSection) || (mode == MultiplexMode::ComponentRadialSection) || + (mode == MultiplexMode::ComponentSection) || (mode == MultiplexMode::Section) || + (mode == MultiplexMode::ComponentSectionType); +} - /** - * @brief Reads, multiplexes, and registers a parameter that may depend on particle type - * @details Reads the fiels NAME from the IParameterProvider. The multiplexing behavior is inferred from - * the length of the field NAME. - * - * Depending on the (inferred) mode, the read parameter values are multiplexed to a size of nParType. - * In case of multiplexing, only the first instance of the parameter is registered. - * - * @param [in] paramProvider ParameterProvider - * @param [in] parameters Map to register the parameters in - * @param [out] values Array to store the read parameters in - * @param [in] name Name of the parameter - * @param [in] nParType Number of particle types - * @param [in] uoi Unit operation index - * @return @c true if the parameter is multiplexed (single value), otherwise @c false - */ - bool readAndRegisterMultiplexTypeParam(IParameterProvider& paramProvider, std::unordered_map& parameters, std::vector& values, const std::string& name, unsigned int nParType, UnitOpIdx uoi); +/** + * @brief Reads, multiplexes, and registers a parameter that may depend on particle type + * @details Reads the fiels NAME from the IParameterProvider. The multiplexing behavior is inferred from + * the length of the field NAME. + * + * Depending on the (inferred) mode, the read parameter values are multiplexed to a size of nParType. + * In case of multiplexing, only the first instance of the parameter is registered. + * + * @param [in] paramProvider ParameterProvider + * @param [in] parameters Map to register the parameters in + * @param [out] values Array to store the read parameters in + * @param [in] name Name of the parameter + * @param [in] nParType Number of particle types + * @param [in] uoi Unit operation index + * @return @c true if the parameter is multiplexed (single value), otherwise @c false + */ +bool readAndRegisterMultiplexTypeParam(IParameterProvider& paramProvider, + std::unordered_map& parameters, + std::vector& values, const std::string& name, unsigned int nParType, + UnitOpIdx uoi); - /** - * @brief Sets the value of a multiplexed parameter that may depend on particle type - * @details Sets the value of a parameter and multiplexes the value onto all parameter instances. - * The parameter array is expected to have a size of nParType. - * - * In case of multiplexing, the given value is applied to all parameter instances. - * - * This function optionally checks whether the specified parameter is listed as sensitive. It uses the first - * parameter group / instance to check the @p sensParams set. - * - * @param [in] pId ParameterID - * @param [in] nameHash Hash of the parameter name - * @param [in] mode Multiplexing mode as obtained by readAndRegisterMultiplexTypeParam() - * @param [in,out] data Array with parameters whose values are updated - * @param [in] val Value to apply to the parameter(s) - * @param [in] sensParams If not @c nullptr, the set is checked for the specified parameter. - * If it is not contained in the set, the value is not applied to the parameter. - * @return @c true if the value has been applied, or @c false otherwise - */ - bool multiplexTypeParameterValue(const ParameterId& pId, StringHash nameHash, bool mode, std::vector& data, double value, std::unordered_set const* sensParams); +/** + * @brief Sets the value of a multiplexed parameter that may depend on particle type + * @details Sets the value of a parameter and multiplexes the value onto all parameter instances. + * The parameter array is expected to have a size of nParType. + * + * In case of multiplexing, the given value is applied to all parameter instances. + * + * This function optionally checks whether the specified parameter is listed as sensitive. It uses the first + * parameter group / instance to check the @p sensParams set. + * + * @param [in] pId ParameterID + * @param [in] nameHash Hash of the parameter name + * @param [in] mode Multiplexing mode as obtained by readAndRegisterMultiplexTypeParam() + * @param [in,out] data Array with parameters whose values are updated + * @param [in] val Value to apply to the parameter(s) + * @param [in] sensParams If not @c nullptr, the set is checked for the specified parameter. + * If it is not contained in the set, the value is not applied to the parameter. + * @return @c true if the value has been applied, or @c false otherwise + */ +bool multiplexTypeParameterValue(const ParameterId& pId, StringHash nameHash, bool mode, std::vector& data, + double value, std::unordered_set const* sensParams); - /** - * @brief Sets AD info of a multiplexed parameter that may depend on particle type - * @details Sets the AD direction and seed value of a parameter and multiplexes the info onto all parameter instances. - * The parameter array is expected to have a size of nParType. - * - * In case of multiplexing, the given AD info is applied to all parameter instances. - * - * The first parameter group / instance is added to the @p sensParams set in order to mark the parameter as sensitive. - * - * @param [in] pId ParameterID - * @param [in] nameHash Hash of the parameter name - * @param [in] mode Multiplexing mode as obtained by readAndRegisterMultiplexTypeParam() - * @param [in,out] data Array with parameters whose AD info are updated - * @param [in] adDirection AD direction - * @param [in] adValue AD seed value - * @param [in,out] sensParams The parameter(s) are marked sensitive by adding them to this set - * @return @c true if the parameter has been found, or @c false otherwise - */ - bool multiplexTypeParameterAD(const ParameterId& pId, StringHash nameHash, bool mode, std::vector& data, unsigned int adDirection, double adValue, std::unordered_set& sensParams); +/** + * @brief Sets AD info of a multiplexed parameter that may depend on particle type + * @details Sets the AD direction and seed value of a parameter and multiplexes the info onto all parameter instances. + * The parameter array is expected to have a size of nParType. + * + * In case of multiplexing, the given AD info is applied to all parameter instances. + * + * The first parameter group / instance is added to the @p sensParams set in order to mark the parameter as + * sensitive. + * + * @param [in] pId ParameterID + * @param [in] nameHash Hash of the parameter name + * @param [in] mode Multiplexing mode as obtained by readAndRegisterMultiplexTypeParam() + * @param [in,out] data Array with parameters whose AD info are updated + * @param [in] adDirection AD direction + * @param [in] adValue AD seed value + * @param [in,out] sensParams The parameter(s) are marked sensitive by adding them to this set + * @return @c true if the parameter has been found, or @c false otherwise + */ +bool multiplexTypeParameterAD(const ParameterId& pId, StringHash nameHash, bool mode, std::vector& data, + unsigned int adDirection, double adValue, std::unordered_set& sensParams); - /** - * @brief Reads, multiplexes, and registers a parameter that depends on particle type, component, and (optionally) section - * @details Reads the fiels NAME and NAME_MULTIPLEX (if available) from the IParameterProvider. If NAME_MULTIPLEX is missing, - * the multiplexing behavior is inferred from the length of the field NAME. - * - * Depending on the (inferred) mode, the read parameter values are multiplexed to a size of nComp * nParType, or - * nComp * nParType * nSections. The ordering is section-type-major. - * - * In case of multiplexing, only the first group of parameters is registered. For example, if one value for all - * components in each particle type is given (read array of size nParType), the first component of each particle - * type is registered. - * - * @param [in] paramProvider ParameterProvider - * @param [in] parameters Map to register the parameters in - * @param [out] values Array to store the read parameters in - * @param [in] name Name of the parameter - * @param [in] nParType Number of particle types - * @param [in] nComp Number of components - * @param [in] uoi Unit operation index - * @return Inferred or read multiplexing mode - */ - MultiplexMode readAndRegisterMultiplexCompTypeSecParam(IParameterProvider& paramProvider, std::unordered_map& parameters, std::vector& values, const std::string& name, unsigned int nParType, unsigned int nComp, UnitOpIdx uoi); +/** + * @brief Reads, multiplexes, and registers a parameter that depends on particle type, component, and (optionally) + * section + * @details Reads the fiels NAME and NAME_MULTIPLEX (if available) from the IParameterProvider. If NAME_MULTIPLEX is + * missing, the multiplexing behavior is inferred from the length of the field NAME. + * + * Depending on the (inferred) mode, the read parameter values are multiplexed to a size of nComp * nParType, + * or nComp * nParType * nSections. The ordering is section-type-major. + * + * In case of multiplexing, only the first group of parameters is registered. For example, if one value for all + * components in each particle type is given (read array of size nParType), the first component of each + * particle type is registered. + * + * @param [in] paramProvider ParameterProvider + * @param [in] parameters Map to register the parameters in + * @param [out] values Array to store the read parameters in + * @param [in] name Name of the parameter + * @param [in] nParType Number of particle types + * @param [in] nComp Number of components + * @param [in] uoi Unit operation index + * @return Inferred or read multiplexing mode + */ +MultiplexMode readAndRegisterMultiplexCompTypeSecParam(IParameterProvider& paramProvider, + std::unordered_map& parameters, + std::vector& values, const std::string& name, + unsigned int nParType, unsigned int nComp, UnitOpIdx uoi); - /** - * @brief Sets the value of a multiplexed parameter that depends on particle type, component, and (optionally) section - * @details Sets the value of a parameter and multiplexes the value onto all parameter instances. - * The parameter array is expected to have a size of nComp * nParType, or nComp * nParType * nSections. - * The ordering is section-type-major. - * - * In case of multiplexing, the given value is applied to all parameter instances. For example, if one parameter for all - * components in each particle type is given, the value is applied to all components of the specified particle type. - * - * This function optionally checks whether the specified parameter is listed as sensitive. It uses the first - * parameter group / instance to check the @p sensParams set. - * - * @param [in] pId ParameterID - * @param [in] nameHash Hash of the parameter name - * @param [in] mode Multiplexing mode as obtained by readAndRegisterMultiplexCompTypeSecParam() - * @param [in,out] data Array with parameters whose values are updated - * @param [in] nParType Number of particle types - * @param [in] nComp Number of components - * @param [in] val Value to apply to the parameter(s) - * @param [in] sensParams If not @c nullptr, the set is checked for the specified parameter. - * If it is not contained in the set, the value is not applied to the parameter. - * @return @c true if the value has been applied, or @c false otherwise - */ - bool multiplexCompTypeSecParameterValue(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, std::vector& data, unsigned int nParType, unsigned int nComp, double value, std::unordered_set const* sensParams); +/** + * @brief Sets the value of a multiplexed parameter that depends on particle type, component, and (optionally) section + * @details Sets the value of a parameter and multiplexes the value onto all parameter instances. + * The parameter array is expected to have a size of nComp * nParType, or nComp * nParType * nSections. + * The ordering is section-type-major. + * + * In case of multiplexing, the given value is applied to all parameter instances. For example, if one + * parameter for all components in each particle type is given, the value is applied to all components of the specified + * particle type. + * + * This function optionally checks whether the specified parameter is listed as sensitive. It uses the first + * parameter group / instance to check the @p sensParams set. + * + * @param [in] pId ParameterID + * @param [in] nameHash Hash of the parameter name + * @param [in] mode Multiplexing mode as obtained by readAndRegisterMultiplexCompTypeSecParam() + * @param [in,out] data Array with parameters whose values are updated + * @param [in] nParType Number of particle types + * @param [in] nComp Number of components + * @param [in] val Value to apply to the parameter(s) + * @param [in] sensParams If not @c nullptr, the set is checked for the specified parameter. + * If it is not contained in the set, the value is not applied to the parameter. + * @return @c true if the value has been applied, or @c false otherwise + */ +bool multiplexCompTypeSecParameterValue(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, + std::vector& data, unsigned int nParType, unsigned int nComp, + double value, std::unordered_set const* sensParams); - /** - * @brief Sets AD info of a multiplexed parameter that depends on particle type, component, and (optionally) section - * @details Sets the AD direction and seed value of a parameter and multiplexes the info onto all parameter instances. - * The parameter array is expected to have a size of nComp * nParType, or nComp * nParType * nSections. - * The ordering is section-type-major. - * - * In case of multiplexing, the given AD info is applied to all parameter instances. For example, if one parameter for all - * components in each particle type is given, the AD info is applied to all components of the specified particle type. - * - * The first parameter group / instance is added to the @p sensParams set in order to mark the parameter as sensitive. - * - * @param [in] pId ParameterID - * @param [in] nameHash Hash of the parameter name - * @param [in] mode Multiplexing mode as obtained by readAndRegisterMultiplexCompTypeSecParam() - * @param [in,out] data Array with parameters whose AD info are updated - * @param [in] nParType Number of particle types - * @param [in] nComp Number of components - * @param [in] adDirection AD direction - * @param [in] adValue AD seed value - * @param [in,out] sensParams The parameter(s) are marked sensitive by adding them to this set - * @return @c true if the parameter has been found, or @c false otherwise - */ - bool multiplexCompTypeSecParameterAD(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, std::vector& data, unsigned int nParType, unsigned int nComp, unsigned int adDirection, double adValue, std::unordered_set& sensParams); +/** + * @brief Sets AD info of a multiplexed parameter that depends on particle type, component, and (optionally) section + * @details Sets the AD direction and seed value of a parameter and multiplexes the info onto all parameter instances. + * The parameter array is expected to have a size of nComp * nParType, or nComp * nParType * nSections. + * The ordering is section-type-major. + * + * In case of multiplexing, the given AD info is applied to all parameter instances. For example, if one + * parameter for all components in each particle type is given, the AD info is applied to all components of the + * specified particle type. + * + * The first parameter group / instance is added to the @p sensParams set in order to mark the parameter as + * sensitive. + * + * @param [in] pId ParameterID + * @param [in] nameHash Hash of the parameter name + * @param [in] mode Multiplexing mode as obtained by readAndRegisterMultiplexCompTypeSecParam() + * @param [in,out] data Array with parameters whose AD info are updated + * @param [in] nParType Number of particle types + * @param [in] nComp Number of components + * @param [in] adDirection AD direction + * @param [in] adValue AD seed value + * @param [in,out] sensParams The parameter(s) are marked sensitive by adding them to this set + * @return @c true if the parameter has been found, or @c false otherwise + */ +bool multiplexCompTypeSecParameterAD(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, + std::vector& data, unsigned int nParType, unsigned int nComp, + unsigned int adDirection, double adValue, std::unordered_set& sensParams); - /** - * @brief Reads, multiplexes, and registers a parameter that depends on particle type, component, bound state, and (optionally) section - * @details Reads the fiels NAME and NAME_MULTIPLEX (if available) from the IParameterProvider. If NAME_MULTIPLEX is missing, - * the multiplexing behavior is inferred from the length of the field NAME. - * - * Depending on the (inferred) mode, the read parameter values are multiplexed to a size of nTotalBound, or - * nTotalBound * nSections. The ordering is section-type-component-major. - * - * In case of multiplexing, only the first group of parameters is registered. See readAndRegisterMultiplexCompTypeSecParam(). - * - * Components and bound states are treated together, that is, a parameter cannot be set independent of bound state - * but dependent on component (and vice versa). If the parameter is particle type independent, the same number of - * bound states per component is expected in all particle types (i.e., same binding model in all types). - * - * @param [in] paramProvider ParameterProvider - * @param [in] parameters Map to register the parameters in - * @param [out] values Array to store the read parameters in - * @param [in] name Name of the parameter - * @param [in] nParType Number of particle types - * @param [in] nComp Number of components - * @param [in] strideBound Array with number of bound states per particle type (additional last element is total number of bound states) - * @param [in] nBound Array with number of bound states per component and particle type in type-major ordering - * @param [in] uoi Unit operation index - * @return Inferred or read multiplexing mode - */ - MultiplexMode readAndRegisterMultiplexBndCompTypeSecParam(IParameterProvider& paramProvider, std::unordered_map& parameters, std::vector& values, const std::string& name, - unsigned int nParType, unsigned int nComp, unsigned int const* strideBound, unsigned int const* nBound, UnitOpIdx uoi); +/** + * @brief Reads, multiplexes, and registers a parameter that depends on particle type, component, bound state, and + * (optionally) section + * @details Reads the fiels NAME and NAME_MULTIPLEX (if available) from the IParameterProvider. If NAME_MULTIPLEX is + * missing, the multiplexing behavior is inferred from the length of the field NAME. + * + * Depending on the (inferred) mode, the read parameter values are multiplexed to a size of nTotalBound, or + * nTotalBound * nSections. The ordering is section-type-component-major. + * + * In case of multiplexing, only the first group of parameters is registered. See + * readAndRegisterMultiplexCompTypeSecParam(). + * + * Components and bound states are treated together, that is, a parameter cannot be set independent of bound + * state but dependent on component (and vice versa). If the parameter is particle type independent, the same number of + * bound states per component is expected in all particle types (i.e., same binding model in all types). + * + * @param [in] paramProvider ParameterProvider + * @param [in] parameters Map to register the parameters in + * @param [out] values Array to store the read parameters in + * @param [in] name Name of the parameter + * @param [in] nParType Number of particle types + * @param [in] nComp Number of components + * @param [in] strideBound Array with number of bound states per particle type (additional last element is total number + * of bound states) + * @param [in] nBound Array with number of bound states per component and particle type in type-major ordering + * @param [in] uoi Unit operation index + * @return Inferred or read multiplexing mode + */ +MultiplexMode readAndRegisterMultiplexBndCompTypeSecParam(IParameterProvider& paramProvider, + std::unordered_map& parameters, + std::vector& values, const std::string& name, + unsigned int nParType, unsigned int nComp, + unsigned int const* strideBound, unsigned int const* nBound, + UnitOpIdx uoi); - /** - * @brief Sets the value of a multiplexed parameter that depends on particle type, component, bound state, and (optionally) section - * @details Sets the value of a parameter and multiplexes the value onto all parameter instances. - * The parameter array is expected to have a size of nTotalBound, or nTotalBound * nSections. - * The ordering is section-type-component-major. - * - * In case of multiplexing, the given value is applied to all parameter instances. See multiplexCompTypeSecParameterValue(). - * - * This function optionally checks whether the specified parameter is listed as sensitive. It uses the first - * parameter group / instance to check the @p sensParams set. - * - * @param [in] pId ParameterID - * @param [in] nameHash Hash of the parameter name - * @param [in] mode Multiplexing mode as obtained by readAndRegisterMultiplexCompTypeSecParam() - * @param [in,out] data Array with parameters whose values are updated - * @param [in] nParType Number of particle types - * @param [in] nComp Number of components - * @param [in] strideBound Array with number of bound states per particle type (additional last element is total number of bound states) - * @param [in] nBound Array with number of bound states per component and particle type in type-major ordering - * @param [in] boundOffset Array with offset to component in bound-phase (cumulative sum of nBound per particle type) per particle type in type-major ordering - * @param [in] val Value to apply to the parameter(s) - * @param [in] sensParams If not @c nullptr, the set is checked for the specified parameter. - * If it is not contained in the set, the value is not applied to the parameter. - * @return @c true if the value has been applied, or @c false otherwise - */ - bool multiplexBndCompTypeSecParameterValue(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, std::vector& data, - unsigned int nParType, unsigned int nComp, unsigned int const* strideBound, unsigned int const* nBound, unsigned int const* boundOffset, double value, std::unordered_set const* sensParams); +/** + * @brief Sets the value of a multiplexed parameter that depends on particle type, component, bound state, and + * (optionally) section + * @details Sets the value of a parameter and multiplexes the value onto all parameter instances. + * The parameter array is expected to have a size of nTotalBound, or nTotalBound * nSections. + * The ordering is section-type-component-major. + * + * In case of multiplexing, the given value is applied to all parameter instances. See + * multiplexCompTypeSecParameterValue(). + * + * This function optionally checks whether the specified parameter is listed as sensitive. It uses the first + * parameter group / instance to check the @p sensParams set. + * + * @param [in] pId ParameterID + * @param [in] nameHash Hash of the parameter name + * @param [in] mode Multiplexing mode as obtained by readAndRegisterMultiplexCompTypeSecParam() + * @param [in,out] data Array with parameters whose values are updated + * @param [in] nParType Number of particle types + * @param [in] nComp Number of components + * @param [in] strideBound Array with number of bound states per particle type (additional last element is total number + * of bound states) + * @param [in] nBound Array with number of bound states per component and particle type in type-major ordering + * @param [in] boundOffset Array with offset to component in bound-phase (cumulative sum of nBound per particle type) + * per particle type in type-major ordering + * @param [in] val Value to apply to the parameter(s) + * @param [in] sensParams If not @c nullptr, the set is checked for the specified parameter. + * If it is not contained in the set, the value is not applied to the parameter. + * @return @c true if the value has been applied, or @c false otherwise + */ +bool multiplexBndCompTypeSecParameterValue(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, + std::vector& data, unsigned int nParType, unsigned int nComp, + unsigned int const* strideBound, unsigned int const* nBound, + unsigned int const* boundOffset, double value, + std::unordered_set const* sensParams); - /** - * @brief Sets AD info of a multiplexed parameter that depends on particle type, component, bound state, and (optionally) section - * @details Sets the AD direction and seed value of a parameter and multiplexes the info onto all parameter instances. - * The parameter array is expected to have a size of nTotalBound, or nTotalBound * nSections. - * The ordering is section-type-component-major. - * - * In case of multiplexing, the given AD info is applied to all parameter instances. See multiplexCompTypeSecParameterAD(). - * - * The first parameter group / instance is added to the @p sensParams set in order to mark the parameter as sensitive. - * - * @param [in] pId ParameterID - * @param [in] nameHash Hash of the parameter name - * @param [in] mode Multiplexing mode as obtained by readAndRegisterMultiplexCompTypeSecParam() - * @param [in,out] data Array with parameters whose AD info are updated - * @param [in] nParType Number of particle types - * @param [in] nComp Number of components - * @param [in] strideBound Array with number of bound states per particle type (additional last element is total number of bound states) - * @param [in] nBound Array with number of bound states per component and particle type in type-major ordering - * @param [in] boundOffset Array with offset to component in bound-phase (cumulative sum of nBound per particle type) per particle type in type-major ordering - * @param [in] adDirection AD direction - * @param [in] adValue AD seed value - * @param [in,out] sensParams The parameter(s) are marked sensitive by adding them to this set - * @return @c true if the parameter has been found, or @c false otherwise - */ - bool multiplexBndCompTypeSecParameterAD(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, std::vector& data, - unsigned int nParType, unsigned int nComp, unsigned int const* strideBound, unsigned int const* nBound, unsigned int const* boundOffset, unsigned int adDirection, double adValue, std::unordered_set& sensParams); +/** + * @brief Sets AD info of a multiplexed parameter that depends on particle type, component, bound state, and + * (optionally) section + * @details Sets the AD direction and seed value of a parameter and multiplexes the info onto all parameter instances. + * The parameter array is expected to have a size of nTotalBound, or nTotalBound * nSections. + * The ordering is section-type-component-major. + * + * In case of multiplexing, the given AD info is applied to all parameter instances. See + * multiplexCompTypeSecParameterAD(). + * + * The first parameter group / instance is added to the @p sensParams set in order to mark the parameter as + * sensitive. + * + * @param [in] pId ParameterID + * @param [in] nameHash Hash of the parameter name + * @param [in] mode Multiplexing mode as obtained by readAndRegisterMultiplexCompTypeSecParam() + * @param [in,out] data Array with parameters whose AD info are updated + * @param [in] nParType Number of particle types + * @param [in] nComp Number of components + * @param [in] strideBound Array with number of bound states per particle type (additional last element is total number + * of bound states) + * @param [in] nBound Array with number of bound states per component and particle type in type-major ordering + * @param [in] boundOffset Array with offset to component in bound-phase (cumulative sum of nBound per particle type) + * per particle type in type-major ordering + * @param [in] adDirection AD direction + * @param [in] adValue AD seed value + * @param [in,out] sensParams The parameter(s) are marked sensitive by adding them to this set + * @return @c true if the parameter has been found, or @c false otherwise + */ +bool multiplexBndCompTypeSecParameterAD(const ParameterId& pId, StringHash nameHash, MultiplexMode mode, + std::vector& data, unsigned int nParType, unsigned int nComp, + unsigned int const* strideBound, unsigned int const* nBound, + unsigned int const* boundOffset, unsigned int adDirection, double adValue, + std::unordered_set& sensParams); } // namespace model } // namespace cadet -#endif // LIBCADET_PARAMMULTIPLEXING_HPP_ +#endif // LIBCADET_PARAMMULTIPLEXING_HPP_ diff --git a/src/libcadet/model/Parameters.hpp b/src/libcadet/model/Parameters.hpp index 37376a7ab..a8a851193 100644 --- a/src/libcadet/model/Parameters.hpp +++ b/src/libcadet/model/Parameters.hpp @@ -35,7 +35,6 @@ namespace cadet { - namespace model { @@ -46,14 +45,17 @@ namespace model class ScalarBoolParameter { public: - /** * @brief Underlying type */ typedef bool storage_t; - ScalarBoolParameter(bool& p) : _p(&p) { } - ScalarBoolParameter(bool* p) : _p(p) { } + ScalarBoolParameter(bool& p) : _p(&p) + { + } + ScalarBoolParameter(bool* p) : _p(p) + { + } /** * @brief Reads parameters and verifies them @@ -64,7 +66,8 @@ class ScalarBoolParameter * @param [in] nBoundStates Array with number of bound states for each component * @return @c true if the parameters were read and validated successfully, otherwise @c false */ - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { *_p = paramProvider.getBool(varName); } @@ -77,7 +80,11 @@ class ScalarBoolParameter * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) { } + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) + { + } /** * @brief Reserves space in the storage of the parameters @@ -86,22 +93,33 @@ class ScalarBoolParameter * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) { } + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) + { + } - inline const bool& get() const CADET_NOEXCEPT { return *_p; } - inline bool& get() CADET_NOEXCEPT { return *_p; } + inline const bool& get() const CADET_NOEXCEPT + { + return *_p; + } + inline bool& get() CADET_NOEXCEPT + { + return *_p; + } /** * @brief Returns the number of elements in the parameter * @return Number of elements in the parameter */ - inline std::size_t size() const CADET_NOEXCEPT { return 1; } + inline std::size_t size() const CADET_NOEXCEPT + { + return 1; + } protected: bool* _p; }; - /** * @brief Scalar external parameter * @details Just a single value. @@ -109,14 +127,17 @@ class ScalarBoolParameter class ScalarParameter { public: - /** * @brief Underlying type */ typedef active storage_t; - ScalarParameter(active& p) : _p(&p) { } - ScalarParameter(active* p) : _p(p) { } + ScalarParameter(active& p) : _p(&p) + { + } + ScalarParameter(active* p) : _p(p) + { + } /** * @brief Reads parameters and verifies them @@ -127,7 +148,8 @@ class ScalarParameter * @param [in] nBoundStates Array with number of bound states for each component * @return @c true if the parameters were read and validated successfully, otherwise @c false */ - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { *_p = paramProvider.getDouble(varName); } @@ -142,7 +164,8 @@ class ScalarParameter * @param [in] defaultVal Default value if parameter is not available in @p paramProvider * @return @c true if the parameters were read and validated successfully, otherwise @c false */ - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates, double defaultVal) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates, double defaultVal) { if (paramProvider.exists(varName)) *_p = paramProvider.getDouble(varName); @@ -158,9 +181,12 @@ class ScalarParameter * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { - parameters[makeParamId(hashStringRuntime(varName), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, ReactionIndep, SectionIndep)] = _p; + parameters[makeParamId(hashStringRuntime(varName), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, + ReactionIndep, SectionIndep)] = _p; } /** @@ -170,22 +196,33 @@ class ScalarParameter * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) { } + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) + { + } - inline const active& get() const CADET_NOEXCEPT { return *_p; } - inline active& get() CADET_NOEXCEPT { return *_p; } + inline const active& get() const CADET_NOEXCEPT + { + return *_p; + } + inline active& get() CADET_NOEXCEPT + { + return *_p; + } /** * @brief Returns the number of elements in the parameter * @return Number of elements in the parameter */ - inline std::size_t size() const CADET_NOEXCEPT { return 1; } + inline std::size_t size() const CADET_NOEXCEPT + { + return 1; + } protected: active* _p; }; - /** * @brief Component dependent scalar external parameter * @details A single value per component. @@ -193,37 +230,52 @@ class ScalarParameter class ScalarComponentDependentParameter { public: - typedef std::vector storage_t; - ScalarComponentDependentParameter(std::vector& p) : _p(&p) { } - ScalarComponentDependentParameter(std::vector* p) : _p(p) { } + ScalarComponentDependentParameter(std::vector& p) : _p(&p) + { + } + ScalarComponentDependentParameter(std::vector* p) : _p(p) + { + } - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { readParameterMatrix(*_p, paramProvider, varName, nComp, 1); } - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { registerComponentBoundStateDependentParam(hashStringRuntime(varName), parameters, *_p, unitOpIdx, parTypeIdx); } - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) { _p->reserve(nComp); } - inline std::size_t size() const CADET_NOEXCEPT { return _p->size(); } + inline std::size_t size() const CADET_NOEXCEPT + { + return _p->size(); + } - inline const std::vector& get() const CADET_NOEXCEPT { return *_p; } - inline std::vector& get() CADET_NOEXCEPT { return *_p; } + inline const std::vector& get() const CADET_NOEXCEPT + { + return *_p; + } + inline std::vector& get() CADET_NOEXCEPT + { + return *_p; + } protected: std::vector* _p; }; - /** * @brief Bound state dependent scalar external parameter * @details A single value per bound state. Can be used for multiple binding site types. @@ -231,37 +283,52 @@ class ScalarComponentDependentParameter class ScalarBoundStateDependentParameter { public: - typedef std::vector storage_t; - ScalarBoundStateDependentParameter(std::vector& p) : _p(&p) { } - ScalarBoundStateDependentParameter(std::vector* p) : _p(p) { } + ScalarBoundStateDependentParameter(std::vector& p) : _p(&p) + { + } + ScalarBoundStateDependentParameter(std::vector* p) : _p(p) + { + } - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { readScalarParameterOrArray(*_p, paramProvider, varName, 1); } - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { registerScalarBoundStateDependentParam(hashStringRuntime(varName), parameters, *_p, unitOpIdx, parTypeIdx); } - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) { _p->reserve(numSlices); } - inline std::size_t size() const CADET_NOEXCEPT { return _p->size(); } + inline std::size_t size() const CADET_NOEXCEPT + { + return _p->size(); + } - inline const std::vector& get() const CADET_NOEXCEPT { return *_p; } - inline std::vector& get() CADET_NOEXCEPT { return *_p; } + inline const std::vector& get() const CADET_NOEXCEPT + { + return *_p; + } + inline std::vector& get() CADET_NOEXCEPT + { + return *_p; + } protected: std::vector* _p; }; - /** * @brief Reaction dependent scalar external parameter * @details A single value per reaction. @@ -269,13 +336,17 @@ class ScalarBoundStateDependentParameter class ScalarReactionDependentParameter { public: - typedef std::vector storage_t; - ScalarReactionDependentParameter(std::vector& p) : _p(&p) { } - ScalarReactionDependentParameter(std::vector* p) : _p(p) { } + ScalarReactionDependentParameter(std::vector& p) : _p(&p) + { + } + ScalarReactionDependentParameter(std::vector* p) : _p(p) + { + } - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { if (paramProvider.exists(varName)) readParameterMatrix(*_p, paramProvider, varName, 1, 1); @@ -283,10 +354,14 @@ class ScalarReactionDependentParameter _p->clear(); } - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { const StringHash nameHash = hashStringRuntime(varName); - registerParam1DArray(parameters, *_p, [=](bool multi, unsigned int r) { return makeParamId(nameHash, unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, r, SectionIndep); }); + registerParam1DArray(parameters, *_p, [=](bool multi, unsigned int r) { + return makeParamId(nameHash, unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, r, SectionIndep); + }); } inline void reserve(unsigned int nReactions, unsigned int nComp, unsigned int nBoundStates) @@ -294,16 +369,24 @@ class ScalarReactionDependentParameter _p->reserve(nReactions); } - inline std::size_t size() const CADET_NOEXCEPT { return _p->size(); } + inline std::size_t size() const CADET_NOEXCEPT + { + return _p->size(); + } - inline const std::vector& get() const CADET_NOEXCEPT { return *_p; } - inline std::vector& get() CADET_NOEXCEPT { return *_p; } + inline const std::vector& get() const CADET_NOEXCEPT + { + return *_p; + } + inline std::vector& get() CADET_NOEXCEPT + { + return *_p; + } protected: std::vector* _p; }; - /** * @brief Component dependent reaction dependent parameter * @details Holds a scalar for each component and reaction. @@ -311,13 +394,17 @@ class ScalarReactionDependentParameter class ComponentDependentReactionDependentParameter { public: - typedef std::vector storage_t; - ComponentDependentReactionDependentParameter(std::vector& p) : _p(&p) { } - ComponentDependentReactionDependentParameter(std::vector* p) : _p(p) { } + ComponentDependentReactionDependentParameter(std::vector& p) : _p(&p) + { + } + ComponentDependentReactionDependentParameter(std::vector* p) : _p(p) + { + } - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { if (paramProvider.exists(varName)) readParameterMatrix(*_p, paramProvider, varName, 1, 1); @@ -325,10 +412,17 @@ class ComponentDependentReactionDependentParameter _p->clear(); } - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { const StringHash nameHash = hashStringRuntime(varName); - registerParam2DArray(parameters, *_p, [=](bool multi, unsigned r, unsigned int c) { return makeParamId(nameHash, unitOpIdx, c, parTypeIdx, BoundStateIndep, r, SectionIndep); }, nComp); + registerParam2DArray( + parameters, *_p, + [=](bool multi, unsigned r, unsigned int c) { + return makeParamId(nameHash, unitOpIdx, c, parTypeIdx, BoundStateIndep, r, SectionIndep); + }, + nComp); } inline void reserve(unsigned int nReactions, unsigned int nComp, unsigned int nBoundStates) @@ -336,72 +430,102 @@ class ComponentDependentReactionDependentParameter _p->reserve(nComp * nReactions); } - inline typename std::vector::size_type size() const CADET_NOEXCEPT { return _p->size(); } + inline typename std::vector::size_type size() const CADET_NOEXCEPT + { + return _p->size(); + } - inline const std::vector& get() const CADET_NOEXCEPT { return *_p; } - inline std::vector& get() CADET_NOEXCEPT { return *_p; } + inline const std::vector& get() const CADET_NOEXCEPT + { + return *_p; + } + inline std::vector& get() CADET_NOEXCEPT + { + return *_p; + } protected: std::vector* _p; }; - /** * @brief Component and bound state dependent external parameter * @details A single value per component and bound state / binding site type. - * @tparam compMajor Determines whether the values are stored in component-major or bound-state-/binding-site-type-major ordering + * @tparam compMajor Determines whether the values are stored in component-major or bound-state-/binding-site-type-major + * ordering */ -template -class BaseComponentBoundStateDependentParameter +template class BaseComponentBoundStateDependentParameter { public: - typedef util::SlicedVector storage_t; - BaseComponentBoundStateDependentParameter(util::SlicedVector& p) : _p(&p) { } - BaseComponentBoundStateDependentParameter(util::SlicedVector* p) : _p(p) { } + BaseComponentBoundStateDependentParameter(util::SlicedVector& p) : _p(&p) + { + } + BaseComponentBoundStateDependentParameter(util::SlicedVector* p) : _p(p) + { + } - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { if (compMajor) { - readBoundStateDependentParameter, active>(*_p, paramProvider, varName, nComp, nBoundStates); + readBoundStateDependentParameter, active>(*_p, paramProvider, varName, nComp, + nBoundStates); } else { const unsigned int numStates = firstNonEmptyBoundStates(nBoundStates, nComp); - readBoundStateDependentParameter, active>(*_p, paramProvider, varName, nComp, numStates); + readBoundStateDependentParameter, active>(*_p, paramProvider, varName, nComp, + numStates); } } - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { if (compMajor) { - registerComponentBoundStateDependentParamCompMajor(hashStringRuntime(varName), parameters, *_p, unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParamCompMajor(hashStringRuntime(varName), parameters, *_p, unitOpIdx, + parTypeIdx); } else { - registerComponentBoundStateDependentParam(hashStringRuntime(varName), parameters, *_p, unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParam(hashStringRuntime(varName), parameters, *_p, unitOpIdx, + parTypeIdx); } } - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) { _p->reserve(numElem, numSlices); } - inline typename util::SlicedVector::size_type slices() const CADET_NOEXCEPT { return _p->slices(); } - inline typename util::SlicedVector::size_type size() const CADET_NOEXCEPT { return _p->size(); } + inline typename util::SlicedVector::size_type slices() const CADET_NOEXCEPT + { + return _p->slices(); + } + inline typename util::SlicedVector::size_type size() const CADET_NOEXCEPT + { + return _p->size(); + } - inline const util::SlicedVector& get() const CADET_NOEXCEPT { return *_p; } - inline util::SlicedVector& get() CADET_NOEXCEPT { return *_p; } + inline const util::SlicedVector& get() const CADET_NOEXCEPT + { + return *_p; + } + inline util::SlicedVector& get() CADET_NOEXCEPT + { + return *_p; + } protected: util::SlicedVector* _p; }; - /** * @brief Component and bound state dependent parameter * @details Components contain values for each bound state. @@ -414,7 +538,6 @@ typedef BaseComponentBoundStateDependentParameter ComponentMajorBoundState */ typedef BaseComponentBoundStateDependentParameter ComponentBoundStateMajorDependentParameter; - /** * @brief Component dependent bound-state matrix valued external parameter * @details Holds a square matrix for each component that has the size of the number of corresponding bound states. @@ -422,23 +545,32 @@ typedef BaseComponentBoundStateDependentParameter ComponentBoundStateMajo class ComponentDependentBoundStateMatrixParameter { public: - typedef util::SlicedVector storage_t; - ComponentDependentBoundStateMatrixParameter(util::SlicedVector& p) : _p(&p) { } - ComponentDependentBoundStateMatrixParameter(util::SlicedVector* p) : _p(p) { } + ComponentDependentBoundStateMatrixParameter(util::SlicedVector& p) : _p(&p) + { + } + ComponentDependentBoundStateMatrixParameter(util::SlicedVector* p) : _p(p) + { + } - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { - readMatrixValuedBoundStateDependentParameter, active>(*_p, paramProvider, varName, nComp, nBoundStates); + readMatrixValuedBoundStateDependentParameter, active>(*_p, paramProvider, varName, + nComp, nBoundStates); } - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { - registerComponentBoundStateDependentParamCompMajor(hashStringRuntime(varName), parameters, *_p, unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParamCompMajor(hashStringRuntime(varName), parameters, *_p, unitOpIdx, + parTypeIdx); } - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) { unsigned int sumSquared = 0; for (unsigned int i = 0; i < nComp; ++i) @@ -447,17 +579,28 @@ class ComponentDependentBoundStateMatrixParameter _p->reserve(sumSquared, nComp); } - inline typename util::SlicedVector::size_type slices() const CADET_NOEXCEPT { return _p->slices(); } - inline typename util::SlicedVector::size_type size() const CADET_NOEXCEPT { return _p->size(); } + inline typename util::SlicedVector::size_type slices() const CADET_NOEXCEPT + { + return _p->slices(); + } + inline typename util::SlicedVector::size_type size() const CADET_NOEXCEPT + { + return _p->size(); + } - inline const util::SlicedVector& get() const CADET_NOEXCEPT { return *_p; } - inline util::SlicedVector& get() CADET_NOEXCEPT { return *_p; } + inline const util::SlicedVector& get() const CADET_NOEXCEPT + { + return *_p; + } + inline util::SlicedVector& get() CADET_NOEXCEPT + { + return *_p; + } protected: util::SlicedVector* _p; }; - /** * @brief Component dependent component vector valued parameter * @details Holds a vector for each component that has the size of the number of components. @@ -465,39 +608,57 @@ class ComponentDependentBoundStateMatrixParameter class ComponentDependentComponentVectorParameter { public: - typedef util::SlicedVector storage_t; - ComponentDependentComponentVectorParameter(util::SlicedVector& p) : _p(&p) { } - ComponentDependentComponentVectorParameter(util::SlicedVector* p) : _p(p) { } + ComponentDependentComponentVectorParameter(util::SlicedVector& p) : _p(&p) + { + } + ComponentDependentComponentVectorParameter(util::SlicedVector* p) : _p(p) + { + } - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { readBoundStateDependentParameter, active>(*_p, paramProvider, varName, nComp, nComp); } - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { registerComponentBoundStateDependentParam(hashStringRuntime(varName), parameters, *_p, unitOpIdx, parTypeIdx); } - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) { const unsigned int compSquared = nComp * nComp; _p->reserve(compSquared, nComp); } - inline typename util::SlicedVector::size_type slices() const CADET_NOEXCEPT { return _p->slices(); } - inline typename util::SlicedVector::size_type size() const CADET_NOEXCEPT { return _p->size(); } + inline typename util::SlicedVector::size_type slices() const CADET_NOEXCEPT + { + return _p->slices(); + } + inline typename util::SlicedVector::size_type size() const CADET_NOEXCEPT + { + return _p->size(); + } - inline const util::SlicedVector& get() const CADET_NOEXCEPT { return *_p; } - inline util::SlicedVector& get() CADET_NOEXCEPT { return *_p; } + inline const util::SlicedVector& get() const CADET_NOEXCEPT + { + return *_p; + } + inline util::SlicedVector& get() CADET_NOEXCEPT + { + return *_p; + } protected: util::SlicedVector* _p; }; - /** * @brief Scalar reference conentrations * @details Just one liquid and one solid phase reference concentration. @@ -505,14 +666,17 @@ class ComponentDependentComponentVectorParameter class ReferenceConcentrationParameter { public: - /** * @brief Underlying type */ typedef active storage_t; - ReferenceConcentrationParameter(active& refC, active& refQ) : _refC(&refC), _refQ(&refQ) { } - ReferenceConcentrationParameter(active* refC, active* refQ) : _refC(refC), _refQ(refQ) { } + ReferenceConcentrationParameter(active& refC, active& refQ) : _refC(&refC), _refQ(&refQ) + { + } + ReferenceConcentrationParameter(active* refC, active* refQ) : _refC(refC), _refQ(refQ) + { + } /** * @brief Reads parameters and verifies them @@ -523,7 +687,8 @@ class ReferenceConcentrationParameter * @param [in] nBoundStates Array with number of bound states for each component * @return @c true if the parameters were read and validated successfully, otherwise @c false */ - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { readReferenceConcentrations(paramProvider, varName, *_refC, *_refQ); } @@ -536,10 +701,14 @@ class ReferenceConcentrationParameter * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { - parameters[makeParamId(hashStringRuntime(varName + "REFC0"), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, ReactionIndep, SectionIndep)] = _refC; - parameters[makeParamId(hashStringRuntime(varName + "REFQ"), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, ReactionIndep, SectionIndep)] = _refQ; + parameters[makeParamId(hashStringRuntime(varName + "REFC0"), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, + ReactionIndep, SectionIndep)] = _refC; + parameters[makeParamId(hashStringRuntime(varName + "REFQ"), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, + ReactionIndep, SectionIndep)] = _refQ; } /** @@ -549,26 +718,43 @@ class ReferenceConcentrationParameter * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) { } + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) + { + } /** * @brief Returns the number of elements in the parameter * @return Number of elements in the parameter */ - inline std::size_t size() const CADET_NOEXCEPT { return 1; } + inline std::size_t size() const CADET_NOEXCEPT + { + return 1; + } - inline const active& getC() const CADET_NOEXCEPT { return *_refC; } - inline active& getC() CADET_NOEXCEPT { return *_refC; } + inline const active& getC() const CADET_NOEXCEPT + { + return *_refC; + } + inline active& getC() CADET_NOEXCEPT + { + return *_refC; + } - inline const active& getQ() const CADET_NOEXCEPT { return *_refQ; } - inline active& getQ() CADET_NOEXCEPT { return *_refQ; } + inline const active& getQ() const CADET_NOEXCEPT + { + return *_refQ; + } + inline active& getQ() CADET_NOEXCEPT + { + return *_refQ; + } protected: active* _refC; //!< Reference liquid phase concentration active* _refQ; //!< Reference solid phase concentration }; - /** * @brief Vectorial reference conentrations * @details Liquid and one solid phase reference concentrations per binding site type. @@ -576,14 +762,19 @@ class ReferenceConcentrationParameter class ReferenceConcentrationBoundStateDependentParameter { public: - /** * @brief Underlying type */ typedef std::vector storage_t; - ReferenceConcentrationBoundStateDependentParameter(std::vector& refC, std::vector& refQ) : _refC(&refC), _refQ(&refQ) { } - ReferenceConcentrationBoundStateDependentParameter(std::vector* refC, std::vector* refQ) : _refC(refC), _refQ(refQ) { } + ReferenceConcentrationBoundStateDependentParameter(std::vector& refC, std::vector& refQ) + : _refC(&refC), _refQ(&refQ) + { + } + ReferenceConcentrationBoundStateDependentParameter(std::vector* refC, std::vector* refQ) + : _refC(refC), _refQ(refQ) + { + } /** * @brief Reads parameters and verifies them @@ -594,7 +785,8 @@ class ReferenceConcentrationBoundStateDependentParameter * @param [in] nBoundStates Array with number of bound states for each component * @return @c true if the parameters were read and validated successfully, otherwise @c false */ - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { const unsigned int numStates = firstNonEmptyBoundStates(nBoundStates, nComp); readReferenceConcentrations(paramProvider, numStates, varName, *_refC, *_refQ); @@ -608,10 +800,14 @@ class ReferenceConcentrationBoundStateDependentParameter * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { - registerScalarBoundStateDependentParam(hashStringRuntime(varName + "REFC0"), parameters, *_refC, unitOpIdx, parTypeIdx); - registerScalarBoundStateDependentParam(hashStringRuntime(varName + "REFQ"), parameters, *_refQ, unitOpIdx, parTypeIdx); + registerScalarBoundStateDependentParam(hashStringRuntime(varName + "REFC0"), parameters, *_refC, unitOpIdx, + parTypeIdx); + registerScalarBoundStateDependentParam(hashStringRuntime(varName + "REFQ"), parameters, *_refQ, unitOpIdx, + parTypeIdx); } /** @@ -621,7 +817,8 @@ class ReferenceConcentrationBoundStateDependentParameter * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) { _refC->reserve(numSlices); _refQ->reserve(numSlices); @@ -631,14 +828,16 @@ class ReferenceConcentrationBoundStateDependentParameter * @brief Returns the number of elements in the parameter * @return Number of elements in the parameter */ - inline std::size_t size() const CADET_NOEXCEPT { return _refC->size(); } + inline std::size_t size() const CADET_NOEXCEPT + { + return _refC->size(); + } protected: std::vector* _refC; //!< Reference liquid phase concentration std::vector* _refQ; //!< Reference solid phase concentration }; - /** * @brief Scalar external parameter * @details Just a single value. @@ -646,7 +845,6 @@ class ReferenceConcentrationBoundStateDependentParameter class ExternalScalarParameter { public: - /** * @brief Underlying type */ @@ -661,7 +859,8 @@ class ExternalScalarParameter * @param [in] nBoundStates Array with number of bound states for each component * @return @c true if the parameters were read and validated successfully, otherwise @c false */ - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { _base = paramProvider.getDouble("EXT_" + varName); _linear = paramProvider.getDouble("EXT_" + varName + "_T"); @@ -677,12 +876,18 @@ class ExternalScalarParameter * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { - parameters[makeParamId(hashStringRuntime("EXT_" + varName), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, ReactionIndep, SectionIndep)] = &_base; - parameters[makeParamId(hashStringRuntime("EXT_" + varName + "_T"), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, ReactionIndep, SectionIndep)] = &_linear; - parameters[makeParamId(hashStringRuntime("EXT_" + varName + "_TT"), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, ReactionIndep, SectionIndep)] = &_quad; - parameters[makeParamId(hashStringRuntime("EXT_" + varName + "_TTT"), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, ReactionIndep, SectionIndep)] = &_cube; + parameters[makeParamId(hashStringRuntime("EXT_" + varName), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_base; + parameters[makeParamId(hashStringRuntime("EXT_" + varName + "_T"), unitOpIdx, CompIndep, parTypeIdx, + BoundStateIndep, ReactionIndep, SectionIndep)] = &_linear; + parameters[makeParamId(hashStringRuntime("EXT_" + varName + "_TT"), unitOpIdx, CompIndep, parTypeIdx, + BoundStateIndep, ReactionIndep, SectionIndep)] = &_quad; + parameters[makeParamId(hashStringRuntime("EXT_" + varName + "_TTT"), unitOpIdx, CompIndep, parTypeIdx, + BoundStateIndep, ReactionIndep, SectionIndep)] = &_cube; } /** @@ -692,7 +897,10 @@ class ExternalScalarParameter * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) { } + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) + { + } /** * @brief Calculates a parameter in order to take the external profile into account @@ -726,7 +934,8 @@ class ExternalScalarParameter * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void updateTimeDerivative(active& result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(active& result, double extVal, double extTimeDiff, unsigned int nComp, + unsigned int const* nBoundStates) const { updateTimeDerivative(&result, extVal, extTimeDiff, nComp, nBoundStates); } @@ -739,20 +948,29 @@ class ExternalScalarParameter * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, + unsigned int const* nBoundStates) const { - *result = extTimeDiff * (static_cast(_linear) + extVal * (2.0 * static_cast(_quad) + 3.0 * extVal * static_cast(_cube))); + *result = extTimeDiff * (static_cast(_linear) + extVal * (2.0 * static_cast(_quad) + + 3.0 * extVal * static_cast(_cube))); } /** * @brief Returns the base value that does not depend on an external value * @return Constant base value */ - inline storage_t& base() CADET_NOEXCEPT { return _base; } - inline const storage_t& base() const CADET_NOEXCEPT { return _base; } + inline storage_t& base() CADET_NOEXCEPT + { + return _base; + } + inline const storage_t& base() const CADET_NOEXCEPT + { + return _base; + } /** - * @brief Returns the amount of additional memory (usually dynamically allocated by containers) for storing the final parameters + * @brief Returns the amount of additional memory (usually dynamically allocated by containers) for storing the + * final parameters * @details In a model, externally dependent parameters are stored in a struct, usually called * VariableParams. This is sufficient for "static" parameter types that do * not require additional memory (which is usually allocated dynamically). @@ -766,7 +984,11 @@ class ExternalScalarParameter * @param [in] nBoundStates Array with number of bound states for each component * @return Amount of additional memory in bytes */ - inline std::size_t additionalDynamicMemory(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT { return 0; } + inline std::size_t additionalDynamicMemory(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT + { + return 0; + } /** * @brief Prepares the cache for the updated values @@ -774,14 +996,18 @@ class ExternalScalarParameter * @param [in,out] cache Cache object to be prepared * @param [in] ptr Pointer to cache buffer */ - template - inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const { } + template inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const + { + } /** * @brief Returns the number of elements in the parameter * @return Number of elements in the parameter */ - inline std::size_t size() const CADET_NOEXCEPT { return 1; } + inline std::size_t size() const CADET_NOEXCEPT + { + return 1; + } protected: active _base; @@ -790,7 +1016,6 @@ class ExternalScalarParameter active _cube; }; - /** * @brief Component dependent scalar external parameter * @details A single value per component. @@ -798,10 +1023,10 @@ class ExternalScalarParameter class ExternalScalarComponentDependentParameter { public: - typedef std::vector storage_t; - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { readParameterMatrix(_base, paramProvider, "EXT_" + varName, nComp, 1); readParameterMatrix(_linear, paramProvider, "EXT_" + varName + "_T", nComp, 1); @@ -809,15 +1034,22 @@ class ExternalScalarComponentDependentParameter readParameterMatrix(_cube, paramProvider, "EXT_" + varName + "_TTT", nComp, 1); } - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { - registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName), parameters, _base, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_T"), parameters, _linear, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TT"), parameters, _quad, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TTT"), parameters, _cube, unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName), parameters, _base, unitOpIdx, + parTypeIdx); + registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_T"), parameters, _linear, + unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TT"), parameters, _quad, + unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TTT"), parameters, _cube, + unitOpIdx, parTypeIdx); } - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) { _base.reserve(nComp); _linear.reserve(nComp); @@ -825,7 +1057,8 @@ class ExternalScalarComponentDependentParameter _cube.reserve(nComp); } - inline void update(std::vector& result, double extVal, unsigned int nComp, unsigned int const* nBoundStates) const + inline void update(std::vector& result, double extVal, unsigned int nComp, + unsigned int const* nBoundStates) const { update(result.data(), extVal, nComp, nBoundStates); } @@ -836,27 +1069,46 @@ class ExternalScalarComponentDependentParameter result[i] = _base[i] + extVal * (_linear[i] + extVal * (_quad[i] + extVal * _cube[i])); } - inline void updateTimeDerivative(std::vector& result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(std::vector& result, double extVal, double extTimeDiff, unsigned int nComp, + unsigned int const* nBoundStates) const { updateTimeDerivative(result.data(), extVal, extTimeDiff, nComp, nBoundStates); } - inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, + unsigned int const* nBoundStates) const { for (std::size_t i = 0; i < _base.size(); ++i) - result[i] = extTimeDiff * (static_cast(_linear[i]) + extVal * (2.0 * static_cast(_quad[i]) + 3.0 * extVal * static_cast(_cube[i]))); + result[i] = extTimeDiff * + (static_cast(_linear[i]) + + extVal * (2.0 * static_cast(_quad[i]) + 3.0 * extVal * static_cast(_cube[i]))); } - inline std::size_t size() const CADET_NOEXCEPT { return _base.size(); } - inline bool allSameSize() const CADET_NOEXCEPT { return (_base.size() == _linear.size()) && (_base.size() == _quad.size()) && (_base.size() == _cube.size()); } + inline std::size_t size() const CADET_NOEXCEPT + { + return _base.size(); + } + inline bool allSameSize() const CADET_NOEXCEPT + { + return (_base.size() == _linear.size()) && (_base.size() == _quad.size()) && (_base.size() == _cube.size()); + } - inline std::size_t additionalDynamicMemory(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT { return nComp * sizeof(active) + alignof(active); } + inline std::size_t additionalDynamicMemory(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT + { + return nComp * sizeof(active) + alignof(active); + } - inline storage_t& base() CADET_NOEXCEPT { return _base; } - inline const storage_t& base() const CADET_NOEXCEPT { return _base; } + inline storage_t& base() CADET_NOEXCEPT + { + return _base; + } + inline const storage_t& base() const CADET_NOEXCEPT + { + return _base; + } - template - inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const + template inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const { cache.fromTemplate(buffer, _base); } @@ -868,7 +1120,6 @@ class ExternalScalarComponentDependentParameter std::vector _cube; }; - /** * @brief Bound state dependent scalar external parameter * @details A single value per bound state. Can be used for multiple binding site types. @@ -876,10 +1127,10 @@ class ExternalScalarComponentDependentParameter class ExternalScalarBoundStateDependentParameter { public: - typedef std::vector storage_t; - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { readScalarParameterOrArray(_base, paramProvider, "EXT_" + varName, 1); readScalarParameterOrArray(_linear, paramProvider, "EXT_" + varName + "_T", 1); @@ -887,15 +1138,22 @@ class ExternalScalarBoundStateDependentParameter readScalarParameterOrArray(_cube, paramProvider, "EXT_" + varName + "_TTT", 1); } - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { - registerScalarBoundStateDependentParam(hashStringRuntime("EXT_" + varName), parameters, _base, unitOpIdx, parTypeIdx); - registerScalarBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_T"), parameters, _linear, unitOpIdx, parTypeIdx); - registerScalarBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TT"), parameters, _quad, unitOpIdx, parTypeIdx); - registerScalarBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TTT"), parameters, _cube, unitOpIdx, parTypeIdx); + registerScalarBoundStateDependentParam(hashStringRuntime("EXT_" + varName), parameters, _base, unitOpIdx, + parTypeIdx); + registerScalarBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_T"), parameters, _linear, + unitOpIdx, parTypeIdx); + registerScalarBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TT"), parameters, _quad, + unitOpIdx, parTypeIdx); + registerScalarBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TTT"), parameters, _cube, + unitOpIdx, parTypeIdx); } - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) { _base.reserve(numSlices); _linear.reserve(numSlices); @@ -903,7 +1161,8 @@ class ExternalScalarBoundStateDependentParameter _cube.reserve(numSlices); } - inline void update(std::vector& result, double extVal, unsigned int nComp, unsigned int const* nBoundStates) const + inline void update(std::vector& result, double extVal, unsigned int nComp, + unsigned int const* nBoundStates) const { update(result.data(), extVal, nComp, nBoundStates); } @@ -914,21 +1173,32 @@ class ExternalScalarBoundStateDependentParameter result[i] = _base[i] + extVal * (_linear[i] + extVal * (_quad[i] + extVal * _cube[i])); } - inline void updateTimeDerivative(std::vector& result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(std::vector& result, double extVal, double extTimeDiff, unsigned int nComp, + unsigned int const* nBoundStates) const { updateTimeDerivative(result.data(), extVal, extTimeDiff, nComp, nBoundStates); } - inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, + unsigned int const* nBoundStates) const { for (std::size_t i = 0; i < _base.size(); ++i) - result[i] = extTimeDiff * (static_cast(_linear[i]) + extVal * (2.0 * static_cast(_quad[i]) + 3.0 * extVal * static_cast(_cube[i]))); + result[i] = extTimeDiff * + (static_cast(_linear[i]) + + extVal * (2.0 * static_cast(_quad[i]) + 3.0 * extVal * static_cast(_cube[i]))); } - inline std::size_t size() const CADET_NOEXCEPT { return _base.size(); } - inline bool allSameSize() const CADET_NOEXCEPT { return (_base.size() == _linear.size()) && (_base.size() == _quad.size()) && (_base.size() == _cube.size()); } + inline std::size_t size() const CADET_NOEXCEPT + { + return _base.size(); + } + inline bool allSameSize() const CADET_NOEXCEPT + { + return (_base.size() == _linear.size()) && (_base.size() == _quad.size()) && (_base.size() == _cube.size()); + } - inline std::size_t additionalDynamicMemory(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT + inline std::size_t additionalDynamicMemory(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT { for (unsigned int i = 0; i < nComp; ++i) { @@ -938,11 +1208,16 @@ class ExternalScalarBoundStateDependentParameter return 0; } - inline storage_t& base() CADET_NOEXCEPT { return _base; } - inline const storage_t& base() const CADET_NOEXCEPT { return _base; } + inline storage_t& base() CADET_NOEXCEPT + { + return _base; + } + inline const storage_t& base() const CADET_NOEXCEPT + { + return _base; + } - template - inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const + template inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const { cache.fromTemplate(buffer, _base); } @@ -954,7 +1229,6 @@ class ExternalScalarBoundStateDependentParameter std::vector _cube; }; - /** * @brief Reaction dependent scalar external parameter * @details A single value per reaction. @@ -962,10 +1236,10 @@ class ExternalScalarBoundStateDependentParameter class ExternalScalarReactionDependentParameter { public: - typedef std::vector storage_t; - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { readParameterMatrix(_base, paramProvider, "EXT_" + varName, 1, 1); readParameterMatrix(_linear, paramProvider, "EXT_" + varName + "_T", 1, 1); @@ -973,19 +1247,29 @@ class ExternalScalarReactionDependentParameter readParameterMatrix(_cube, paramProvider, "EXT_" + varName + "_TTT", 1, 1); } - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { const StringHash hashConst = hashStringRuntime("EXT_" + varName); - registerParam1DArray(parameters, _base, [=](bool multi, unsigned int r) { return makeParamId(hashConst, unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, r, SectionIndep); }); + registerParam1DArray(parameters, _base, [=](bool multi, unsigned int r) { + return makeParamId(hashConst, unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, r, SectionIndep); + }); const StringHash hashLinear = hashStringRuntime("EXT_" + varName + "_T"); - registerParam1DArray(parameters, _linear, [=](bool multi, unsigned int r) { return makeParamId(hashLinear, unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, r, SectionIndep); }); + registerParam1DArray(parameters, _linear, [=](bool multi, unsigned int r) { + return makeParamId(hashLinear, unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, r, SectionIndep); + }); const StringHash hashQuad = hashStringRuntime("EXT_" + varName + "_TT"); - registerParam1DArray(parameters, _quad, [=](bool multi, unsigned int r) { return makeParamId(hashQuad, unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, r, SectionIndep); }); + registerParam1DArray(parameters, _quad, [=](bool multi, unsigned int r) { + return makeParamId(hashQuad, unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, r, SectionIndep); + }); const StringHash hashCube = hashStringRuntime("EXT_" + varName + "_TTT"); - registerParam1DArray(parameters, _cube, [=](bool multi, unsigned int r) { return makeParamId(hashCube, unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, r, SectionIndep); }); + registerParam1DArray(parameters, _cube, [=](bool multi, unsigned int r) { + return makeParamId(hashCube, unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, r, SectionIndep); + }); } inline void reserve(unsigned int nReactions, unsigned int nComp, unsigned int nBoundStates) @@ -996,7 +1280,8 @@ class ExternalScalarReactionDependentParameter _cube.reserve(nReactions); } - inline void update(std::vector& result, double extVal, unsigned int nComp, unsigned int const* nBoundStates) const + inline void update(std::vector& result, double extVal, unsigned int nComp, + unsigned int const* nBoundStates) const { update(result.data(), extVal, nComp, nBoundStates); } @@ -1007,27 +1292,46 @@ class ExternalScalarReactionDependentParameter result[i] = _base[i] + extVal * (_linear[i] + extVal * (_quad[i] + extVal * _cube[i])); } - inline void updateTimeDerivative(std::vector& result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(std::vector& result, double extVal, double extTimeDiff, unsigned int nComp, + unsigned int const* nBoundStates) const { updateTimeDerivative(result.data(), extVal, extTimeDiff, nComp, nBoundStates); } - inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, + unsigned int const* nBoundStates) const { for (std::size_t i = 0; i < _base.size(); ++i) - result[i] = extTimeDiff * (static_cast(_linear[i]) + extVal * (2.0 * static_cast(_quad[i]) + 3.0 * extVal * static_cast(_cube[i]))); + result[i] = extTimeDiff * + (static_cast(_linear[i]) + + extVal * (2.0 * static_cast(_quad[i]) + 3.0 * extVal * static_cast(_cube[i]))); } - inline std::size_t size() const CADET_NOEXCEPT { return _base.size(); } - inline bool allSameSize() const CADET_NOEXCEPT { return (_base.size() == _linear.size()) && (_base.size() == _quad.size()) && (_base.size() == _cube.size()); } + inline std::size_t size() const CADET_NOEXCEPT + { + return _base.size(); + } + inline bool allSameSize() const CADET_NOEXCEPT + { + return (_base.size() == _linear.size()) && (_base.size() == _quad.size()) && (_base.size() == _cube.size()); + } - inline std::size_t additionalDynamicMemory(unsigned int nReactions, unsigned int nComp, unsigned int totalNumBoundStates) const CADET_NOEXCEPT { return nReactions * sizeof(active) + alignof(active); } + inline std::size_t additionalDynamicMemory(unsigned int nReactions, unsigned int nComp, + unsigned int totalNumBoundStates) const CADET_NOEXCEPT + { + return nReactions * sizeof(active) + alignof(active); + } - inline storage_t& base() CADET_NOEXCEPT { return _base; } - inline const storage_t& base() const CADET_NOEXCEPT { return _base; } + inline storage_t& base() CADET_NOEXCEPT + { + return _base; + } + inline const storage_t& base() const CADET_NOEXCEPT + { + return _base; + } - template - inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const + template inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const { cache.fromTemplate(buffer, _base); } @@ -1039,7 +1343,6 @@ class ExternalScalarReactionDependentParameter std::vector _cube; }; - /** * @brief Component dependent reaction dependent external parameter * @details Holds a scalar for each component and reaction. @@ -1047,10 +1350,10 @@ class ExternalScalarReactionDependentParameter class ExternalComponentDependentReactionDependentParameter { public: - typedef std::vector storage_t; - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { readParameterMatrix(_base, paramProvider, "EXT_" + varName, 1, 1); readParameterMatrix(_linear, paramProvider, "EXT_" + varName + "_T", 1, 1); @@ -1058,19 +1361,41 @@ class ExternalComponentDependentReactionDependentParameter readParameterMatrix(_cube, paramProvider, "EXT_" + varName + "_TTT", 1, 1); } - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { const StringHash hashConst = hashStringRuntime("EXT_" + varName); - registerParam2DArray(parameters, _base, [=](bool multi, unsigned int r, unsigned c) { return makeParamId(hashConst, unitOpIdx, c, parTypeIdx, BoundStateIndep, r, SectionIndep); }, nComp); + registerParam2DArray( + parameters, _base, + [=](bool multi, unsigned int r, unsigned c) { + return makeParamId(hashConst, unitOpIdx, c, parTypeIdx, BoundStateIndep, r, SectionIndep); + }, + nComp); const StringHash hashLinear = hashStringRuntime("EXT_" + varName + "_T"); - registerParam2DArray(parameters, _linear, [=](bool multi, unsigned int r, unsigned c) { return makeParamId(hashLinear, unitOpIdx, c, parTypeIdx, BoundStateIndep, r, SectionIndep); }, nComp); + registerParam2DArray( + parameters, _linear, + [=](bool multi, unsigned int r, unsigned c) { + return makeParamId(hashLinear, unitOpIdx, c, parTypeIdx, BoundStateIndep, r, SectionIndep); + }, + nComp); const StringHash hashQuad = hashStringRuntime("EXT_" + varName + "_TT"); - registerParam2DArray(parameters, _quad, [=](bool multi, unsigned int r, unsigned c) { return makeParamId(hashQuad, unitOpIdx, c, parTypeIdx, BoundStateIndep, r, SectionIndep); }, nComp); + registerParam2DArray( + parameters, _quad, + [=](bool multi, unsigned int r, unsigned c) { + return makeParamId(hashQuad, unitOpIdx, c, parTypeIdx, BoundStateIndep, r, SectionIndep); + }, + nComp); const StringHash hashCube = hashStringRuntime("EXT_" + varName + "_TTT"); - registerParam2DArray(parameters, _cube, [=](bool multi, unsigned int r, unsigned c) { return makeParamId(hashCube, unitOpIdx, c, parTypeIdx, BoundStateIndep, r, SectionIndep); }, nComp); + registerParam2DArray( + parameters, _cube, + [=](bool multi, unsigned int r, unsigned c) { + return makeParamId(hashCube, unitOpIdx, c, parTypeIdx, BoundStateIndep, r, SectionIndep); + }, + nComp); } inline void reserve(unsigned int nReactions, unsigned int nComp, unsigned int nBoundStates) @@ -1081,7 +1406,8 @@ class ExternalComponentDependentReactionDependentParameter _cube.reserve(nReactions * nComp); } - inline void update(std::vector& result, double extVal, unsigned int nComp, unsigned int const* nBoundStates) const + inline void update(std::vector& result, double extVal, unsigned int nComp, + unsigned int const* nBoundStates) const { update(result.data(), extVal, nComp, nBoundStates); } @@ -1092,27 +1418,46 @@ class ExternalComponentDependentReactionDependentParameter result[i] = _base[i] + extVal * (_linear[i] + extVal * (_quad[i] + extVal * _cube[i])); } - inline void updateTimeDerivative(std::vector& result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(std::vector& result, double extVal, double extTimeDiff, unsigned int nComp, + unsigned int const* nBoundStates) const { updateTimeDerivative(result.data(), extVal, extTimeDiff, nComp, nBoundStates); } - inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, + unsigned int const* nBoundStates) const { for (std::size_t i = 0; i < _base.size(); ++i) - result[i] = extTimeDiff * (static_cast(_linear[i]) + extVal * (2.0 * static_cast(_quad[i]) + 3.0 * extVal * static_cast(_cube[i]))); + result[i] = extTimeDiff * + (static_cast(_linear[i]) + + extVal * (2.0 * static_cast(_quad[i]) + 3.0 * extVal * static_cast(_cube[i]))); } - inline std::size_t size() const CADET_NOEXCEPT { return _base.size(); } - inline bool allSameSize() const CADET_NOEXCEPT { return (_base.size() == _linear.size()) && (_base.size() == _quad.size()) && (_base.size() == _cube.size()); } + inline std::size_t size() const CADET_NOEXCEPT + { + return _base.size(); + } + inline bool allSameSize() const CADET_NOEXCEPT + { + return (_base.size() == _linear.size()) && (_base.size() == _quad.size()) && (_base.size() == _cube.size()); + } - inline std::size_t additionalDynamicMemory(unsigned int nReactions, unsigned int nComp, unsigned int totalNumBoundStates) const CADET_NOEXCEPT { return nReactions * sizeof(active) + alignof(active); } + inline std::size_t additionalDynamicMemory(unsigned int nReactions, unsigned int nComp, + unsigned int totalNumBoundStates) const CADET_NOEXCEPT + { + return nReactions * sizeof(active) + alignof(active); + } - inline storage_t& base() CADET_NOEXCEPT { return _base; } - inline const storage_t& base() const CADET_NOEXCEPT { return _base; } + inline storage_t& base() CADET_NOEXCEPT + { + return _base; + } + inline const storage_t& base() const CADET_NOEXCEPT + { + return _base; + } - template - inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const + template inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const { cache.fromTemplate(buffer, _base); } @@ -1124,57 +1469,75 @@ class ExternalComponentDependentReactionDependentParameter std::vector _cube; }; - /** * @brief Component and bound state dependent external parameter * @details A single value per component and bound state / binding site type. - * @tparam compMajor Determines whether the values are stored in component-major or bound-state-/binding-site-type-major ordering + * @tparam compMajor Determines whether the values are stored in component-major or bound-state-/binding-site-type-major + * ordering */ -template -class ExternalBaseComponentBoundStateDependentParameter +template class ExternalBaseComponentBoundStateDependentParameter { public: - typedef util::SlicedVector storage_t; - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { if (compMajor) { - readBoundStateDependentParameter, active>(_base, paramProvider, "EXT_" + varName, nComp, nBoundStates); - readBoundStateDependentParameter, active>(_linear, paramProvider, "EXT_" + varName + "_T", nComp, nBoundStates); - readBoundStateDependentParameter, active>(_quad, paramProvider, "EXT_" + varName + "_TT", nComp, nBoundStates); - readBoundStateDependentParameter, active>(_cube, paramProvider, "EXT_" + varName + "_TTT", nComp, nBoundStates); + readBoundStateDependentParameter, active>(_base, paramProvider, "EXT_" + varName, + nComp, nBoundStates); + readBoundStateDependentParameter, active>( + _linear, paramProvider, "EXT_" + varName + "_T", nComp, nBoundStates); + readBoundStateDependentParameter, active>( + _quad, paramProvider, "EXT_" + varName + "_TT", nComp, nBoundStates); + readBoundStateDependentParameter, active>( + _cube, paramProvider, "EXT_" + varName + "_TTT", nComp, nBoundStates); } else { const unsigned int numStates = firstNonEmptyBoundStates(nBoundStates, nComp); - readBoundStateDependentParameter, active>(_base, paramProvider, "EXT_" + varName, nComp, numStates); - readBoundStateDependentParameter, active>(_linear, paramProvider, "EXT_" + varName + "_T", nComp, numStates); - readBoundStateDependentParameter, active>(_quad, paramProvider, "EXT_" + varName + "_TT", nComp, numStates); - readBoundStateDependentParameter, active>(_cube, paramProvider, "EXT_" + varName + "_TTT", nComp, numStates); + readBoundStateDependentParameter, active>(_base, paramProvider, "EXT_" + varName, + nComp, numStates); + readBoundStateDependentParameter, active>( + _linear, paramProvider, "EXT_" + varName + "_T", nComp, numStates); + readBoundStateDependentParameter, active>( + _quad, paramProvider, "EXT_" + varName + "_TT", nComp, numStates); + readBoundStateDependentParameter, active>( + _cube, paramProvider, "EXT_" + varName + "_TTT", nComp, numStates); } } - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { if (compMajor) { - registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName), parameters, _base, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName + "_T"), parameters, _linear, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName + "_TT"), parameters, _quad, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName + "_TTT"), parameters, _cube, unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName), parameters, _base, + unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName + "_T"), parameters, + _linear, unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName + "_TT"), parameters, + _quad, unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName + "_TTT"), parameters, + _cube, unitOpIdx, parTypeIdx); } else { - registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName), parameters, _base, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_T"), parameters, _linear, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TT"), parameters, _quad, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TTT"), parameters, _cube, unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName), parameters, _base, unitOpIdx, + parTypeIdx); + registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_T"), parameters, _linear, + unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TT"), parameters, _quad, + unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TTT"), parameters, _cube, + unitOpIdx, parTypeIdx); } } - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) { _base.reserve(numElem, numSlices); _linear.reserve(numElem, numSlices); @@ -1182,7 +1545,8 @@ class ExternalBaseComponentBoundStateDependentParameter _cube.reserve(numElem, numSlices); } - inline void update(util::SlicedVector& result, double extVal, unsigned int nComp, unsigned int const* nBoundStates) const + inline void update(util::SlicedVector& result, double extVal, unsigned int nComp, + unsigned int const* nBoundStates) const { update(result.data(), extVal, nComp, nBoundStates); } @@ -1190,40 +1554,64 @@ class ExternalBaseComponentBoundStateDependentParameter inline void update(active* result, double extVal, unsigned int nComp, unsigned int const* nBoundStates) const { for (std::size_t i = 0; i < _base.size(); ++i) - result[i] = _base.native(i) + extVal * (_linear.native(i) + extVal * (_quad.native(i) + extVal * _cube.native(i))); + result[i] = + _base.native(i) + extVal * (_linear.native(i) + extVal * (_quad.native(i) + extVal * _cube.native(i))); } - inline void updateTimeDerivative(util::SlicedVector& result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(util::SlicedVector& result, double extVal, double extTimeDiff, + unsigned int nComp, unsigned int const* nBoundStates) const { updateTimeDerivative(result.data(), extVal, extTimeDiff, nComp, nBoundStates); } - inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, + unsigned int const* nBoundStates) const { for (std::size_t i = 0; i < _base.size(); ++i) - result[i] = extTimeDiff * (static_cast(_linear.native(i)) + extVal * (2.0 * static_cast(_quad.native(i)) + 3.0 * extVal * static_cast(_cube.native(i)))); + result[i] = extTimeDiff * (static_cast(_linear.native(i)) + + extVal * (2.0 * static_cast(_quad.native(i)) + + 3.0 * extVal * static_cast(_cube.native(i)))); } - inline typename util::SlicedVector::size_type slices() const CADET_NOEXCEPT { return _base.slices(); } - inline typename util::SlicedVector::size_type size() const CADET_NOEXCEPT { return _base.size(); } - inline bool allSameSize() const CADET_NOEXCEPT { return (_base.size() == _linear.size()) && (_base.size() == _quad.size()) && (_base.size() == _cube.size()); } + inline typename util::SlicedVector::size_type slices() const CADET_NOEXCEPT + { + return _base.slices(); + } + inline typename util::SlicedVector::size_type size() const CADET_NOEXCEPT + { + return _base.size(); + } + inline bool allSameSize() const CADET_NOEXCEPT + { + return (_base.size() == _linear.size()) && (_base.size() == _quad.size()) && (_base.size() == _cube.size()); + } - inline std::size_t additionalDynamicMemory(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT + inline std::size_t additionalDynamicMemory(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT { if (compMajor) - return totalNumBoundStates * sizeof(active) + alignof(active) + (nComp + 1) * sizeof(typename util::SlicedVector::size_type) + alignof(typename util::SlicedVector::size_type); + return totalNumBoundStates * sizeof(active) + alignof(active) + + (nComp + 1) * sizeof(typename util::SlicedVector::size_type) + + alignof(typename util::SlicedVector::size_type); else { const unsigned int numStates = firstNonEmptyBoundStates(nBoundStates, nComp); - return numStates * nComp * sizeof(active) + alignof(active) + (numStates + 1) * sizeof(typename util::SlicedVector::size_type) + alignof(typename util::SlicedVector::size_type); + return numStates * nComp * sizeof(active) + alignof(active) + + (numStates + 1) * sizeof(typename util::SlicedVector::size_type) + + alignof(typename util::SlicedVector::size_type); } } - inline storage_t& base() CADET_NOEXCEPT { return _base; } - inline const storage_t& base() const CADET_NOEXCEPT { return _base; } + inline storage_t& base() CADET_NOEXCEPT + { + return _base; + } + inline const storage_t& base() const CADET_NOEXCEPT + { + return _base; + } - template - inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const + template inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const { cache.fromTemplate(buffer, _base); } @@ -1235,7 +1623,6 @@ class ExternalBaseComponentBoundStateDependentParameter util::SlicedVector _cube; }; - /** * @brief Component and bound state dependent external parameter * @details Components contain values for each bound state. @@ -1248,7 +1635,6 @@ typedef ExternalBaseComponentBoundStateDependentParameter ExternalComponen */ typedef ExternalBaseComponentBoundStateDependentParameter ExternalComponentBoundStateMajorDependentParameter; - /** * @brief Component dependent bound-state matrix valued external parameter * @details Holds a square matrix for each component that has the size of the number of corresponding bound states. @@ -1256,26 +1642,37 @@ typedef ExternalBaseComponentBoundStateDependentParameter ExternalCompone class ExternalComponentDependentBoundStateMatrixParameter { public: - typedef util::SlicedVector storage_t; - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { - readMatrixValuedBoundStateDependentParameter, active>(_base, paramProvider, "EXT_" + varName, nComp, nBoundStates); - readMatrixValuedBoundStateDependentParameter, active>(_linear, paramProvider, "EXT_" + varName + "_T", nComp, nBoundStates); - readMatrixValuedBoundStateDependentParameter, active>(_quad, paramProvider, "EXT_" + varName + "_TT", nComp, nBoundStates); - readMatrixValuedBoundStateDependentParameter, active>(_cube, paramProvider, "EXT_" + varName + "_TTT", nComp, nBoundStates); + readMatrixValuedBoundStateDependentParameter, active>( + _base, paramProvider, "EXT_" + varName, nComp, nBoundStates); + readMatrixValuedBoundStateDependentParameter, active>( + _linear, paramProvider, "EXT_" + varName + "_T", nComp, nBoundStates); + readMatrixValuedBoundStateDependentParameter, active>( + _quad, paramProvider, "EXT_" + varName + "_TT", nComp, nBoundStates); + readMatrixValuedBoundStateDependentParameter, active>( + _cube, paramProvider, "EXT_" + varName + "_TTT", nComp, nBoundStates); } - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { - registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName), parameters, _base, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName + "_T"), parameters, _linear, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName + "_TT"), parameters, _quad, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName + "_TTT"), parameters, _cube, unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName), parameters, _base, + unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName + "_T"), parameters, + _linear, unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName + "_TT"), parameters, + _quad, unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParamCompMajor(hashStringRuntime("EXT_" + varName + "_TTT"), parameters, + _cube, unitOpIdx, parTypeIdx); } - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) { unsigned int sumSquared = 0; for (unsigned int i = 0; i < nComp; ++i) @@ -1287,7 +1684,8 @@ class ExternalComponentDependentBoundStateMatrixParameter _cube.reserve(sumSquared, nComp); } - inline void update(util::SlicedVector& result, double extVal, unsigned int nComp, unsigned int const* nBoundStates) const + inline void update(util::SlicedVector& result, double extVal, unsigned int nComp, + unsigned int const* nBoundStates) const { update(result.data(), extVal, nComp, nBoundStates); } @@ -1295,38 +1693,60 @@ class ExternalComponentDependentBoundStateMatrixParameter inline void update(active* result, double extVal, unsigned int nComp, unsigned int const* nBoundStates) const { for (std::size_t i = 0; i < _base.size(); ++i) - result[i] = _base.native(i) + extVal * (_linear.native(i) + extVal * (_quad.native(i) + extVal * _cube.native(i))); + result[i] = + _base.native(i) + extVal * (_linear.native(i) + extVal * (_quad.native(i) + extVal * _cube.native(i))); } - inline void updateTimeDerivative(util::SlicedVector& result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(util::SlicedVector& result, double extVal, double extTimeDiff, + unsigned int nComp, unsigned int const* nBoundStates) const { updateTimeDerivative(result.data(), extVal, extTimeDiff, nComp, nBoundStates); } - inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, + unsigned int const* nBoundStates) const { for (std::size_t i = 0; i < _base.size(); ++i) - result[i] = extTimeDiff * (static_cast(_linear.native(i)) + extVal * (2.0 * static_cast(_quad.native(i)) + 3.0 * extVal * static_cast(_cube.native(i)))); + result[i] = extTimeDiff * (static_cast(_linear.native(i)) + + extVal * (2.0 * static_cast(_quad.native(i)) + + 3.0 * extVal * static_cast(_cube.native(i)))); } - inline typename util::SlicedVector::size_type slices() const CADET_NOEXCEPT { return _base.slices(); } - inline typename util::SlicedVector::size_type size() const CADET_NOEXCEPT { return _base.size(); } - inline bool allSameSize() const CADET_NOEXCEPT { return (_base.size() == _linear.size()) && (_base.size() == _quad.size()) && (_base.size() == _cube.size()); } + inline typename util::SlicedVector::size_type slices() const CADET_NOEXCEPT + { + return _base.slices(); + } + inline typename util::SlicedVector::size_type size() const CADET_NOEXCEPT + { + return _base.size(); + } + inline bool allSameSize() const CADET_NOEXCEPT + { + return (_base.size() == _linear.size()) && (_base.size() == _quad.size()) && (_base.size() == _cube.size()); + } - inline std::size_t additionalDynamicMemory(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT + inline std::size_t additionalDynamicMemory(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT { unsigned int sumSquared = 0; for (unsigned int i = 0; i < nComp; ++i) sumSquared += nBoundStates[i] * nBoundStates[i]; - return sumSquared * sizeof(active) + alignof(active) + (nComp + 1) * sizeof(typename util::SlicedVector::size_type) + alignof(typename util::SlicedVector::size_type); + return sumSquared * sizeof(active) + alignof(active) + + (nComp + 1) * sizeof(typename util::SlicedVector::size_type) + + alignof(typename util::SlicedVector::size_type); } - inline storage_t& base() CADET_NOEXCEPT { return _base; } - inline const storage_t& base() const CADET_NOEXCEPT { return _base; } + inline storage_t& base() CADET_NOEXCEPT + { + return _base; + } + inline const storage_t& base() const CADET_NOEXCEPT + { + return _base; + } - template - inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const + template inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const { cache.fromTemplate(buffer, _base); } @@ -1338,7 +1758,6 @@ class ExternalComponentDependentBoundStateMatrixParameter util::SlicedVector _cube; }; - /** * @brief Component dependent component vector valued external parameter * @details Holds a vector for each component that has the size of the number of components. @@ -1346,26 +1765,37 @@ class ExternalComponentDependentBoundStateMatrixParameter class ExternalComponentDependentComponentVectorParameter { public: - typedef util::SlicedVector storage_t; - inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBoundStates) + inline void configure(const std::string& varName, IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBoundStates) { - readBoundStateDependentParameter, active>(_base, paramProvider, "EXT_" + varName, nComp, nComp); - readBoundStateDependentParameter, active>(_linear, paramProvider, "EXT_" + varName + "_T", nComp, nComp); - readBoundStateDependentParameter, active>(_quad, paramProvider, "EXT_" + varName + "_TT", nComp, nComp); - readBoundStateDependentParameter, active>(_cube, paramProvider, "EXT_" + varName + "_TTT", nComp, nComp); + readBoundStateDependentParameter, active>(_base, paramProvider, "EXT_" + varName, + nComp, nComp); + readBoundStateDependentParameter, active>(_linear, paramProvider, + "EXT_" + varName + "_T", nComp, nComp); + readBoundStateDependentParameter, active>(_quad, paramProvider, + "EXT_" + varName + "_TT", nComp, nComp); + readBoundStateDependentParameter, active>(_cube, paramProvider, + "EXT_" + varName + "_TTT", nComp, nComp); } - inline void registerParam(const std::string& varName, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParam(const std::string& varName, std::unordered_map& parameters, + UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, + unsigned int const* nBoundStates) { - registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName), parameters, _base, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_T"), parameters, _linear, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TT"), parameters, _quad, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TTT"), parameters, _cube, unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName), parameters, _base, unitOpIdx, + parTypeIdx); + registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_T"), parameters, _linear, + unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TT"), parameters, _quad, + unitOpIdx, parTypeIdx); + registerComponentBoundStateDependentParam(hashStringRuntime("EXT_" + varName + "_TTT"), parameters, _cube, + unitOpIdx, parTypeIdx); } - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) { const unsigned int compSquared = nComp * nComp; @@ -1375,7 +1805,8 @@ class ExternalComponentDependentComponentVectorParameter _cube.reserve(compSquared, nComp); } - inline void update(util::SlicedVector& result, double extVal, unsigned int nComp, unsigned int const* nBoundStates) const + inline void update(util::SlicedVector& result, double extVal, unsigned int nComp, + unsigned int const* nBoundStates) const { update(result.data(), extVal, nComp, nBoundStates); } @@ -1383,34 +1814,56 @@ class ExternalComponentDependentComponentVectorParameter inline void update(active* result, double extVal, unsigned int nComp, unsigned int const* nBoundStates) const { for (std::size_t i = 0; i < _base.size(); ++i) - result[i] = _base.native(i) + extVal * (_linear.native(i) + extVal * (_quad.native(i) + extVal * _cube.native(i))); + result[i] = + _base.native(i) + extVal * (_linear.native(i) + extVal * (_quad.native(i) + extVal * _cube.native(i))); } - inline void updateTimeDerivative(util::SlicedVector& result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(util::SlicedVector& result, double extVal, double extTimeDiff, + unsigned int nComp, unsigned int const* nBoundStates) const { updateTimeDerivative(result.data(), extVal, extTimeDiff, nComp, nBoundStates); } - inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, unsigned int const* nBoundStates) const + inline void updateTimeDerivative(active* result, double extVal, double extTimeDiff, unsigned int nComp, + unsigned int const* nBoundStates) const { for (std::size_t i = 0; i < _base.size(); ++i) - result[i] = extTimeDiff * (static_cast(_linear.native(i)) + extVal * (2.0 * static_cast(_quad.native(i)) + 3.0 * extVal * static_cast(_cube.native(i)))); + result[i] = extTimeDiff * (static_cast(_linear.native(i)) + + extVal * (2.0 * static_cast(_quad.native(i)) + + 3.0 * extVal * static_cast(_cube.native(i)))); } - inline typename util::SlicedVector::size_type slices() const CADET_NOEXCEPT { return _base.slices(); } - inline typename util::SlicedVector::size_type size() const CADET_NOEXCEPT { return _base.size(); } - inline bool allSameSize() const CADET_NOEXCEPT { return (_base.size() == _linear.size()) && (_base.size() == _quad.size()) && (_base.size() == _cube.size()); } + inline typename util::SlicedVector::size_type slices() const CADET_NOEXCEPT + { + return _base.slices(); + } + inline typename util::SlicedVector::size_type size() const CADET_NOEXCEPT + { + return _base.size(); + } + inline bool allSameSize() const CADET_NOEXCEPT + { + return (_base.size() == _linear.size()) && (_base.size() == _quad.size()) && (_base.size() == _cube.size()); + } - inline std::size_t additionalDynamicMemory(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT + inline std::size_t additionalDynamicMemory(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT { - return nComp * nComp * sizeof(active) + alignof(active) + (nComp + 1) * sizeof(typename util::SlicedVector::size_type) + alignof(typename util::SlicedVector::size_type); + return nComp * nComp * sizeof(active) + alignof(active) + + (nComp + 1) * sizeof(typename util::SlicedVector::size_type) + + alignof(typename util::SlicedVector::size_type); } - inline storage_t& base() CADET_NOEXCEPT { return _base; } - inline const storage_t& base() const CADET_NOEXCEPT { return _base; } + inline storage_t& base() CADET_NOEXCEPT + { + return _base; + } + inline const storage_t& base() const CADET_NOEXCEPT + { + return _base; + } - template - inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const + template inline void prepareCache(T& cache, LinearBufferAllocator& buffer) const { cache.fromTemplate(buffer, _base); } @@ -1422,9 +1875,8 @@ class ExternalComponentDependentComponentVectorParameter util::SlicedVector _cube; }; +} // namespace model -} // namespace model - -} // namespace cadet +} // namespace cadet -#endif // LIBCADET_PARAMETERS_HPP_ +#endif // LIBCADET_PARAMETERS_HPP_ diff --git a/src/libcadet/model/ReactionModel.hpp b/src/libcadet/model/ReactionModel.hpp index c44a29a21..bf204b165 100644 --- a/src/libcadet/model/ReactionModel.hpp +++ b/src/libcadet/model/ReactionModel.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines reaction model interfaces. */ @@ -26,7 +26,7 @@ #include "linalg/DenseMatrix.hpp" #include "linalg/BandMatrix.hpp" #ifdef ENABLE_DG - #include "linalg/BandedEigenSparseRowIterator.hpp" +#include "linalg/BandedEigenSparseRowIterator.hpp" #endif #include "linalg/CompressedSparseMatrix.hpp" #include "AutoDiff.hpp" @@ -53,8 +53,9 @@ namespace model class IDynamicReactionModel { public: - - virtual ~IDynamicReactionModel() CADET_NOEXCEPT { } + virtual ~IDynamicReactionModel() CADET_NOEXCEPT + { + } /** * @brief Returns the name of the dynamic reaction model @@ -85,26 +86,29 @@ class IDynamicReactionModel * @brief Sets the number of components in the model * @details This function is called prior to configure() by the underlying model. * It can only be called once. Model parameters are configured by configure(). - * + * * @param [in] paramProvider Parameter provider * @param [in] nComp Number of components * @param [in] nBound Array of size @p nComp which contains the number of bound states for each component - * @param [in] boundOffset Array of size @p nComp with offsets to the first bound state of each component beginning from the solid phase + * @param [in] boundOffset Array of size @p nComp with offsets to the first bound state of each component beginning + * from the solid phase */ - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) = 0; + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) = 0; /** - * @brief Configures the model by extracting all non-structural parameters (e.g., model parameters) from the given @p paramProvider + * @brief Configures the model by extracting all non-structural parameters (e.g., model parameters) from the given + * @p paramProvider * @details The scope of the cadet::IParameterProvider is left unchanged on return. - * + * * The structure of the model is left unchanged, that is, the number of degrees of * freedom stays the same (e.g., number of bound states is left unchanged). Only * true (non-structural) model parameters are read and changed. - * + * * This function may only be called if configureModelDiscretization() has been called * in the past. Contrary to configureModelDiscretization(), it can be called multiple * times. - * + * * @param [in] paramProvider Parameter provider * @param [in] unitOpIdx Index of the unit operation this dynamic reaction model belongs to * @param [in] parTypeIdx Index of the particle type this dynamic reaction model belongs to @@ -115,7 +119,7 @@ class IDynamicReactionModel /** * @brief Sets external functions for this dynamic reaction model * @details The external functions are not owned by this IDynamicReactionModel. - * + * * @param [in] extFuns Pointer to array of IExternalFunction objects of size @p size * @param [in] size Number of elements in the IExternalFunction array @p extFuns */ @@ -137,7 +141,7 @@ class IDynamicReactionModel /** * @brief Sets a parameter value * @details The parameter identified by its unique parameter is set to the given value. - * + * * @param [in] pId ParameterId that identifies the parameter uniquely * @param [in] value Value of the parameter * @return @c true if the parameter has been successfully set to the given value, @@ -178,23 +182,25 @@ class IDynamicReactionModel * @param [in] nBoundStates Array with bound states for each component * @return Size of the workspace in bytes */ - virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT = 0; + virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT = 0; /** * @brief Evaluates the residual for one liquid phase cell * @details Adds the dynamic reaction terms to the residual vector for the given * liquid phase cell. The reaction terms are premultiplied with a given * factor. That is, this function realizes - * \f[ \begin{align} + * \f[ \begin{align} * \text{res} = \text{res} + \gamma R * \end{align} \f] * where @f$ R @f$ are the reaction terms. - * + * * This function is called simultaneously from multiple threads. - * + * * @param [in] t Current time point * @param [in] secIdx Index of the current section - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] y Pointer to first component in the current cell * @param [out] res Pointer to residual of the first component in the current particle shell's liquid phase * @param [in] factor Factor @f$ \gamma @f$ @@ -202,106 +208,140 @@ class IDynamicReactionModel * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, - active* res, const active& factor, LinearBufferAllocator workSpace) const = 0; + active* res, const active& factor, LinearBufferAllocator workSpace) const = 0; virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, - active* res, double factor, LinearBufferAllocator workSpace) const = 0; + active* res, double factor, LinearBufferAllocator workSpace) const = 0; virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, - active* res, double factor, LinearBufferAllocator workSpace) const = 0; + active* res, double factor, LinearBufferAllocator workSpace) const = 0; virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, - double* res, double factor, LinearBufferAllocator workSpace) const = 0; + double* res, double factor, LinearBufferAllocator workSpace) const = 0; /** * @brief Adds the analytical Jacobian of the reaction terms for one liquid phase cell * @details Adds the Jacobian of the dynamic reaction terms for the given liquid phase * cell to the full Jacobian. The reaction terms are premultiplied with a given * factor. That is, this function realizes - * \f[ \begin{align} + * \f[ \begin{align} * J = J + \gamma J_R * \end{align} \f] * where @f$ J_R @f$ is the Jacobian of the reaction terms. - * + * * This function is called simultaneously from multiple threads. * It can be left out (empty implementation) if AD is used to evaluate the Jacobian. * * @param [in] t Current time point * @param [in] secIdx Index of the current section - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] y Pointer to first component in the current cell * @param [in] factor Factor @f$ \gamma @f$ - * @param [in,out] jac Row iterator pointing to the first component row of the underlying matrix in which the Jacobian is stored + * @param [in,out] jac Row iterator pointing to the first component row of the underlying matrix in which the + * Jacobian is stored * @param [in,out] workSpace Memory work space */ - virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double factor, linalg::BandMatrix::RowIterator jac, LinearBufferAllocator workSpace) const = 0; - virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double factor, linalg::DenseBandedRowIterator jac, LinearBufferAllocator workSpace) const = 0; - virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double factor, linalg::BandedSparseRowIterator jac, LinearBufferAllocator workSpace) const = 0; - #ifdef ENABLE_DG - virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double factor, linalg::BandedEigenSparseRowIterator jac, LinearBufferAllocator workSpace) const = 0; - #endif + virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + double factor, linalg::BandMatrix::RowIterator jac, + LinearBufferAllocator workSpace) const = 0; + virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + double factor, linalg::DenseBandedRowIterator jac, + LinearBufferAllocator workSpace) const = 0; + virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + double factor, linalg::BandedSparseRowIterator jac, + LinearBufferAllocator workSpace) const = 0; +#ifdef ENABLE_DG + virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + double factor, linalg::BandedEigenSparseRowIterator jac, + LinearBufferAllocator workSpace) const = 0; +#endif /** * @brief Evaluates the residual for one combined phase cell * @details Adds the dynamic reaction terms to the residual vector for the given * combined phase cell. The reaction terms are premultiplied with a given * factor. That is, this function realizes - * \f[ \begin{align} + * \f[ \begin{align} * \text{res} = \text{res} + \gamma R * \end{align} \f] * where @f$ R @f$ are the reaction terms. - * + * * This function is called simultaneously from multiple threads. - * + * * @param [in] t Current time point * @param [in] secIdx Index of the current section - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] yLiquid Pointer to first component in the current cell's liquid phase * @param [in] ySolid Pointer to first component in the current cell's solid phase * @param [out] resLiquid Pointer to residual of the first component in the current particle shell's liquid phase - * @param [out] resSolid Pointer to residual of the first bound state of the first component in the current particle shell + * @param [out] resSolid Pointer to residual of the first bound state of the first component in the current particle + * shell * @param [in] factor Factor @f$ \gamma @f$ * @param [in,out] workSpace Memory work space * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* yLiquid, - active const* ySolid, active* resLiquid, active* resSolid, double factor, LinearBufferAllocator workSpace) const = 0; + active const* ySolid, active* resLiquid, active* resSolid, double factor, + LinearBufferAllocator workSpace) const = 0; virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, - double const* ySolid, active* resLiquid, active* resSolid, double factor, LinearBufferAllocator workSpace) const = 0; + double const* ySolid, active* resLiquid, active* resSolid, double factor, + LinearBufferAllocator workSpace) const = 0; virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, - double const* ySolid, double* resLiquid, double* resSolid, double factor, LinearBufferAllocator workSpace) const = 0; + double const* ySolid, double* resLiquid, double* resSolid, double factor, + LinearBufferAllocator workSpace) const = 0; /** * @brief Adds the analytical Jacobian of the reaction terms for one combined phase cell * @details Adds the Jacobian of the dynamic reaction terms for the given combined phase * cell to the full Jacobian. The reaction terms are premultiplied with a given * factor. That is, this function realizes - * \f[ \begin{align} + * \f[ \begin{align} * J = J + \gamma J_R * \end{align} \f] * where @f$ J_R @f$ is the Jacobian of the reaction terms. - * + * * This function is called simultaneously from multiple threads. * It can be left out (empty implementation) if AD is used to evaluate the Jacobian. * * @param [in] t Current time point * @param [in] secIdx Index of the current section - * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, inner center = 0) + * @param [in] colPos Position in normalized coordinates (column inlet = 0, column outlet = 1; outer shell = 1, + * inner center = 0) * @param [in] yLiquid Pointer to first component in the current cell's liquid phase * @param [in] ySolid Pointer to first component in the current cell's solid phase * @param [in] factor Factor @f$ \gamma @f$ - * @param [in,out] jacLiquid Row iterator pointing to the first component row of the underlying matrix in which the Jacobian is stored - * @param [in,out] jacSolid Row iterator pointing to the first bound state row of the underlying matrix in which the Jacobian is stored + * @param [in,out] jacLiquid Row iterator pointing to the first component row of the underlying matrix in which the + * Jacobian is stored + * @param [in,out] jacSolid Row iterator pointing to the first bound state row of the underlying matrix in which the + * Jacobian is stored * @param [in,out] workSpace Memory work space */ - virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, double factor, linalg::BandMatrix::RowIterator jacLiquid, linalg::BandMatrix::RowIterator jacSolid, LinearBufferAllocator workSpace) const = 0; - virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, double factor, linalg::DenseBandedRowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, LinearBufferAllocator workSpace) const = 0; - virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, double factor, linalg::BandMatrix::RowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, LinearBufferAllocator workSpace) const = 0; - #ifdef ENABLE_DG - virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, double factor, linalg::BandedEigenSparseRowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, LinearBufferAllocator workSpace) const = 0; - #endif + virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yLiquid, double const* ySolid, double factor, + linalg::BandMatrix::RowIterator jacLiquid, + linalg::BandMatrix::RowIterator jacSolid, + LinearBufferAllocator workSpace) const = 0; + virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yLiquid, double const* ySolid, double factor, + linalg::DenseBandedRowIterator jacLiquid, + linalg::DenseBandedRowIterator jacSolid, + LinearBufferAllocator workSpace) const = 0; + virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yLiquid, double const* ySolid, double factor, + linalg::BandMatrix::RowIterator jacLiquid, + linalg::DenseBandedRowIterator jacSolid, + LinearBufferAllocator workSpace) const = 0; +#ifdef ENABLE_DG + virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yLiquid, double const* ySolid, double factor, + linalg::BandedEigenSparseRowIterator jacLiquid, + linalg::DenseBandedRowIterator jacSolid, + LinearBufferAllocator workSpace) const = 0; +#endif /** * @brief Returns the number of reactions for a liquid phase cell @@ -321,4 +361,4 @@ class IDynamicReactionModel } // namespace model } // namespace cadet -#endif // LIBCADET_REACTIONMODELINTERFACE_HPP_ +#endif // LIBCADET_REACTIONMODELINTERFACE_HPP_ diff --git a/src/libcadet/model/StirredTankModel.cpp b/src/libcadet/model/StirredTankModel.cpp index ce5be7d9b..eed30f1cb 100644 --- a/src/libcadet/model/StirredTankModel.cpp +++ b/src/libcadet/model/StirredTankModel.cpp @@ -39,30 +39,39 @@ namespace model namespace { - inline void bindingFlux(IBindingModel* binding, double t, unsigned int secIdx, const ColumnPosition& colPos, active const* yCp, active const* yQ, active* res, cadet::LinearBufferAllocator buffer, WithParamSensitivity) - { - binding->flux(t, secIdx, colPos, yQ, yCp, res, buffer, WithParamSensitivity()); - } - - inline void bindingFlux(IBindingModel* binding, double t, unsigned int secIdx, const ColumnPosition& colPos, active const* yCp, active const* yQ, active* res, cadet::LinearBufferAllocator buffer, WithoutParamSensitivity) - { - binding->flux(t, secIdx, colPos, yQ, yCp, res, buffer, WithoutParamSensitivity()); - } +inline void bindingFlux(IBindingModel* binding, double t, unsigned int secIdx, const ColumnPosition& colPos, + active const* yCp, active const* yQ, active* res, cadet::LinearBufferAllocator buffer, + WithParamSensitivity) +{ + binding->flux(t, secIdx, colPos, yQ, yCp, res, buffer, WithParamSensitivity()); +} - inline void bindingFlux(IBindingModel* binding, double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yCp, double const* yQ, active* res, cadet::LinearBufferAllocator buffer, WithParamSensitivity) - { - binding->flux(t, secIdx, colPos, yQ, yCp, res, buffer); - } +inline void bindingFlux(IBindingModel* binding, double t, unsigned int secIdx, const ColumnPosition& colPos, + active const* yCp, active const* yQ, active* res, cadet::LinearBufferAllocator buffer, + WithoutParamSensitivity) +{ + binding->flux(t, secIdx, colPos, yQ, yCp, res, buffer, WithoutParamSensitivity()); +} - inline void bindingFlux(IBindingModel* binding, double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yCp, double const* yQ, double* res, cadet::LinearBufferAllocator buffer, WithoutParamSensitivity) - { - binding->flux(t, secIdx, colPos, yQ, yCp, res, buffer); - } +inline void bindingFlux(IBindingModel* binding, double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yCp, double const* yQ, active* res, cadet::LinearBufferAllocator buffer, + WithParamSensitivity) +{ + binding->flux(t, secIdx, colPos, yQ, yCp, res, buffer); } +inline void bindingFlux(IBindingModel* binding, double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yCp, double const* yQ, double* res, cadet::LinearBufferAllocator buffer, + WithoutParamSensitivity) +{ + binding->flux(t, secIdx, colPos, yQ, yCp, res, buffer); +} +} // namespace -CSTRModel::CSTRModel(UnitOpIdx unitOpIdx) : UnitOperationBase(unitOpIdx), _nComp(0), _nParType(0), _nBound(nullptr), _boundOffset(nullptr), _strideBound(nullptr), _offsetParType(nullptr), - _totalBound(0), _analyticJac(true), _jac(), _jacFact(), _factorizeJac(false), _initConditions(0), _initConditionsDot(0), _dynReactionBulk(nullptr) +CSTRModel::CSTRModel(UnitOpIdx unitOpIdx) + : UnitOperationBase(unitOpIdx), _nComp(0), _nParType(0), _nBound(nullptr), _boundOffset(nullptr), + _strideBound(nullptr), _offsetParType(nullptr), _totalBound(0), _analyticJac(true), _jac(), _jacFact(), + _factorizeJac(false), _initConditions(0), _initConditionsDot(0), _dynReactionBulk(nullptr) { // Mutliplexed binding and reaction models make no sense in CSTR _singleBinding = false; @@ -100,8 +109,8 @@ bool CSTRModel::usesAD() const CADET_NOEXCEPT #endif } -void CSTRModel::setFlowRates(active const* in, active const* out) CADET_NOEXCEPT -{ +void CSTRModel::setFlowRates(active const* in, active const* out) CADET_NOEXCEPT +{ _flowRateIn = in[0]; _flowRateOut = out[0]; } @@ -114,7 +123,7 @@ bool CSTRModel::configureModelDiscretization(IParameterProvider& paramProvider, { const std::vector nBound = paramProvider.getIntArray("NBOUND"); _nParType = nBound.size() / _nComp; - + _nBound = new unsigned int[_nComp * _nParType]; std::copy(nBound.begin(), nBound.begin() + _nComp * _nParType, _nBound); @@ -134,7 +143,7 @@ bool CSTRModel::configureModelDiscretization(IParameterProvider& paramProvider, { bo[i] = bo[i - 1] + nb[i - 1]; } - _strideBound[j] = bo[_nComp-1] + nb[_nComp - 1]; + _strideBound[j] = bo[_nComp - 1] + nb[_nComp - 1]; _totalBound += _strideBound[j]; if (j < _nParType - 1) @@ -203,7 +212,8 @@ bool CSTRModel::configureModelDiscretization(IParameterProvider& paramProvider, { const std::vector bindModelNames = paramProvider.getStringArray("ADSORPTION_MODEL"); if (bindModelNames.size() < _nParType) - throw InvalidParameterException("Field ADSORPTION_MODEL contains too few elements (" + std::to_string(_nParType) + " required)"); + throw InvalidParameterException("Field ADSORPTION_MODEL contains too few elements (" + + std::to_string(_nParType) + " required)"); for (unsigned int i = 0; i < _nParType; ++i) { @@ -211,8 +221,11 @@ bool CSTRModel::configureModelDiscretization(IParameterProvider& paramProvider, if (!_binding[i]) throw InvalidParameterException("Unknown binding model " + bindModelNames[i]); - MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", _nParType == 1, i, _nParType == 1, _binding[i]->usesParamProviderInDiscretizationConfig()); - bindingConfSuccess = _binding[i]->configureModelDiscretization(paramProvider, _nComp, _nBound + i * _nComp, _boundOffset + i * _nComp) && bindingConfSuccess; + MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", _nParType == 1, i, _nParType == 1, + _binding[i]->usesParamProviderInDiscretizationConfig()); + bindingConfSuccess = _binding[i]->configureModelDiscretization(paramProvider, _nComp, _nBound + i * _nComp, + _boundOffset + i * _nComp) && + bindingConfSuccess; } } @@ -243,7 +256,8 @@ bool CSTRModel::configureModelDiscretization(IParameterProvider& paramProvider, { const std::vector dynReactModelNames = paramProvider.getStringArray("REACTION_MODEL_PARTICLES"); if (dynReactModelNames.size() < _nParType) - throw InvalidParameterException("Field REACTION_MODEL_PARTICLES contains too few elements (" + std::to_string(_nParType) + " required)"); + throw InvalidParameterException("Field REACTION_MODEL_PARTICLES contains too few elements (" + + std::to_string(_nParType) + " required)"); for (unsigned int i = 0; i < _nParType; ++i) { @@ -251,8 +265,11 @@ bool CSTRModel::configureModelDiscretization(IParameterProvider& paramProvider, if (!_dynReaction[i]) throw InvalidParameterException("Unknown dynamic reaction model " + dynReactModelNames[i]); - MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", _nParType == 1, i, _nParType == 1, _dynReaction[i]->usesParamProviderInDiscretizationConfig()); - reactionConfSuccess = _dynReaction[i]->configureModelDiscretization(paramProvider, _nComp, _nBound + i * _nComp, _boundOffset + i * _nComp) && reactionConfSuccess; + MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", _nParType == 1, i, _nParType == 1, + _dynReaction[i]->usesParamProviderInDiscretizationConfig()); + reactionConfSuccess = _dynReaction[i]->configureModelDiscretization( + paramProvider, _nComp, _nBound + i * _nComp, _boundOffset + i * _nComp) && + reactionConfSuccess; } } @@ -280,25 +297,34 @@ bool CSTRModel::configure(IParameterProvider& paramProvider) _parTypeVolFrac.resize(1, 1.0); if (_nParType != _parTypeVolFrac.size()) - throw InvalidParameterException("Number of elements in field PAR_TYPE_VOLFRAC does not match number of particle types"); + throw InvalidParameterException( + "Number of elements in field PAR_TYPE_VOLFRAC does not match number of particle types"); // Check that particle volume fractions sum to 1.0 - const double volFracSum = std::accumulate(_parTypeVolFrac.begin(), _parTypeVolFrac.end(), 0.0, - [](double a, const active& b) -> double { return a + static_cast(b); }); + const double volFracSum = + std::accumulate(_parTypeVolFrac.begin(), _parTypeVolFrac.end(), 0.0, + [](double a, const active& b) -> double { return a + static_cast(b); }); if (std::abs(1.0 - volFracSum) > 1e-10) - throw InvalidParameterException("Sum of field PAR_TYPE_VOLFRAC differs from 1.0 (is " + std::to_string(volFracSum) + ")"); + throw InvalidParameterException("Sum of field PAR_TYPE_VOLFRAC differs from 1.0 (is " + + std::to_string(volFracSum) + ")"); } _parameters.clear(); if (hasFlowrateFilter) - registerScalarSectionDependentParam(hashString("FLOWRATE_FILTER"), _parameters, _flowRateFilter, _unitOpIdx, ParTypeIndep); - _parameters[makeParamId(hashString("POROSITY"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_porosity; + registerScalarSectionDependentParam(hashString("FLOWRATE_FILTER"), _parameters, _flowRateFilter, _unitOpIdx, + ParTypeIndep); + _parameters[makeParamId(hashString("POROSITY"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep)] = &_porosity; if (_totalBound > 0) - registerParam1DArray(_parameters, _parTypeVolFrac, [=](bool multi, unsigned int type) { return makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, type, BoundStateIndep, ReactionIndep, SectionIndep); }); + registerParam1DArray(_parameters, _parTypeVolFrac, [=](bool multi, unsigned int type) { + return makeParamId(hashString("PAR_TYPE_VOLFRAC"), _unitOpIdx, CompIndep, type, BoundStateIndep, + ReactionIndep, SectionIndep); + }); // Register initial conditions parameters for (unsigned int i = 0; i < _nComp; ++i) - _parameters[makeParamId(hashString("INIT_C"), _unitOpIdx, i, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = _initConditions.data() + i; + _parameters[makeParamId(hashString("INIT_C"), _unitOpIdx, i, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep)] = _initConditions.data() + i; for (unsigned int type = 0; type < _nParType; ++type) { @@ -313,7 +339,8 @@ bool CSTRModel::configure(IParameterProvider& paramProvider) _parameters[initParams[i]] = ic + i; } - _parameters[makeParamId(hashString("INIT_VOLUME"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = _initConditions.data() + _nComp + _totalBound; + _parameters[makeParamId(hashString("INIT_VOLUME"), _unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = _initConditions.data() + _nComp + _totalBound; // Reconfigure binding model bool bindingConfSuccess = true; @@ -321,8 +348,8 @@ bool CSTRModel::configure(IParameterProvider& paramProvider) { for (unsigned int type = 0; type < _nParType; ++type) { - if (!_binding[type] || !_binding[type]->requiresConfiguration()) - continue; + if (!_binding[type] || !_binding[type]->requiresConfiguration()) + continue; MultiplexedScopeSelector scopeGuard(paramProvider, "adsorption", type, _nParType == 1, true); bindingConfSuccess = _binding[type]->configure(paramProvider, _unitOpIdx, type) && bindingConfSuccess; @@ -344,7 +371,8 @@ bool CSTRModel::configure(IParameterProvider& paramProvider) continue; MultiplexedScopeSelector scopeGuard(paramProvider, "reaction_particle", type, _nParType == 1, true); - dynReactionConfSuccess = _dynReaction[type]->configure(paramProvider, _unitOpIdx, type) && dynReactionConfSuccess; + dynReactionConfSuccess = + _dynReaction[type]->configure(paramProvider, _unitOpIdx, type) && dynReactionConfSuccess; } return bindingConfSuccess && dynReactionConfSuccess; @@ -416,7 +444,9 @@ void CSTRModel::useAnalyticJacobian(const bool analyticJac) #endif } -void CSTRModel::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) +void CSTRModel::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac) { if (_flowRateFilter.size() > 1) { @@ -498,7 +528,7 @@ void CSTRModel::readInitialCondition(IParameterProvider& paramProvider) if (initC.size() < _nComp) throw InvalidParameterException("INIT_C does not contain enough values for all components"); - + ad::copyToAd(initC.data(), _initConditions.data(), _nComp); if (paramProvider.exists("INIT_Q")) @@ -515,9 +545,11 @@ void CSTRModel::readInitialCondition(IParameterProvider& paramProvider) _initConditions[_nComp + _totalBound].setValue(0.0); } -void CSTRModel::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) +void CSTRModel::consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) { - double * const c = vecStateY + _nComp; + double* const c = vecStateY + _nComp; const double v = c[_nComp + _totalBound]; // Check if volume is 0 @@ -530,14 +562,15 @@ void CSTRModel::consistentInitialState(const SimulationTime& simTime, double* co const double vDot = flowIn - flowOut - static_cast(_curFlowRateFilter); // We have the equation - // \dot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]) + V * (\dot{c}_i + 1 / beta * [sum_j sum_m d_j \dot{q}_{i,m}]) = c_{in,i} * F_in + c_i * F_out + // \dot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]) + V * (\dot{c}_i + 1 / beta * [sum_j sum_m d_j + // \dot{q}_{i,m}]) = c_{in,i} * F_in + c_i * F_out // which is now algebraic wrt. c due to V = 0: // \dot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]) = c_{in,i} * F_in + c_i * F_out // Separating knowns from unknowns gives // (\dot{V} + F_out) * c + \dot{V} / beta * [sum_j sum_m d_j q_{i,m}] = c_in * F_in // We assume that all binding models are fully dynamic (i.e., they do not have - // algebraic equations). + // algebraic equations). // TODO: Figure out what to do when at least one binding model has algebraic equations. // If the binding models do not have algebraic equations, the @@ -566,7 +599,8 @@ void CSTRModel::consistentInitialState(const SimulationTime& simTime, double* co return; for (unsigned int i = 0; i < _nComp; ++i) - c[i] = vecStateY[i] * flowIn / denom;; + c[i] = vecStateY[i] * flowIn / denom; + ; const double qFactor = vDot * (1.0 / static_cast(_porosity) - 1.0) / denom; for (unsigned int type = 0; type < _nParType; ++type) @@ -601,7 +635,8 @@ void CSTRModel::consistentInitialState(const SimulationTime& simTime, double* co // Determine whether nonlinear solver is required const ColumnPosition colPos{0.0, 0.0, 0.0}; - if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, c + _nComp + _offsetParType[type], c, tlmAlloc)) + if (!_binding[type]->preConsistentInitialState(simTime.t, simTime.secIdx, colPos, + c + _nComp + _offsetParType[type], c, tlmAlloc)) continue; hasNoQSreactions = false; @@ -612,7 +647,7 @@ void CSTRModel::consistentInitialState(const SimulationTime& simTime, double* co // Mark component for conserved moiety if it has a quasi-stationary bound state unsigned int idx = 0; - for (unsigned int comp = 0; comp < _nComp; ++ comp) + for (unsigned int comp = 0; comp < _nComp; ++comp) { // Skip components that are already marked if (qsMask[comp]) @@ -686,8 +721,7 @@ void CSTRModel::consistentInitialState(const SimulationTime& simTime, double* co { ad::copyToAd(vecStateY, adJac.adY, _nComp); - jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) - { + jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) { active* const localAdY = adJac.adY + _nComp; active* const localAdRes = adJac.adRes + _nComp; @@ -701,14 +735,16 @@ void CSTRModel::consistentInitialState(const SimulationTime& simTime, double* co linalg::applyVectorSubset(x, mask, localAdY); // Call residual function - residualImpl(simTime.t, simTime.secIdx, adJac.adY, nullptr, adJac.adRes, tlmAlloc.manageRemainingMemory()); + residualImpl(simTime.t, simTime.secIdx, adJac.adY, nullptr, adJac.adRes, + tlmAlloc.manageRemainingMemory()); #ifdef CADET_CHECK_ANALYTIC_JACOBIAN std::copy_n(c, mask.len, fullX); linalg::applyVectorSubset(x, mask, fullX); // Compute analytic Jacobian - residualImpl(simTime.t, simTime.secIdx, fullX, nullptr, fullResidual, tlmAlloc.manageRemainingMemory()); + residualImpl(simTime.t, simTime.secIdx, fullX, nullptr, fullResidual, + tlmAlloc.manageRemainingMemory()); // Compare const double diff = ad::compareDenseJacobianWithAd(localAdRes, adJac.adDirOffset, _jac); @@ -737,10 +773,12 @@ void CSTRModel::consistentInitialState(const SimulationTime& simTime, double* co for (unsigned int type = 0; type < _nParType; ++type) { - const double factor = epsQ * static_cast(_parTypeVolFrac[type]);; + const double factor = epsQ * static_cast(_parTypeVolFrac[type]); + ; const unsigned int offset = _nComp + _offsetParType[type] + _boundOffset[type * _nComp + comp]; - unsigned int bIdx = numActiveComp + numMaskActive(mask, _nComp, _boundOffset[type * _nComp + comp]); + unsigned int bIdx = + numActiveComp + numMaskActive(mask, _nComp, _boundOffset[type * _nComp + comp]); for (unsigned int state = 0; state < _nBound[type * _nComp + comp]; ++state) { if (!mask.mask[offset + state]) @@ -759,14 +797,14 @@ void CSTRModel::consistentInitialState(const SimulationTime& simTime, double* co } else { - jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) - { + jacFunc = [&](double const* const x, linalg::detail::DenseMatrixBase& mat) { // Prepare input vector by overwriting masked items std::copy_n(c, mask.len, fullX + _nComp); linalg::applyVectorSubset(x, mask, fullX + _nComp); // Call residual function - residualImpl(simTime.t, simTime.secIdx, fullX, nullptr, fullResidual, tlmAlloc.manageRemainingMemory()); + residualImpl(simTime.t, simTime.secIdx, fullX, nullptr, fullResidual, + tlmAlloc.manageRemainingMemory()); // Extract Jacobian from full Jacobian mat.setAll(0.0); @@ -787,10 +825,12 @@ void CSTRModel::consistentInitialState(const SimulationTime& simTime, double* co for (unsigned int type = 0; type < _nParType; ++type) { - const double factor = epsQ * static_cast(_parTypeVolFrac[type]);; + const double factor = epsQ * static_cast(_parTypeVolFrac[type]); + ; const unsigned int offset = _nComp + _offsetParType[type] + _boundOffset[type * _nComp + comp]; - unsigned int bIdx = numActiveComp + numMaskActive(mask, _nComp, _boundOffset[type * _nComp + comp]); + unsigned int bIdx = + numActiveComp + numMaskActive(mask, _nComp, _boundOffset[type * _nComp + comp]); for (unsigned int state = 0; state < _nBound[type * _nComp + comp]; ++state) { if (!mask.mask[offset + state]) @@ -813,14 +853,14 @@ void CSTRModel::consistentInitialState(const SimulationTime& simTime, double* co // Apply nonlinear solver _nonlinearSolver->solve( - [&](double const* const x, double* const r) - { + [&](double const* const x, double* const r) { // Prepare input vector by overwriting masked items std::copy_n(c, mask.len, fullX + _nComp); linalg::applyVectorSubset(x, mask, fullX + _nComp); // Call residual function - residualImpl(simTime.t, simTime.secIdx, fullX, nullptr, fullResidual, tlmAlloc.manageRemainingMemory()); + residualImpl(simTime.t, simTime.secIdx, fullX, nullptr, fullResidual, + tlmAlloc.manageRemainingMemory()); // Extract values from residual linalg::selectVectorSubset(fullResidual + _nComp, mask, r); @@ -839,10 +879,12 @@ void CSTRModel::consistentInitialState(const SimulationTime& simTime, double* co for (unsigned int type = 0; type < _nParType; ++type) { - const double factor = epsQ * static_cast(_parTypeVolFrac[type]);; + const double factor = epsQ * static_cast(_parTypeVolFrac[type]); + ; const unsigned int offset = _nComp + _offsetParType[type] + _boundOffset[type * _nComp + comp]; - unsigned int bIdx = numActiveComp + numMaskActive(mask, _nComp, _boundOffset[type * _nComp + comp]); + unsigned int bIdx = + numActiveComp + numMaskActive(mask, _nComp, _boundOffset[type * _nComp + comp]); for (unsigned int state = 0; state < _nBound[type * _nComp + comp]; ++state) { if (!mask.mask[offset + state]) @@ -867,12 +909,14 @@ void CSTRModel::consistentInitialState(const SimulationTime& simTime, double* co for (unsigned int type = 0; type < _nParType; ++type) { const ColumnPosition colPos{0.0, 0.0, 0.0}; - _binding[type]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, c + _nComp + _offsetParType[type], c, tlmAlloc); + _binding[type]->postConsistentInitialState(simTime.t, simTime.secIdx, colPos, + c + _nComp + _offsetParType[type], c, tlmAlloc); } } } -void CSTRModel::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem) +void CSTRModel::consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem) { double const* const c = vecStateY + _nComp; double* const cDot = vecStateYdot + _nComp; @@ -898,11 +942,14 @@ void CSTRModel::consistentInitialTimeDerivative(const SimulationTime& simTime, d cDot[_nComp + _totalBound] = vDot; // We have the equation - // V * \dot{c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]} + \dot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]) = c_{in,i} * F_in - c_i * F_out + // V * \dot{c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]} + \dot{V} * (c_i + 1 / beta * [sum_j sum_m d_j + // q_{i,m}]) = c_{in,i} * F_in - c_i * F_out // which is now algebraic wrt. c due to V = 0: // \dot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]) = c_{in,i} * F_in - c_i * F_out // So we take the derivative wrt. to time t on both sides - // 2 * \dot{V} * \dot{c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]} + V * \ddot{c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]} + \ddot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]) = \dot{c}_{in,i} * F_in - \dot{c} * F_out + // 2 * \dot{V} * \dot{c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]} + V * \ddot{c_i + 1 / beta * [sum_j sum_m + // d_j q_{i,m}]} + \ddot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]) = \dot{c}_{in,i} * F_in - \dot{c} + // * F_out // and use the fact that \ddot{V} = 0 and V = 0 to arrive at // 2 * \dot{V} * \dot{c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]} = \dot{c}_{in,i} * F_in - \dot{c} * F_out // Separating knowns from unknowns gives @@ -921,7 +968,7 @@ void CSTRModel::consistentInitialTimeDerivative(const SimulationTime& simTime, d // F_in <= F_filter // On the other hand, we infer from (*) that // 0 <= F_out <= 0 => F_out = 0 => F_in = F_filter - // This, in turn, concludes \dot{V} = 0. In this situation, + // This, in turn, concludes \dot{V} = 0. In this situation, // F_in = F_filter = 0 // has to hold as otherwise the liquid (solvent) is immediately // and fully taken out, leaving only the pure dry components. @@ -999,9 +1046,9 @@ void CSTRModel::consistentInitialTimeDerivative(const SimulationTime& simTime, d std::fill_n(static_cast(dFluxDt), _strideBound[type], 0.0); if (_binding[type]->dependsOnTime()) { - _binding[type]->timeDerivativeQuasiStationaryFluxes(simTime.t, simTime.secIdx, - ColumnPosition{0.0, 0.0, 0.0}, - c, c + _nComp + _offsetParType[type], static_cast(dFluxDt), tlmAlloc); + _binding[type]->timeDerivativeQuasiStationaryFluxes( + simTime.t, simTime.secIdx, ColumnPosition{0.0, 0.0, 0.0}, c, c + _nComp + _offsetParType[type], + static_cast(dFluxDt), tlmAlloc); } // Copy row from original Jacobian and set right hand side @@ -1031,12 +1078,14 @@ void CSTRModel::consistentInitialTimeDerivative(const SimulationTime& simTime, d } } -void CSTRModel::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) +void CSTRModel::leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) { // It is assumed that the bound states are (approximately) correctly initialized. // Thus, only the liquid phase has to be initialized - double * const c = vecStateY + _nComp; + double* const c = vecStateY + _nComp; const double v = c[_nComp]; // Check if volume is 0 @@ -1049,7 +1098,8 @@ void CSTRModel::leanConsistentInitialState(const SimulationTime& simTime, double const double vDot = flowIn - flowOut - static_cast(_curFlowRateFilter); // We have the equation - // \dot{V} * (c + 1 / beta * [sum_j q_j]) + V * (\dot{c} + 1 / beta * [sum_j \dot{q}_j]) = c_in * F_in + c * F_out + // \dot{V} * (c + 1 / beta * [sum_j q_j]) + V * (\dot{c} + 1 / beta * [sum_j \dot{q}_j]) = c_in * F_in + c * + // F_out // which is now algebraic wrt. c due to V = 0: // \dot{V} * (c + 1 / beta * [sum_j q_j]) = c_in * F_in + c * F_out // Separating knowns from unknowns gives @@ -1077,7 +1127,8 @@ void CSTRModel::leanConsistentInitialState(const SimulationTime& simTime, double return; for (unsigned int i = 0; i < _nComp; ++i) - c[i] = vecStateY[i] * flowIn / denom;; + c[i] = vecStateY[i] * flowIn / denom; + ; const double qFactor = vDot * (1.0 / static_cast(_porosity) - 1.0) / denom; for (unsigned int type = 0; type < _nParType; ++type) @@ -1098,7 +1149,8 @@ void CSTRModel::leanConsistentInitialState(const SimulationTime& simTime, double } } -void CSTRModel::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem) +void CSTRModel::leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, + double* const res, util::ThreadLocalStorage& threadLocalMem) { // It is assumed that the bound states are (approximately) correctly initialized. // Thus, only the liquid phase has to be initialized @@ -1119,11 +1171,14 @@ void CSTRModel::leanConsistentInitialTimeDerivative(double t, double const* cons if (v == 0.0) { // We have the equation - // V * \dot{c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]} + \dot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]) = c_{in,i} * F_in - c_i * F_out + // V * \dot{c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]} + \dot{V} * (c_i + 1 / beta * [sum_j sum_m d_j + // q_{i,m}]) = c_{in,i} * F_in - c_i * F_out // which is now algebraic wrt. c due to V = 0: // \dot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]) = c_{in,i} * F_in - c_i * F_out // So we take the derivative wrt. to time t on both sides - // 2 * \dot{V} * \dot{c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]} + V * \ddot{c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]} + \ddot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]) = \dot{c}_{in,i} * F_in - \dot{c} * F_out + // 2 * \dot{V} * \dot{c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]} + V * \ddot{c_i + 1 / beta * [sum_j sum_m + // d_j q_{i,m}]} + \ddot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]) = \dot{c}_{in,i} * F_in - \dot{c} + // * F_out // and use the fact that \ddot{V} = 0 and V = 0 to arrive at // 2 * \dot{V} * \dot{c_i + 1 / beta * [sum_j sum_m d_j q_{i,m}]} = \dot{c}_{in,i} * F_in - \dot{c} * F_out // Separating knowns from unknowns gives @@ -1142,7 +1197,7 @@ void CSTRModel::leanConsistentInitialTimeDerivative(double t, double const* cons // F_in <= F_filter // On the other hand, we infer from (*) that // 0 <= F_out <= 0 => F_out = 0 => F_in = F_filter - // This, in turn, concludes \dot{V} = 0. In this situation, + // This, in turn, concludes \dot{V} = 0. In this situation, // F_in = F_filter = 0 // has to hold as otherwise the liquid (solvent) is immediately // and fully taken out, leaving only the pure dry components. @@ -1193,10 +1248,14 @@ void CSTRModel::leanConsistentInitialTimeDerivative(double t, double const* cons { const double invBeta = 1.0 / static_cast(_porosity) - 1.0; - // Concentrations: V * (\dot{c} + 1 / beta * [sum_j sum_m d_j \dot{q}_{j,m}]) = c_in * F_in + c * F_out - \dot{V} * (c + 1 / beta * [sum_j sum_m d_j q_{j,m}]) - // <=> V * \dot{c} = c_in * F_in + c * F_out - \dot{V} * (c + 1 / beta * [sum_j sum_m d_j q_{j,m}]) - V / beta * [sum_j sum_m d_j \dot{q}_{j,m}] - // = -res - \dot{V} * (c + 1 / beta * [sum_j sum_m d_j q_{j,m}]) - V / beta * [sum_j sum_m d_j \dot{q}_{j,m}] - // => \dot{c} = (-res - \dot{V} * (c + 1 / beta * [sum_j sum_m d_j q_{j,m}]) - V / beta * [sum_j sum_m d_j \dot{q}_m]) / V + // Concentrations: V * (\dot{c} + 1 / beta * [sum_j sum_m d_j \dot{q}_{j,m}]) = c_in * F_in + c * F_out - + // \dot{V} * (c + 1 / beta * [sum_j sum_m d_j q_{j,m}]) + // <=> V * \dot{c} = c_in * F_in + c * F_out - \dot{V} * (c + 1 / beta * [sum_j sum_m d_j q_{j,m}]) + // - V / beta * [sum_j sum_m d_j \dot{q}_{j,m}] + // = -res - \dot{V} * (c + 1 / beta * [sum_j sum_m d_j q_{j,m}]) - V / beta * [sum_j + // sum_m d_j \dot{q}_{j,m}] + // => \dot{c} = (-res - \dot{V} * (c + 1 / beta * [sum_j sum_m d_j q_{j,m}]) - V / beta * [sum_j sum_m d_j + // \dot{q}_m]) / V for (unsigned int i = 0; i < _nComp; ++i) { double qSum = 0.0; @@ -1204,7 +1263,7 @@ void CSTRModel::leanConsistentInitialTimeDerivative(double t, double const* cons for (unsigned int type = 0; type < _nParType; ++type) { double const* const localQ = c + _nComp + _offsetParType[type] + _boundOffset[type * _nComp + i]; - double const* const localQdot = cDot + _nComp + _offsetParType[type] +_boundOffset[type * _nComp + i]; + double const* const localQdot = cDot + _nComp + _offsetParType[type] + _boundOffset[type * _nComp + i]; double qSumType = 0.0; double qDotSumType = 0.0; for (unsigned int j = 0; j < _nBound[type * _nComp + i]; ++j) @@ -1222,23 +1281,28 @@ void CSTRModel::leanConsistentInitialTimeDerivative(double t, double const* cons } void CSTRModel::leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem) { consistentInitialSensitivity(simTime, simState, vecSensY, vecSensYdot, adRes, threadLocalMem); } -int CSTRModel::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int CSTRModel::jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) { return residual(simTime, simState, res, adJac, threadLocalMem, true, false); } -int CSTRModel::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem) +int CSTRModel::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem) { - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem.get()); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem.get()); } template -int CSTRModel::residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, LinearBufferAllocator tlmAlloc) +int CSTRModel::residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, + ResidualType* const res, LinearBufferAllocator tlmAlloc) { StateType const* const cIn = y; StateType const* const c = y + _nComp; @@ -1256,7 +1320,8 @@ int CSTRModel::residualImpl(double t, unsigned int secIdx, StateType const* cons res[i] = cIn[i]; } - // Concentrations: \dot{V} * (c + 1 / beta * [sum_j q_j]) + V * (\dot{c} + 1 / beta * [sum_j \dot{q}_j]) = c_in * F_in - c * F_out + // Concentrations: \dot{V} * (c + 1 / beta * [sum_j q_j]) + V * (\dot{c} + 1 / beta * [sum_j \dot{q}_j]) = c_in * + // F_in - c * F_out const ParamType invBeta = 1.0 / static_cast(_porosity) - 1.0; ResidualType* const resC = res + _nComp; for (unsigned int i = 0; i < _nComp; ++i) @@ -1279,7 +1344,7 @@ int CSTRModel::residualImpl(double t, unsigned int secIdx, StateType const* cons { StateType const* const q = c + _nComp + _offsetParType[type] + _boundOffset[type * _nComp + i]; double const* const qDot = cDot + _nComp + _offsetParType[type] + _boundOffset[type * _nComp + i]; - + StateType qSumType = 0.0; double qDotSumType = 0.0; for (unsigned int j = 0; j < _nBound[type * _nComp + i]; ++j) @@ -1305,7 +1370,8 @@ int CSTRModel::residualImpl(double t, unsigned int secIdx, StateType const* cons // Assemble Jacobian: Liquid phase - // Concentrations: \dot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{j,i,m}]) + V * (\dot{c}_i + 1 / beta * [sum_j sum_m d_j \dot{q}_{j,i,m}]) - c_{in,i} * F_in + c_i * F_out == 0 + // Concentrations: \dot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{j,i,m}]) + V * (\dot{c}_i + 1 / beta * [sum_j + // sum_m d_j \dot{q}_{j,i,m}]) - c_{in,i} * F_in + c_i * F_out == 0 const double vDotTimeFactor = static_cast(vDot); for (unsigned int i = 0; i < _nComp; ++i) { @@ -1360,7 +1426,8 @@ int CSTRModel::residualImpl(double t, unsigned int secIdx, StateType const* cons for (unsigned int comp = 0; comp < _nComp; ++comp) _jac.native(comp, _nComp + _totalBound) += static_cast(flux[comp]); - _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(c), -static_cast(v), _jac.row(0), subAlloc); + _dynReactionBulk->analyticJacobianLiquidAdd(t, secIdx, colPos, reinterpret_cast(c), + -static_cast(v), _jac.row(0), subAlloc); } } @@ -1369,7 +1436,8 @@ int CSTRModel::residualImpl(double t, unsigned int secIdx, StateType const* cons { // Binding IBindingModel* const binding = _binding[type]; - bindingFlux(binding, t, secIdx, colPos, c, c + _nComp + _offsetParType[type], res + 2 * _nComp + _offsetParType[type], tlmAlloc, typename ParamSens::enabled()); + bindingFlux(binding, t, secIdx, colPos, c, c + _nComp + _offsetParType[type], + res + 2 * _nComp + _offsetParType[type], tlmAlloc, typename ParamSens::enabled()); int const* const qsReaction = binding->reactionQuasiStationarity(); @@ -1395,7 +1463,9 @@ int CSTRModel::residualImpl(double t, unsigned int secIdx, StateType const* cons if (wantJac) { // Assemble Jacobian: Binding - _binding[type]->analyticJacobian(t, secIdx, colPos, reinterpret_cast(y) + 2 * _nComp + _offsetParType[type], _nComp + _offsetParType[type], _jac.row(_nComp + _offsetParType[type]), tlmAlloc); + _binding[type]->analyticJacobian( + t, secIdx, colPos, reinterpret_cast(y) + 2 * _nComp + _offsetParType[type], + _nComp + _offsetParType[type], _jac.row(_nComp + _offsetParType[type]), tlmAlloc); } // Reaction @@ -1411,7 +1481,8 @@ int CSTRModel::residualImpl(double t, unsigned int secIdx, StateType const* cons std::fill_n(fluxLiquid, _nComp, 0.0); std::fill_n(fluxSolid, _strideBound[type], 0.0); - dynReaction->residualCombinedAdd(t, secIdx, colPos, c, c + _nComp + _offsetParType[type], fluxLiquid, fluxSolid, -1.0, subAlloc); + dynReaction->residualCombinedAdd(t, secIdx, colPos, c, c + _nComp + _offsetParType[type], fluxLiquid, + fluxSolid, -1.0, subAlloc); for (unsigned int comp = 0; comp < _nComp; ++comp) resC[comp] += v * fluxLiquid[comp]; @@ -1424,7 +1495,9 @@ int CSTRModel::residualImpl(double t, unsigned int secIdx, StateType const* cons for (unsigned int bnd = 0; bnd < _nBound[type * _nComp + comp]; ++bnd, ++idx) { // Add reaction term to mobile phase - resC[comp] += static_cast::type>(liquidFactor) * fluxSolid[idx]; + resC[comp] += + static_cast::type>(liquidFactor) * + fluxSolid[idx]; if (!qsReaction[idx]) { @@ -1446,34 +1519,45 @@ int CSTRModel::residualImpl(double t, unsigned int secIdx, StateType const* cons for (unsigned int bnd = 0; bnd < _nBound[type * _nComp + comp]; ++bnd, ++idx) sum += static_cast(fluxSolid[idx]); - _jac.native(comp, _nComp + _totalBound) += static_cast(fluxLiquid[comp]) + static_cast(invBeta) * static_cast(_parTypeVolFrac[type]) * sum; + _jac.native(comp, _nComp + _totalBound) += + static_cast(fluxLiquid[comp]) + + static_cast(invBeta) * static_cast(_parTypeVolFrac[type]) * sum; } // dRes / dC and dRes / dQ - BufferedArray fluxJacobianMem = subAlloc.array((_strideBound[type] + _nComp) * (_strideBound[type] + _nComp)); - linalg::DenseMatrixView jacFlux(static_cast(fluxJacobianMem), nullptr, _strideBound[type] + _nComp, _strideBound[type] + _nComp); - dynReaction->analyticJacobianCombinedAdd(t, secIdx, colPos, reinterpret_cast(c), reinterpret_cast(c + _nComp + _offsetParType[type]), - -1.0, jacFlux.row(0), jacFlux.row(_nComp), subAlloc); + BufferedArray fluxJacobianMem = + subAlloc.array((_strideBound[type] + _nComp) * (_strideBound[type] + _nComp)); + linalg::DenseMatrixView jacFlux(static_cast(fluxJacobianMem), nullptr, + _strideBound[type] + _nComp, _strideBound[type] + _nComp); + dynReaction->analyticJacobianCombinedAdd( + t, secIdx, colPos, reinterpret_cast(c), + reinterpret_cast(c + _nComp + _offsetParType[type]), -1.0, jacFlux.row(0), + jacFlux.row(_nComp), subAlloc); idx = 0; - const double liquidFactor = static_cast(v) * static_cast(invBeta) * static_cast(_parTypeVolFrac[type]); + const double liquidFactor = + static_cast(v) * static_cast(invBeta) * static_cast(_parTypeVolFrac[type]); for (unsigned int comp = 0; comp < _nComp; ++comp) { // Add bulk part of reaction to mobile phase Jacobian jacFlux.addSubmatrixTo(_jac, static_cast(v), comp, 0, 1, _nComp, comp, 0); - jacFlux.addSubmatrixTo(_jac, static_cast(v), comp, _nComp, 1, _strideBound[type], comp, _nComp + _offsetParType[type]); + jacFlux.addSubmatrixTo(_jac, static_cast(v), comp, _nComp, 1, _strideBound[type], comp, + _nComp + _offsetParType[type]); for (unsigned int bnd = 0; bnd < _nBound[type * _nComp + comp]; ++bnd, ++idx) { // Add Jacobian row to mobile phase jacFlux.addSubmatrixTo(_jac, liquidFactor, _nComp + idx, 0, 1, _nComp, comp, 0); - jacFlux.addSubmatrixTo(_jac, liquidFactor, _nComp + idx, _nComp, 1, _strideBound[type], comp, _nComp + _offsetParType[type]); + jacFlux.addSubmatrixTo(_jac, liquidFactor, _nComp + idx, _nComp, 1, _strideBound[type], comp, + _nComp + _offsetParType[type]); if (!qsReaction[idx]) { // Add Jacobian row to solid phase - jacFlux.addSubmatrixTo(_jac, 1.0, _nComp + idx, 0, 1, _nComp, _nComp + _offsetParType[type] + idx, 0); - jacFlux.addSubmatrixTo(_jac, 1.0, _nComp + idx, _nComp, 1, _strideBound[type], _nComp + _offsetParType[type] + idx, _nComp + _offsetParType[type]); + jacFlux.addSubmatrixTo(_jac, 1.0, _nComp + idx, 0, 1, _nComp, + _nComp + _offsetParType[type] + idx, 0); + jacFlux.addSubmatrixTo(_jac, 1.0, _nComp + idx, _nComp, 1, _strideBound[type], + _nComp + _offsetParType[type] + idx, _nComp + _offsetParType[type]); } } } @@ -1488,7 +1572,8 @@ int CSTRModel::residualImpl(double t, unsigned int secIdx, StateType const* cons } int CSTRModel::residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, - const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity) + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, + bool paramSensitivity) { if (updateJacobian) { @@ -1499,7 +1584,9 @@ int CSTRModel::residual(const SimulationTime& simTime, const ConstSimulationStat { if (paramSensitivity) { - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem.get()); + const int retCode = residualImpl( + simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, + threadLocalMem.get()); // Copy AD residuals to original residuals vector if (res) @@ -1508,7 +1595,8 @@ int CSTRModel::residual(const SimulationTime& simTime, const ConstSimulationStat return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem.get()); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem.get()); } else { @@ -1523,9 +1611,11 @@ int CSTRModel::residual(const SimulationTime& simTime, const ConstSimulationStat // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem.get()); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem.get()); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem.get()); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem.get()); // Copy AD residuals to original residuals vector if (res) @@ -1548,15 +1638,18 @@ int CSTRModel::residual(const SimulationTime& simTime, const ConstSimulationStat // Evaluate with AD enabled int retCode = 0; if (paramSensitivity) - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem.get()); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem.get()); else - retCode = residualImpl(simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem.get()); + retCode = residualImpl( + simTime.t, simTime.secIdx, adJac.adY, simState.vecStateYdot, adJac.adRes, threadLocalMem.get()); // Only do comparison if we have a residuals vector (which is not always the case) if (res) { // Evaluate with analytical Jacobian which is stored in the band matrices - retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem.get()); + retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem.get()); // Compare AD with anaytic Jacobian checkAnalyticJacobianAgainstAd(adJac.adRes, adJac.adDirOffset); @@ -1576,7 +1669,9 @@ int CSTRModel::residual(const SimulationTime& simTime, const ConstSimulationStat // @todo Check if this is necessary ad::resetAd(adJac.adRes, numDofs()); - const int retCode = residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adJac.adRes, threadLocalMem.get()); + const int retCode = + residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, adJac.adRes, threadLocalMem.get()); // Copy AD residuals to original residuals vector if (res) @@ -1585,24 +1680,30 @@ int CSTRModel::residual(const SimulationTime& simTime, const ConstSimulationStat return retCode; } else - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, res, threadLocalMem.get()); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, res, threadLocalMem.get()); } } -int CSTRModel::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int CSTRModel::residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) { return residual(simTime, simState, res, adJac, threadLocalMem, true, false); } -int CSTRModel::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem) +int CSTRModel::residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem) { // Evaluate residual for all parameters using AD in vector mode - return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, simState.vecStateYdot, adRes, threadLocalMem.get()); + return residualImpl(simTime.t, simTime.secIdx, simState.vecStateY, + simState.vecStateYdot, adRes, threadLocalMem.get()); } -int CSTRModel::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) +int CSTRModel::residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) { - // Evaluate residual for all parameters using AD in vector mode and at the same time update the + // Evaluate residual for all parameters using AD in vector mode and at the same time update the // Jacobian (in one AD run, if analytic Jacobians are disabled) return residual(simTime, simState, nullptr, adJac, threadLocalMem, true, true); } @@ -1618,7 +1719,8 @@ void CSTRModel::initializeSensitivityStates(const std::vector& vecSensY } void CSTRModel::consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem) { // TODO: Handle v = 0 @@ -1678,7 +1780,8 @@ void CSTRModel::consistentInitialSensitivity(const SimulationTime& simTime, cons linalg::fillVectorSubset(maskedMultiplier + _nComp, mask, 0.0); // Assemble right hand side - _jac.submatrixMultiplyVector(maskedMultiplier, _nComp, 0, _totalBound, _nComp + _totalBound + 1, static_cast(rhsUnmasked)); + _jac.submatrixMultiplyVector(maskedMultiplier, _nComp, 0, _totalBound, _nComp + _totalBound + 1, + static_cast(rhsUnmasked)); linalg::vectorSubsetAdd(static_cast(rhsUnmasked), mask, -1.0, 1.0, static_cast(rhs)); // Precondition @@ -1746,7 +1849,8 @@ void CSTRModel::consistentInitialSensitivity(const SimulationTime& simTime, cons } } -void CSTRModel::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret) +void CSTRModel::multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret) { const double flowIn = static_cast(_flowRateIn); double* const resTank = ret + _nComp; @@ -1767,7 +1871,8 @@ void CSTRModel::multiplyWithJacobian(const SimulationTime& simTime, const ConstS } } -void CSTRModel::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret) +void CSTRModel::multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* sDot, double* ret) { // Handle inlet DOFs (all algebraic) std::fill_n(ret, _nComp, 0.0); @@ -1782,7 +1887,8 @@ void CSTRModel::multiplyWithDerivativeJacobian(const SimulationTime& simTime, co double* const r = ret + _nComp; double const* const s = sDot + _nComp; - // Concentrations: \dot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{j,i,m}]) + V * (\dot{c}_i + 1 / beta * [sum_j sum_m d_j \dot{q}_{j,i,m}]) - c_{in,i} * F_in + c_i * F_out == 0 + // Concentrations: \dot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{j,i,m}]) + V * (\dot{c}_i + 1 / beta * [sum_j + // sum_m d_j \dot{q}_{j,i,m}]) - c_{in,i} * F_in + c_i * F_out == 0 for (unsigned int i = 0; i < _nComp; ++i) { r[i] = timeV * s[i]; @@ -1839,7 +1945,7 @@ void CSTRModel::multiplyWithDerivativeJacobian(const SimulationTime& simTime, co } int CSTRModel::linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) + const ConstSimulationState& simState) { const double flowIn = static_cast(_flowRateIn); @@ -1877,7 +1983,8 @@ void CSTRModel::addTimeDerivativeJacobian(double t, double alpha, const ConstSim // Assemble Jacobian: dRes / dyDot - // Concentrations: \dot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{j,i,m}]) + V * (\dot{c}_i + 1 / beta * [sum_j sum_m d_j \dot{q}_{j,i,m}]) - c_{in,i} * F_in + c_i * F_out == 0 + // Concentrations: \dot{V} * (c_i + 1 / beta * [sum_j sum_m d_j q_{j,i,m}]) + V * (\dot{c}_i + 1 / beta * [sum_j + // sum_m d_j \dot{q}_{j,i,m}]) - c_{in,i} * F_in + c_i * F_out == 0 for (unsigned int i = 0; i < _nComp; ++i) { mat.native(i, i) += timeV; @@ -1958,7 +2065,6 @@ void CSTRModel::checkAnalyticJacobianAgainstAd(active const* const adRes, unsign #endif - unsigned int CSTRModel::Exporter::numSolidPhaseDofs() const CADET_NOEXCEPT { return _totalBound; @@ -2026,13 +2132,12 @@ int CSTRModel::Exporter::writeOutlet(double* buffer) const return _nComp; } - - -void registerCSTRModel(std::unordered_map>& models) +void registerCSTRModel( + std::unordered_map>& models) { models[CSTRModel::identifier()] = [](UnitOpIdx uoId, IParameterProvider&) { return new CSTRModel(uoId); }; } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/StirredTankModel.hpp b/src/libcadet/model/StirredTankModel.hpp index 8d1b287a6..72c160c3d 100644 --- a/src/libcadet/model/StirredTankModel.hpp +++ b/src/libcadet/model/StirredTankModel.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,9 +11,9 @@ // ============================================================================= /** -* @file -* Defines the CSTR model as a unit operation. -*/ + * @file + * Defines the CSTR model as a unit operation. + */ #ifndef LIBCADET_CSTR_HPP_ #define LIBCADET_CSTR_HPP_ @@ -38,7 +38,8 @@ namespace model * @brief Continuous stirred tank (reactor) model * @details This is a simple CSTR model with variable volume using the ``well mixed assumption''. * @f[\begin{align} - \frac{\mathrm{d}}{\mathrm{d} t}\left( V \left[ c_i + \frac{1}{\beta} \sum_{j=1}^{N_{\text{bnd},i}} q_{i,j} \right] \right) &= F_{\text{in}} c_{\text{in},i} - F_{\text{out}} c_i \\ + \frac{\mathrm{d}}{\mathrm{d} t}\left( V \left[ c_i + \frac{1}{\beta} \sum_{j=1}^{N_{\text{bnd},i}} q_{i,j} \right] +\right) &= F_{\text{in}} c_{\text{in},i} - F_{\text{out}} c_i \\ a \frac{\partial q_{i,j}}{\partial t} &= f_{\text{iso},i,j}(c, q) \\ \frac{\partial V}{\partial t} &= F_{\text{in}} - F_{\text{out}} - F_{\text{filter}} \end{align} @f] @@ -47,7 +48,6 @@ namespace model class CSTRModel : public UnitOperationBase { public: - CSTRModel(UnitOpIdx unitOpIdx); virtual ~CSTRModel() CADET_NOEXCEPT; @@ -56,87 +56,170 @@ class CSTRModel : public UnitOperationBase virtual bool usesAD() const CADET_NOEXCEPT; virtual unsigned int requiredADdirs() const CADET_NOEXCEPT; - virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT { return _unitOpIdx; } - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _nComp; } + virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT + { + return _unitOpIdx; + } + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _nComp; + } virtual void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT; - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual bool canAccumulate() const CADET_NOEXCEPT { return true; } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual bool canAccumulate() const CADET_NOEXCEPT + { + return true; + } - static const char* identifier() { return "CSTR"; } - virtual const char* unitOperationName() const CADET_NOEXCEPT { return identifier(); } + static const char* identifier() + { + return "CSTR"; + } + virtual const char* unitOperationName() const CADET_NOEXCEPT + { + return identifier(); + } virtual bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper); virtual bool configure(IParameterProvider& paramProvider); - virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac); + virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac); virtual void useAnalyticJacobian(const bool analyticJac); virtual void reportSolution(ISolutionRecorder& recorder, double const* const solution) const; virtual void reportSolutionStructure(ISolutionRecorder& recorder) const; - virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem); - virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, bool paramSensitivity); - - virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem); + virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem, bool updateJacobian, + bool paramSensitivity); + + virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); + virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem); + virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem); virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState); + const ConstSimulationState& simState); virtual void prepareADvectors(const AdJacobianParams& adJac) const; virtual void applyInitialCondition(const SimulationState& simState) const; virtual void readInitialCondition(IParameterProvider& paramProvider); - virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); - + virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem); + virtual void initializeSensitivityStates(const std::vector& vecSensY) const; virtual void consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + + virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem); + virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem); - virtual void leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem); + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem); - virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) { } + virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) + { + } - virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret); - virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret); + virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret); + virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* sDot, double* ret); - virtual bool hasInlet() const CADET_NOEXCEPT { return true; } - virtual bool hasOutlet() const CADET_NOEXCEPT { return true; } + virtual bool hasInlet() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasOutlet() const CADET_NOEXCEPT + { + return true; + } - virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT { return _nComp; } - virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT { return 1; } - virtual unsigned int localInletComponentIndex(unsigned int port) const CADET_NOEXCEPT { return 0; } - virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT { return 1; } + virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT + { + return _nComp; + } + virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int localInletComponentIndex(unsigned int port) const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT + { + return 1; + } virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections); - virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut) { } + virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut) + { + } virtual unsigned int threadLocalMemorySize() const CADET_NOEXCEPT; #ifdef CADET_BENCHMARK_MODE - virtual std::vector benchmarkTimings() const { return std::vector(0); } - virtual char const* const* benchmarkDescriptions() const { return nullptr; } + virtual std::vector benchmarkTimings() const + { + return std::vector(0); + } + virtual char const* const* benchmarkDescriptions() const + { + return nullptr; + } #endif - inline const std::vector& flowRateFilter() const { return _flowRateFilter; } - inline std::vector& flowRateFilter() { return _flowRateFilter; } - inline void flowRateFilter(const std::vector& frf) { _flowRateFilter = frf; } - inline void setFlowRates(double in, double out) CADET_NOEXCEPT { _flowRateIn = in; _flowRateOut = out; } + inline const std::vector& flowRateFilter() const + { + return _flowRateFilter; + } + inline std::vector& flowRateFilter() + { + return _flowRateFilter; + } + inline void flowRateFilter(const std::vector& frf) + { + _flowRateFilter = frf; + } + inline void setFlowRates(double in, double out) CADET_NOEXCEPT + { + _flowRateIn = in; + _flowRateOut = out; + } protected: - template - int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, ResidualType* const res, LinearBufferAllocator threadLocalMem); + int residualImpl(double t, unsigned int secIdx, StateType const* const y, double const* const yDot, + ResidualType* const res, LinearBufferAllocator threadLocalMem); template void addTimeDerivativeJacobian(double t, double alpha, const ConstSimulationState& simState, MatrixType& mat); @@ -146,27 +229,29 @@ class CSTRModel : public UnitOperationBase void checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const; #endif - unsigned int _nComp; //!< Number of components - unsigned int _nParType; //!< Number of particle types - unsigned int* _nBound; //!< Array with number of bound states for each component in each particle type - unsigned int* _boundOffset; //!< Array with offset to the first bound state of each component in the solid phase for each particle type + unsigned int _nComp; //!< Number of components + unsigned int _nParType; //!< Number of particle types + unsigned int* _nBound; //!< Array with number of bound states for each component in each particle type + unsigned int* _boundOffset; //!< Array with offset to the first bound state of each component in the solid phase for + //!< each particle type unsigned int* _strideBound; //!< Total number of bound states per particle type unsigned int* _offsetParType; //!< Offset to bound states of a particle type in solid phase - unsigned int _totalBound; //!< Total number of all bound states in all particle types + unsigned int _totalBound; //!< Total number of all bound states in all particle types - active _porosity; //!< Porosity \f$ \varepsilon \f$ - active _flowRateIn; //!< Volumetric flow rate of incoming stream - active _flowRateOut; //!< Volumetric flow rate of drawn outgoing stream - active _curFlowRateFilter; //!< Current volumetric flow rate of liquid outtake stream for this section + active _porosity; //!< Porosity \f$ \varepsilon \f$ + active _flowRateIn; //!< Volumetric flow rate of incoming stream + active _flowRateOut; //!< Volumetric flow rate of drawn outgoing stream + active _curFlowRateFilter; //!< Current volumetric flow rate of liquid outtake stream for this section std::vector _flowRateFilter; //!< Volumetric flow rate of liquid outtake stream std::vector _parTypeVolFrac; //!< Volume fraction of each particle type - bool _analyticJac; //!< Flag that determines whether analytic or AD Jacobian is used - linalg::DenseMatrix _jac; //!< Jacobian + bool _analyticJac; //!< Flag that determines whether analytic or AD Jacobian is used + linalg::DenseMatrix _jac; //!< Jacobian linalg::DenseMatrix _jacFact; //!< Factorized Jacobian - bool _factorizeJac; //!< Flag that tracks whether the Jacobian needs to be factorized + bool _factorizeJac; //!< Flag that tracks whether the Jacobian needs to be factorized - std::vector _initConditions; //!< Initial conditions, ordering: Liquid phase concentration, solid phase concentration, volume + std::vector _initConditions; //!< Initial conditions, ordering: Liquid phase concentration, solid phase + //!< concentration, volume std::vector _initConditionsDot; //!< Initial conditions for time derivative IDynamicReactionModel* _dynReactionBulk; //!< Dynamic reactions in the bulk volume @@ -174,55 +259,139 @@ class CSTRModel : public UnitOperationBase class Exporter : public ISolutionExporter { public: + Exporter(unsigned int nComp, unsigned int nParType, unsigned int const* nBound, unsigned int const* strideBound, + unsigned int const* boundOffset, unsigned int totalBound, double const* data) + : _data(data), _nComp(nComp), _nParType(nParType), _nBound(nBound), _strideBound(strideBound), + _boundOffset(boundOffset), _totalBound(totalBound) + { + } + + virtual bool hasParticleFlux() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasSolidPhase() const CADET_NOEXCEPT + { + return _totalBound > 0; + } + virtual bool hasVolume() const CADET_NOEXCEPT + { + return true; + } + virtual bool isParticleLumped() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasPrimaryExtent() const CADET_NOEXCEPT + { + return false; + } - Exporter(unsigned int nComp, unsigned int nParType, unsigned int const* nBound, unsigned int const* strideBound, unsigned int const* boundOffset, unsigned int totalBound, double const* data) - : _data(data), _nComp(nComp), _nParType(nParType), _nBound(nBound), _strideBound(strideBound), _boundOffset(boundOffset), _totalBound(totalBound) { } - - virtual bool hasParticleFlux() const CADET_NOEXCEPT { return false; } - virtual bool hasParticleMobilePhase() const CADET_NOEXCEPT { return false; } - virtual bool hasSolidPhase() const CADET_NOEXCEPT { return _totalBound > 0; } - virtual bool hasVolume() const CADET_NOEXCEPT { return true; } - virtual bool isParticleLumped() const CADET_NOEXCEPT { return true; } - virtual bool hasPrimaryExtent() const CADET_NOEXCEPT { return false; } - - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _nComp; } - virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return 1; } - virtual unsigned int numParticleTypes() const CADET_NOEXCEPT { return _nParType; } - virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT { return 1; } - virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT { return _strideBound[parType]; } - virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT { return _nComp; } - virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return 0; } + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _nComp; + } + virtual unsigned int numPrimaryCoordinates() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numSecondaryCoordinates() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numParticleTypes() const CADET_NOEXCEPT + { + return _nParType; + } + virtual unsigned int numParticleShells(unsigned int parType) const CADET_NOEXCEPT + { + return 1; + } + virtual unsigned int numBoundStates(unsigned int parType) const CADET_NOEXCEPT + { + return _strideBound[parType]; + } + virtual unsigned int numMobilePhaseDofs() const CADET_NOEXCEPT + { + return _nComp; + } + virtual unsigned int numParticleMobilePhaseDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numParticleMobilePhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return 0; + } virtual unsigned int numSolidPhaseDofs() const CADET_NOEXCEPT; - virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT { return _strideBound[parType]; } - virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT { return 1; } + virtual unsigned int numSolidPhaseDofs(unsigned int parType) const CADET_NOEXCEPT + { + return _strideBound[parType]; + } + virtual unsigned int numParticleFluxDofs() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numVolumeDofs() const CADET_NOEXCEPT + { + return 1; + } virtual int writeMobilePhase(double* buffer) const; virtual int writeSolidPhase(double* buffer) const; virtual int writeSolidPhase(unsigned int parType, double* buffer) const; - virtual int writeParticleMobilePhase(double* buffer) const { return 0; } - virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const { return 0; } - virtual int writeParticleFlux(double* buffer) const { return 0; } - virtual int writeParticleFlux(unsigned int parType, double* buffer) const { return 0; } + virtual int writeParticleMobilePhase(double* buffer) const + { + return 0; + } + virtual int writeParticleMobilePhase(unsigned int parType, double* buffer) const + { + return 0; + } + virtual int writeParticleFlux(double* buffer) const + { + return 0; + } + virtual int writeParticleFlux(unsigned int parType, double* buffer) const + { + return 0; + } virtual int writeVolume(double* buffer) const; virtual int writeInlet(unsigned int port, double* buffer) const; virtual int writeInlet(double* buffer) const; virtual int writeOutlet(unsigned int port, double* buffer) const; virtual int writeOutlet(double* buffer) const; - virtual double const* solidPhase(unsigned int parType) const { return _data + 2 * _nComp; } + virtual double const* solidPhase(unsigned int parType) const + { + return _data + 2 * _nComp; + } virtual int writePrimaryCoordinates(double* coords) const { coords[0] = 0.0; return 1; } - virtual int writeSecondaryCoordinates(double* coords) const { return 0; } - virtual int writeParticleCoordinates(unsigned int parType, double* coords) const { return 0; } + virtual int writeSecondaryCoordinates(double* coords) const + { + return 0; + } + virtual int writeParticleCoordinates(unsigned int parType, double* coords) const + { + return 0; + } protected: double const* const _data; @@ -238,4 +407,4 @@ class CSTRModel : public UnitOperationBase } // namespace model } // namespace cadet -#endif // LIBCADET_CSTR_HPP_ +#endif // LIBCADET_CSTR_HPP_ diff --git a/src/libcadet/model/UnitOperation.hpp b/src/libcadet/model/UnitOperation.hpp index f9eefcb80..8c75977b0 100644 --- a/src/libcadet/model/UnitOperation.hpp +++ b/src/libcadet/model/UnitOperation.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines an unit operation model interface. */ @@ -38,12 +38,12 @@ struct ConstSimulationState; namespace util { - class ThreadLocalStorage; +class ThreadLocalStorage; } /** * @brief Defines an unit operation model interface - * @details + * @details */ class IUnitOperation : public IModel { @@ -80,16 +80,17 @@ class IUnitOperation : public IModel virtual unsigned int requiredADdirs() const CADET_NOEXCEPT = 0; /** - * @brief Configures the model discretization by extracting all structural parameters from the given @p paramProvider + * @brief Configures the model discretization by extracting all structural parameters from the given @p + * paramProvider * @details The scope of the cadet::IParameterProvider is left unchanged on return. - * + * * Only the structure / discretization (e.g., number of components, adsorption * model, bound states) is configured. The actual model parameters are configured - * in configure(). - * + * in configure(). + * * This function can only be called once. Once the discretization is set, it cannot * be changed. - * + * * @param [in] paramProvider Parameter provider * @param [in] helper Used to inject or create required objects * @return @c true if the configuration was successful, otherwise @c false @@ -97,19 +98,20 @@ class IUnitOperation : public IModel virtual bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper) = 0; /** - * @brief (Re-)configures the model by extracting all non-structural parameters (e.g., model parameters) from the given @p paramProvider + * @brief (Re-)configures the model by extracting all non-structural parameters (e.g., model parameters) from the + * given @p paramProvider * @details The scope of the cadet::IParameterProvider is left unchanged on return. - * + * * The structure of the model is left unchanged, that is, the number of degrees of * freedom stays the same. Only true (non-structural) model parameters are read and * changed. Parameters that concern discretization (e.g., number of cells), model * structure (e.g., number of components, binding model), and numerical solution * (e.g., tolerances in GMRES iterations) are left untouched. - * + * * This function may only be called if configureModelDiscretization() has been called * in the past. Contrary to configureModelDiscretization(), it can be called multiple * times. - * + * * @param [in] paramProvider Parameter provider * @return @c true if the configuration was successful, otherwise @c false */ @@ -142,7 +144,7 @@ class IUnitOperation : public IModel * @details This also sets values of parameters that are not marked as sensitive at the moment. * If the parameter is part of a fused sensitivity, then all fused parameters are set * to the same value by calling this function for each parameter. - * + * * Note that the AD directions are kept invariant (i.e., they are not resetted). * @param [in] id Parameter ID of the parameter to be manipulated * @param [in] value Value of the parameter @@ -165,19 +167,21 @@ class IUnitOperation : public IModel * @details This function is called after time integration of a section has finished and a new * section is about to be integrated. This allows the model to update internal state before * consistent initialization is performed. - * + * * This function is also called at the beginning of the time integration, which allows * the model to perform setup operations. - * + * * If AD is used by the model, the function has the opportunity to update the seed vectors. * The general initialization of the seed vectors is performed by prepareADvectors(). - * + * * @param [in] t Current time point * @param [in] secIdx Index of the new section that is about to be integrated * @param [in] simState Simulation state * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) */ - virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const ConstSimulationState& simState, const AdJacobianParams& adJac) = 0; + virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const ConstSimulationState& simState, + const AdJacobianParams& adJac) = 0; /** * @brief Applies initial conditions to the state vector and its time derivative @@ -188,28 +192,30 @@ class IUnitOperation : public IModel * the initial conditions set by this function will be corrected for consistency. * Note that the state vector and its time derivative are pre-initialized with zero by the * time integrator. - * - * @param [in,out] simState State of the simulation (state vector and its time derivatives) to be updated with initial values + * + * @param [in,out] simState State of the simulation (state vector and its time derivatives) to be updated with + * initial values */ virtual void applyInitialCondition(const SimulationState& simState) const = 0; /** * @brief Reads initial conditions for the state vector and its time derivative from the given parameter provider * @details The scope of the cadet::IParameterProvider is left unchanged on return. - * + * * @param [in] paramProvider Parameter provider */ virtual void readInitialCondition(IParameterProvider& paramProvider) = 0; /** * @brief Computes the residual - * + * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] simState State of the simulation (state vector and its time derivative) * @param [out] res Pointer to local residual vector * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ - virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, util::ThreadLocalStorage& threadLocalMem) = 0; + virtual int residual(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + util::ThreadLocalStorage& threadLocalMem) = 0; /** * @brief Computes the Jacobian (analytical or AD) @@ -220,25 +226,29 @@ class IUnitOperation : public IModel * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ - virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) = 0; + virtual int jacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, + const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) = 0; /** * @brief Computes the residual and updates the Jacobian - * + * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] simState State of the simulation (state vector and its time derivative) * @param [out] res Pointer to local residual vector * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ - virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double* const res, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) = 0; + virtual int residualWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double* const res, const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) = 0; /** * @brief Computes the solution of the linear system involving the system Jacobian - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the - * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) \f$, - * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p rhs. + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) + * x = b \f] has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the + * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, + * \dot{y}) \f$, may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned + * in @p rhs. * * @param [in] t Current time point * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) @@ -249,19 +259,19 @@ class IUnitOperation : public IModel * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const ConstSimulationState& simState) = 0; + const ConstSimulationState& simState) = 0; /** * @brief Prepares the AD system vectors by constructing seed vectors * @details Sets the seed vectors used in AD. Since the AD vector slice is fully managed by the model, * the seeds are unchanged during one time integration (except for possible changes in * notifyDiscontinuousSectionTransition()). This function is called at the beginning of - * every time integration and should initialize the AD seed vectors. - * + * every time integration and should initialize the AD seed vectors. + * * If those vectors do not change during one time integration, their initialization should * be performed in this function. The notifyDiscontinuousSectionTransition() function is * only used to update seed vectors during time integration. - * + * * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) */ virtual void prepareADvectors(const AdJacobianParams& adJac) const = 0; @@ -269,14 +279,14 @@ class IUnitOperation : public IModel /** * @brief Sets initial sensitivity system state * @details Given the DAE \f[ F(t, y, \dot{y}, p) = 0, \f] the corresponding (linear) forward sensitivity - * system reads \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial value of \f$ s_0 = \frac{\mathrm{d} y_0}{\mathrm{d}p} \f$ is set by this function. - * Note that consistent initialization is performed later, which in particular calculates a matching - * \f$ \dot{s}_0 = \frac{\mathrm{d} \dot{y}_0}{\mathrm{d}p}. \f$ - * + * system reads \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, + * y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial value of \f$ s_0 = + * \frac{\mathrm{d} y_0}{\mathrm{d}p} \f$ is set by this function. Note that consistent initialization is performed + * later, which in particular calculates a matching \f$ \dot{s}_0 = \frac{\mathrm{d} \dot{y}_0}{\mathrm{d}p}. \f$ + * * The sensitivity state vectors passed to this function are initialized to @c 0. They only have * to change in case of sensitivities with respect to initial conditions. - * + * * @param [out] vecSensY Sensitivity subsystem state vectors */ virtual void initializeSensitivityStates(const std::vector& vecSensY) const = 0; @@ -288,7 +298,7 @@ class IUnitOperation : public IModel * provide means to implement discontinuous behavior (e.g., pulse injection profiles, * switching of valves). After initialization, the simulator notifies all entities * such as models or data sources of its section times. - * + * * The vector of section times consists of strictly increasing time points * @f[ t_0 < t_1 < t_2 < \dots t_N @f] * which mark the beginning and end of a section. The @f$ i@f$-th section is given by @@ -296,10 +306,10 @@ class IUnitOperation : public IModel * If a transition from one section to the next is continuous, the @p secContinuity flag * for that transition is @c true. In this case, the time integrator will not stop at the * transition time point and reinitialize consistently (which will be done for discontinuous - * transitions). - * + * transitions). + * * @param [in] secTimes Vector with section time points (length is @p nSections + 1) - * @param [in] secContinuity Vector of flags that indicate a continuous (@c true) or discontinuous (@c false) + * @param [in] secContinuity Vector of flags that indicate a continuous (@c true) or discontinuous (@c false) * transition from the current section to the next one (length is @p nSections - 1). For instance, * the first element indicates whether the transition from section @c 0 to @c 1 is continuous. * @param [in] nSections Number of sections @@ -312,7 +322,7 @@ class IUnitOperation : public IModel * are error tolerances for each component in each phase (independent of the specific * discretization of the model). The short error spec is expanded into a full one which * contains an error tolerance for each (pure) DOF of the model. - * + * * @param [in] errorSpec Pointer to first element of an array containing the short error spec * @param [in] errorSpecSize Size of the short error tolerance spec * @param [out] expandOut Pointer to the first element of an array receiving the full error specification @@ -398,78 +408,92 @@ class IUnitOperation : public IModel * @param [in,out] adRes Pointer to local residual vector of AD datatypes for computing the sensitivity derivatives * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ - virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, active* const adRes, util::ThreadLocalStorage& threadLocalMem) = 0; + virtual int residualSensFwdAdOnly(const SimulationTime& simTime, const ConstSimulationState& simState, + active* const adRes, util::ThreadLocalStorage& threadLocalMem) = 0; /** * @brief Computes the residual of the forward sensitivity systems using the result of residualSensFwdAdOnly() * @details Assembles and evaluates the residuals of the sensitivity systems - * @f[ \frac{F}{\partial y} s + \frac{F}{\partial \dot{y}} \dot{s} + \frac{\partial F}{\partial p_i} = 0. @f] + * @f[ \frac{F}{\partial y} s + \frac{F}{\partial \dot{y}} \dot{s} + \frac{\partial F}{\partial p_i} = 0. + * @f] * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] simState State of the simulation (state vector and its time derivative) * @param [in] yS Pointers to local sensitivity state vectors * @param [in] ySdot Pointers to local sensitivity time derivative state vectors * @param [out] resS Pointers to local sensitivity residuals - * @param [in] adRes Pointer to local residual vector of AD datatypes with the sensitivity derivatives from residualSensFwdAdOnly() + * @param [in] adRes Pointer to local residual vector of AD datatypes with the sensitivity derivatives from + * residualSensFwdAdOnly() * @param [in] tmp1 Temporary storage in the size of local state vector @p y * @param [in] tmp2 Temporary storage in the size of local state vector of @p y * @param [in] tmp3 Temporary storage in the size of local state vector of @p y * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ - virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3) = 0; + virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3) = 0; /** - * @brief Evaluates the residuals with AD to compute the parameter sensitivities and at the same time updates the Jacobian + * @brief Evaluates the residuals with AD to compute the parameter sensitivities and at the same time updates the + * Jacobian * @details Evaluates @f$ \frac{\partial F}{\partial p} @f$, where @f$ p @f$ are the sensitive parameters - * and @f$ F @f$ is the residual function of this model. At the same time updates the Jacobian of the system. + * and @f$ F @f$ is the residual function of this model. At the same time updates the Jacobian of the + * system. * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] simState State of the simulation (state vector and its time derivative) * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ - virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, const AdJacobianParams& adJac, util::ThreadLocalStorage& threadLocalMem) = 0; + virtual int residualSensFwdWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + const AdJacobianParams& adJac, + util::ThreadLocalStorage& threadLocalMem) = 0; /** * @brief Computes consistent initial values (state variables without their time derivatives) * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have * to be consistent. This functions updates the initial state \f$ y_0 \f$ and, therefore, provides the * first step in consistent initialization. - * + * * This function is to be used with consistentInitialTimeDerivative(). Do not mix normal and lean * consistent initialization! - * + * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in,out] vecStateY State vector with initial values that are to be updated for consistency * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) * @param [in] errorTol Error tolerance for algebraic equations */ - virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) = 0; + virtual void consistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) = 0; /** * @brief Computes consistent initial time derivatives * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have * to be consistent. This functions overwrites the initial time derivatives \f$ \dot{y}_0 \f$ such that * the residual is zero. Thus, this function provides the final step in consistent initialization. - * + * * This function is to be used with consistentInitialState(). Do not mix normal and lean * consistent initialization! - * + * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] vecStateY Consistently initialized state vector - * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent state time derivatives. + * @param [in,out] vecStateYdot On entry, residual without taking time derivatives into account. On exit, consistent + * state time derivatives. */ - virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, util::ThreadLocalStorage& threadLocalMem) = 0; + virtual void consistentInitialTimeDerivative(const SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, + util::ThreadLocalStorage& threadLocalMem) = 0; /** - * @brief Computes approximately / partially consistent initial values (state variables without their time derivatives) + * @brief Computes approximately / partially consistent initial values (state variables without their time + * derivatives) * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have * to be consistent. This functions updates the initial state \f$ y_0 \f$ and, therefore, provides the * first step in consistent initialization. - * + * * This function is possibly faster than consistentInitialState(), but updates only a part of the * state vector. Hence, the result is not guaranteed to be consistent. - * + * * This function is to be used with leanConsistentInitialTimeDerivative(). Do not mix normal and lean * consistent initialization! * @@ -478,35 +502,42 @@ class IUnitOperation : public IModel * @param [in,out] adJac Jacobian information for AD (AD vectors for residual and state, direction offset) * @param [in] errorTol Error tolerance for algebraic equations */ - virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, const AdJacobianParams& adJac, double errorTol, util::ThreadLocalStorage& threadLocalMem) = 0; + virtual void leanConsistentInitialState(const SimulationTime& simTime, double* const vecStateY, + const AdJacobianParams& adJac, double errorTol, + util::ThreadLocalStorage& threadLocalMem) = 0; /** * @brief Computes approximately / partially consistent initial time derivatives * @details Given the DAE \f[ F(t, y, \dot{y}) = 0, \f] the initial values \f$ y_0 \f$ and \f$ \dot{y}_0 \f$ have * to be consistent. This functions overwrites the initial time derivatives \f$ \dot{y}_0 \f$ such that * the residual is zero. Thus, this function provides the final step in consistent initialization. - * + * * This function is possibly faster than consistentInitialTimeDerivative(), but updates only a part of the * time derivative vector. Hence, the result is not guaranteed to be consistent. - * + * * This function is to be used with leanConsistentInitialState(). Do not mix normal and lean * consistent initialization! - * + * * @param [in] t Current time point * @param [in] vecStateY (Lean) consistent initial state - * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state time derivatives. - * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during execution of the function. + * @param [in,out] vecStateYdot On entry, inconsistent state time derivatives. On exit, partially consistent state + * time derivatives. + * @param [in] res On entry, residual without taking time derivatives into account. The data is overwritten during + * execution of the function. */ - virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, util::ThreadLocalStorage& threadLocalMem) = 0; + virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + util::ThreadLocalStorage& threadLocalMem) = 0; /** * @brief Computes consistent initial conditions for all sensitivity subsystems * @details Given the DAE \f[ F(t, y, \dot{y}, p) = 0, \f] the corresponding (linear) forward sensitivity - * system reads \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of \f$ s_0 = \frac{\mathrm{d} y_0}{\mathrm{d}p} \f$ and \f$ \dot{s}_0 = \frac{\mathrm{d} \dot{y}_0}{\mathrm{d}p} \f$ - * have to be consistent, that means, they have to satisfy the sensitivity equation. This function computes the correct \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ - * given \f$ y_0 \f$ and \f$ s_0 \f$. - * + * system reads \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, + * y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of \f$ s_0 = + * \frac{\mathrm{d} y_0}{\mathrm{d}p} \f$ and \f$ \dot{s}_0 = \frac{\mathrm{d} \dot{y}_0}{\mathrm{d}p} \f$ have to + * be consistent, that means, they have to satisfy the sensitivity equation. This function computes the correct \f$ + * s_0 \f$ and \f$ \dot{s}_0 \f$ given \f$ y_0 \f$ and \f$ s_0 \f$. + * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] simState State of the simulation (state vector and its time derivative) * @param [in,out] vecSensY Sensitivity subsystem state vectors @@ -514,19 +545,21 @@ class IUnitOperation : public IModel * @param [in] adRes Pointer to local residual vector of AD datatypes with parameter sensitivities */ virtual void consistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) = 0; + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, util::ThreadLocalStorage& threadLocalMem) = 0; /** * @brief Computes approximately / partially consistent initial conditions for all sensitivity subsystems * @details Given the DAE \f[ F(t, y, \dot{y}, p) = 0, \f] the corresponding (linear) forward sensitivity - * system reads \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] - * The initial values of \f$ s_0 = \frac{\mathrm{d} y_0}{\mathrm{d}p} \f$ and \f$ \dot{s}_0 = \frac{\mathrm{d} \dot{y}_0}{\mathrm{d}p} \f$ - * have to be consistent, that means, they have to satisfy the sensitivity equation. This function computes the correct \f$ s_0 \f$ and \f$ \dot{s}_0 \f$ - * given \f$ y_0 \f$ and \f$ s_0 \f$. - * + * system reads \f[ \frac{\partial F}{\partial y}(t, y, \dot{y}) s + \frac{\partial F}{\partial \dot{y}}(t, + * y, \dot{y}) \dot{s} + \frac{\partial F}{\partial p}(t, y, \dot{y}) = 0. \f] The initial values of \f$ s_0 = + * \frac{\mathrm{d} y_0}{\mathrm{d}p} \f$ and \f$ \dot{s}_0 = \frac{\mathrm{d} \dot{y}_0}{\mathrm{d}p} \f$ have to + * be consistent, that means, they have to satisfy the sensitivity equation. This function computes the correct \f$ + * s_0 \f$ and \f$ \dot{s}_0 \f$ given \f$ y_0 \f$ and \f$ s_0 \f$. + * * This function is possibly faster than consistentInitialSensitivity(), but updates only a part of the - * vectors. Hence, the result is not guaranteed to be consistent. - * + * vectors. Hence, the result is not guaranteed to be consistent. + * * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] simState State of the simulation (state vector and its time derivative) * @param [in,out] vecSensY Sensitivity subsystem state vectors @@ -534,7 +567,9 @@ class IUnitOperation : public IModel * @param [in] adRes Pointer to local residual vector of AD datatypes with parameter sensitivities */ virtual void leanConsistentInitialSensitivity(const SimulationTime& simTime, const ConstSimulationState& simState, - std::vector& vecSensY, std::vector& vecSensYdot, active const* const adRes, util::ThreadLocalStorage& threadLocalMem) = 0; + std::vector& vecSensY, std::vector& vecSensYdot, + active const* const adRes, + util::ThreadLocalStorage& threadLocalMem) = 0; /** * @brief Sets external functions for this binding model @@ -553,14 +588,15 @@ class IUnitOperation : public IModel virtual void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT = 0; /** - * @brief Returns whether this unit operation supports non-matching volumetric inlet and outlet flow rates - * @details If inlet and outlet flow rates do not match, mass is accumulated or lost during time integration. - * @return @c true if flow rates are allowed to differ, otherwise @c false - */ + * @brief Returns whether this unit operation supports non-matching volumetric inlet and outlet flow rates + * @details If inlet and outlet flow rates do not match, mass is accumulated or lost during time integration. + * @return @c true if flow rates are allowed to differ, otherwise @c false + */ virtual bool canAccumulate() const CADET_NOEXCEPT = 0; /** - * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, \dot{y}\right) @f$) + * @brief Multiplies the given vector with the system Jacobian (i.e., @f$ \frac{\partial F}{\partial y}\left(t, y, + * \dot{y}\right) @f$) * @details Actually, the operation @f$ z = \alpha \frac{\partial F}{\partial y} x + \beta z @f$ is performed. * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] simState State of the simulation (state vector and its time derivative) @@ -569,10 +605,12 @@ class IUnitOperation : public IModel * @param [in] beta Factor @f$ \beta @f$ in front of @f$ z @f$ * @param [in,out] ret Vector @f$ z @f$ which stores the result of the operation */ - virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret) = 0; + virtual void multiplyWithJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret) = 0; /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) + * @f$ with a given vector * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) @@ -580,7 +618,8 @@ class IUnitOperation : public IModel * @param [in] sDot Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ * @param [out] ret Vector @f$ z @f$ which stores the result of the operation */ - virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, double const* sDot, double* ret) = 0; + virtual void multiplyWithDerivativeJacobian(const SimulationTime& simTime, const ConstSimulationState& simState, + double const* sDot, double* ret) = 0; /** * @brief Returns the amount of thread local memory required by the unit operation @@ -592,4 +631,4 @@ class IUnitOperation : public IModel } // namespace cadet -#endif // LIBCADET_IUNITOPERATION_HPP_ +#endif // LIBCADET_IUNITOPERATION_HPP_ diff --git a/src/libcadet/model/UnitOperationBase.cpp b/src/libcadet/model/UnitOperationBase.cpp index 3bb84dec0..aab2c9bb7 100644 --- a/src/libcadet/model/UnitOperationBase.cpp +++ b/src/libcadet/model/UnitOperationBase.cpp @@ -30,8 +30,9 @@ namespace cadet namespace model { -UnitOperationBase::UnitOperationBase(UnitOpIdx unitOpIdx) : _unitOpIdx(unitOpIdx), _binding(0, nullptr), _singleBinding(false), - _dynReaction(0, nullptr), _singleDynReaction(false), _nonlinearSolver(nullptr) +UnitOperationBase::UnitOperationBase(UnitOpIdx unitOpIdx) + : _unitOpIdx(unitOpIdx), _binding(0, nullptr), _singleBinding(false), _dynReaction(0, nullptr), + _singleDynReaction(false), _nonlinearSolver(nullptr) { } @@ -95,7 +96,9 @@ std::unordered_map UnitOperationBase::getAllParameterValues { std::unordered_map data; std::transform(_parameters.begin(), _parameters.end(), std::inserter(data, data.end()), - [](const std::pair& p) { return std::make_pair(p.first, static_cast(*p.second)); }); + [](const std::pair& p) { + return std::make_pair(p.first, static_cast(*p.second)); + }); model::getAllParameterValues(data, _binding, _singleBinding); model::getAllParameterValues(data, _dynReaction, _singleDynReaction); @@ -129,7 +132,7 @@ bool UnitOperationBase::hasParameter(const ParameterId& pId) const return true; if (model::hasParameter(pId, _dynReaction, _singleDynReaction)) return true; - + return false; } @@ -217,12 +220,14 @@ bool UnitOperationBase::setSensitiveParameter(const ParameterId& pId, unsigned i if (model::setSensitiveParameter(pId, adDirection, adValue, _sensParams, _binding, _singleBinding)) { - LOG(Debug) << "Found parameter " << pId << " in AdsorptionModel: Dir " << adDirection << " is set to " << adValue; + LOG(Debug) << "Found parameter " << pId << " in AdsorptionModel: Dir " << adDirection << " is set to " + << adValue; return true; } if (model::setSensitiveParameter(pId, adDirection, adValue, _sensParams, _dynReaction, _singleDynReaction)) { - LOG(Debug) << "Found parameter " << pId << " in DynamicReactionModel: Dir " << adDirection << " is set to " << adValue; + LOG(Debug) << "Found parameter " << pId << " in DynamicReactionModel: Dir " << adDirection << " is set to " + << adValue; return true; } @@ -269,8 +274,10 @@ void UnitOperationBase::configureNonlinearSolver() } int UnitOperationBase::residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3) + const std::vector& yS, + const std::vector& ySdot, const std::vector& resS, + active const* adRes, double* const tmp1, double* const tmp2, + double* const tmp3) { for (std::size_t param = 0; param < yS.size(); ++param) { @@ -293,6 +300,6 @@ int UnitOperationBase::residualSensFwdCombine(const SimulationTime& simTime, con return 0; } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/UnitOperationBase.hpp b/src/libcadet/model/UnitOperationBase.hpp index eee528204..26dbe79e4 100644 --- a/src/libcadet/model/UnitOperationBase.hpp +++ b/src/libcadet/model/UnitOperationBase.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides a base class for unit operation models. */ @@ -43,11 +43,13 @@ class IDynamicReactionModel; class UnitOperationBase : public IUnitOperation { public: - UnitOperationBase(UnitOpIdx unitOpIdx); virtual ~UnitOperationBase() CADET_NOEXCEPT; - virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT { return _unitOpIdx; } + virtual UnitOpIdx unitOperationId() const CADET_NOEXCEPT + { + return _unitOpIdx; + } virtual std::unordered_map getAllParameterValues() const; virtual double getParameterDouble(const ParameterId& pId) const; @@ -64,11 +66,11 @@ class UnitOperationBase : public IUnitOperation virtual unsigned int numSensParams() const; virtual int residualSensFwdCombine(const SimulationTime& simTime, const ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3); + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3); protected: - void clearBindingModels() CADET_NOEXCEPT; void clearDynamicReactionModels() CADET_NOEXCEPT; void configureNonlinearSolver(IParameterProvider& paramProvider); @@ -76,14 +78,14 @@ class UnitOperationBase : public IUnitOperation unsigned int maxBindingAdDirs() const CADET_NOEXCEPT; - UnitOpIdx _unitOpIdx; //!< Unit operation index - std::vector _binding; //!< Binding model - bool _singleBinding; //!< Determines whether only a single binding model is present + UnitOpIdx _unitOpIdx; //!< Unit operation index + std::vector _binding; //!< Binding model + bool _singleBinding; //!< Determines whether only a single binding model is present std::vector _dynReaction; //!< Dynamic reaction model bool _singleDynReaction; //!< Determines whether only a single dynamic reaction model is present typedef std::unordered_map paramMap_t; - paramMap_t _parameters; //!< Provides access to all parameters + paramMap_t _parameters; //!< Provides access to all parameters std::unordered_set _sensParams; //!< Holds all parameters with activated AD directions nonlin::Solver* _nonlinearSolver; //!< Solver for nonlinear equations (consistent initialization) @@ -92,4 +94,4 @@ class UnitOperationBase : public IUnitOperation } // namespace model } // namespace cadet -#endif // LIBCADET_UNITOPERATIONBASE_HPP_ +#endif // LIBCADET_UNITOPERATIONBASE_HPP_ diff --git a/src/libcadet/model/binding/AntiLangmuirBinding.cpp b/src/libcadet/model/binding/AntiLangmuirBinding.cpp index abd484987..205d5e8f9 100644 --- a/src/libcadet/model/binding/AntiLangmuirBinding.cpp +++ b/src/libcadet/model/binding/AntiLangmuirBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -50,62 +50,79 @@ namespace cadet namespace model { -inline const char* AntiLangmuirParamHandler::identifier() CADET_NOEXCEPT { return "MULTI_COMPONENT_ANTILANGMUIR"; } +inline const char* AntiLangmuirParamHandler::identifier() CADET_NOEXCEPT +{ + return "MULTI_COMPONENT_ANTILANGMUIR"; +} inline bool AntiLangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() != _antiLangmuir.size()) || (_kA.size() < nComp)) - throw InvalidParameterException("MCAL_KA, MCAL_KD, MCAL_QMAX, and MCAL_ANTILANGMUIR have to have the same size"); + if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() != _antiLangmuir.size()) || + (_kA.size() < nComp)) + throw InvalidParameterException( + "MCAL_KA, MCAL_KD, MCAL_QMAX, and MCAL_ANTILANGMUIR have to have the same size"); return true; } -inline const char* ExtAntiLangmuirParamHandler::identifier() CADET_NOEXCEPT { return "EXT_MULTI_COMPONENT_ANTILANGMUIR"; } +inline const char* ExtAntiLangmuirParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_MULTI_COMPONENT_ANTILANGMUIR"; +} inline bool ExtAntiLangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() != _antiLangmuir.size()) || (_kA.size() < nComp)) - throw InvalidParameterException("MCAL_KA, MCAL_KD, MCAL_QMAX, and MCAL_ANTILANGMUIR have to have the same size"); + if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() != _antiLangmuir.size()) || + (_kA.size() < nComp)) + throw InvalidParameterException( + "MCAL_KA, MCAL_KD, MCAL_QMAX, and MCAL_ANTILANGMUIR have to have the same size"); return true; } - /** * @brief Defines the multi component Anti-Langmuir binding model - * @details Implements the Anti-Langmuir adsorption model: \f[ \begin{align} - * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} q_{\text{max},i} \left( 1 - \sum_j p_j \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} q_i, - * \end{align} \f] - * where the factor @f$ p_j \in \{ -1, 1\} @f$ determines the behavior (@c -1 results in Anti-Langmuir, @c +1 in standard Langmuir). - * Multiple bound states are not supported. - * Components without bound state (i.e., non-binding components) are supported. + * @details Implements the Anti-Langmuir adsorption model: \f[ \begin{align} + * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} q_{\text{max},i} \left( 1 - \sum_j p_j + * \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} q_i, \end{align} \f] where the factor @f$ p_j \in \{ -1, 1\} @f$ + * determines the behavior (@c -1 results in Anti-Langmuir, @c +1 in standard Langmuir). Multiple bound states are not + * supported. Components without bound state (i.e., non-binding components) are supported. * @tparam ParamHandler_t Type that can add support for external function dependence */ -template -class AntiLangmuirBindingBase : public ParamHandlerBindingModelBase +template class AntiLangmuirBindingBase : public ParamHandlerBindingModelBase { public: + AntiLangmuirBindingBase() + { + } + virtual ~AntiLangmuirBindingBase() CADET_NOEXCEPT + { + } - AntiLangmuirBindingBase() { } - virtual ~AntiLangmuirBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } CADET_BINDINGMODELBASE_BOILERPLATE - + protected: using ParamHandlerBindingModelBase::_paramHandler; using ParamHandlerBindingModelBase::_reactionQuasistationarity; using ParamHandlerBindingModelBase::_nComp; using ParamHandlerBindingModelBase::_nBoundStates; - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } template int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Protein flux: -k_{a,i} * c_{p,i} * (1 - \sum q_i / q_{max,i}) + k_{d,i} * q_i ResidualType qSum = 1.0; @@ -130,7 +147,8 @@ class AntiLangmuirBindingBase : public ParamHandlerBindingModelBase(p->kD[i]) * y[bndIdx] - static_cast(p->kA[i]) * yCp[i] * static_cast(p->qMax[i]) * qSum; + res[bndIdx] = static_cast(p->kD[i]) * y[bndIdx] - + static_cast(p->kA[i]) * yCp[i] * static_cast(p->qMax[i]) * qSum; // Next bound component ++bndIdx; @@ -140,9 +158,11 @@ class AntiLangmuirBindingBase : public ParamHandlerBindingModelBase - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Protein flux: -k_{a,i} * c_{p,i} * (1 - \sum q_i / q_{max,i}) + k_{d,i} * q_i double qSum = 1.0; @@ -183,8 +203,10 @@ class AntiLangmuirBindingBase : public ParamHandlerBindingModelBase(p->antiLangmuir[j]) * static_cast(p->qMax[i]) / static_cast(p->qMax[j]); - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. + jac[bndIdx2 - bndIdx] = ka * yCp[i] * static_cast(p->antiLangmuir[j]) * + static_cast(p->qMax[i]) / static_cast(p->qMax[j]); + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] + // corresponds to q_j. ++bndIdx2; } @@ -197,7 +219,6 @@ class AntiLangmuirBindingBase : public ParamHandlerBindingModelBase AntiLangmuirBinding; @@ -205,13 +226,13 @@ typedef AntiLangmuirBindingBase ExternalAntiLangmui namespace binding { - void registerAntiLangmuirModel(std::unordered_map>& bindings) - { - bindings[AntiLangmuirBinding::identifier()] = []() { return new AntiLangmuirBinding(); }; - bindings[ExternalAntiLangmuirBinding::identifier()] = []() { return new ExternalAntiLangmuirBinding(); }; - } -} // namespace binding +void registerAntiLangmuirModel(std::unordered_map>& bindings) +{ + bindings[AntiLangmuirBinding::identifier()] = []() { return new AntiLangmuirBinding(); }; + bindings[ExternalAntiLangmuirBinding::identifier()] = []() { return new ExternalAntiLangmuirBinding(); }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/BiLangmuirBinding.cpp b/src/libcadet/model/binding/BiLangmuirBinding.cpp index 0644bff7a..1d458008c 100644 --- a/src/libcadet/model/binding/BiLangmuirBinding.cpp +++ b/src/libcadet/model/binding/BiLangmuirBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -49,7 +49,10 @@ namespace cadet namespace model { -inline const char* BiLangmuirParamHandler::identifier() CADET_NOEXCEPT { return "MULTI_COMPONENT_BILANGMUIR"; } +inline const char* BiLangmuirParamHandler::identifier() CADET_NOEXCEPT +{ + return "MULTI_COMPONENT_BILANGMUIR"; +} inline bool BiLangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { @@ -59,7 +62,10 @@ inline bool BiLangmuirParamHandler::validateConfig(unsigned int nComp, unsigned return true; } -inline const char* ExtBiLangmuirParamHandler::identifier() CADET_NOEXCEPT { return "EXT_MULTI_COMPONENT_BILANGMUIR"; } +inline const char* ExtBiLangmuirParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_MULTI_COMPONENT_BILANGMUIR"; +} inline bool ExtBiLangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { @@ -69,36 +75,41 @@ inline bool ExtBiLangmuirParamHandler::validateConfig(unsigned int nComp, unsign return true; } - /** * @brief Defines the multi component Bi-Langmuir binding model - * @details Implements the Bi-Langmuir adsorption model: \f[ \begin{align} - * \frac{\mathrm{d}q_i^A}{\mathrm{d}t} &= k_{a,i}^A c_{p,i} q_{\text{max},i}^A \left( 1 - \sum_j \frac{q_j^A}{q_{\text{max},j}^A} \right) - k_{d,i}^A q_i^A \\ - * \frac{\mathrm{d}q_i^B}{\mathrm{d}t} &= k_{a,i}^B c_{p,i} q_{\text{max},i}^B \left( 1 - \sum_j \frac{q_j^B}{q_{\text{max},j}^B} \right) - k_{d,i}^B q_i^B \\ - * \vodts & \vdots - * \end{align} \f] - * Here, several different types of binding sites @f$ q^A @f$, @f$ q^B @f$, etc. are considered. A molecule can either bind to - * site A or B (or C, etc.). A direct exchange between the different binding sites does not occur. - * While components without bound state (i.e., non-binding components) are supported, all other components must have - * the same number of bound states (i.e., binding sites). - * - * Internal state vector order is component-major. The state vector is composed of all components and within each component - * all bound states are listed. - * + * @details Implements the Bi-Langmuir adsorption model: \f[ \begin{align} + * \frac{\mathrm{d}q_i^A}{\mathrm{d}t} &= k_{a,i}^A c_{p,i} q_{\text{max},i}^A \left( 1 - \sum_j + * \frac{q_j^A}{q_{\text{max},j}^A} \right) - k_{d,i}^A q_i^A \\ + * \frac{\mathrm{d}q_i^B}{\mathrm{d}t} &= k_{a,i}^B c_{p,i} q_{\text{max},i}^B \left( 1 - \sum_j + * \frac{q_j^B}{q_{\text{max},j}^B} \right) - k_{d,i}^B q_i^B \\ \vodts & \vdots \end{align} \f] Here, several different + * types of binding sites @f$ q^A @f$, @f$ q^B @f$, etc. are considered. A molecule can either bind to site A or B (or + * C, etc.). A direct exchange between the different binding sites does not occur. While components without bound state + * (i.e., non-binding components) are supported, all other components must have the same number of bound states (i.e., + * binding sites). + * + * Internal state vector order is component-major. The state vector is composed of all components and within + * each component all bound states are listed. + * * See @cite Guiochon2006. * @tparam ParamHandler_t Type that can add support for external function dependence */ -template -class BiLangmuirBindingBase : public ParamHandlerBindingModelBase +template class BiLangmuirBindingBase : public ParamHandlerBindingModelBase { public: + BiLangmuirBindingBase() + { + } + virtual ~BiLangmuirBindingBase() CADET_NOEXCEPT + { + } - BiLangmuirBindingBase() { } - virtual ~BiLangmuirBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { const bool res = BindingModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); @@ -112,7 +123,8 @@ class BiLangmuirBindingBase : public ParamHandlerBindingModelBasehasQuasiStationaryReactions()) return; @@ -159,7 +182,7 @@ class BiLangmuirBindingBase : public ParamHandlerBindingModelBasekA[site]; -// active const* const localKd = p->kD[site]; + // active const* const localKd = p->kD[site]; active const* const localQmax = p->qMax[site]; active const* const localKaT = dpDt->kA[site]; active const* const localKdT = dpDt->kD[site]; @@ -194,10 +217,11 @@ class BiLangmuirBindingBase : public ParamHandlerBindingModelBase(localKdT[i]) * y[bndIdx * nSites] - - yCp[i] * (static_cast(localKaT[i]) * static_cast(localQmax[i]) * qSum - + static_cast(localKa[i]) * static_cast(localQmaxT[i]) * qSum - + static_cast(localKa[i]) * static_cast(localQmax[i]) * qSumT); + dResDt[bndIdx * nSites] = + static_cast(localKdT[i]) * y[bndIdx * nSites] - + yCp[i] * (static_cast(localKaT[i]) * static_cast(localQmax[i]) * qSum + + static_cast(localKa[i]) * static_cast(localQmaxT[i]) * qSum + + static_cast(localKa[i]) * static_cast(localQmax[i]) * qSumT); // Next bound component ++bndIdx; @@ -215,13 +239,17 @@ class BiLangmuirBindingBase : public ParamHandlerBindingModelBase int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Protein flux: -k_{a,i}^j * c_{p,i} * (1 - \sum q_i^j / q_{max,i}^j) + k_{d,i}^j * q_i^j @@ -271,7 +299,9 @@ class BiLangmuirBindingBase : public ParamHandlerBindingModelBase(localKd[i]) * y[bndIdx * nSites] - static_cast(localKa[i]) * yCp[i] * static_cast(localQmax[i]) * qSum; + res[bndIdx * nSites] = + static_cast(localKd[i]) * y[bndIdx * nSites] - + static_cast(localKa[i]) * yCp[i] * static_cast(localQmax[i]) * qSum; // Next bound component ++bndIdx; @@ -282,9 +312,11 @@ class BiLangmuirBindingBase : public ParamHandlerBindingModelBase - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Protein flux: -k_{a,i}^j * c_{p,i} * (1 - \sum q_i^j / q_{max,i}^j) + k_{d,i}^j * q_i^j @@ -342,7 +374,8 @@ class BiLangmuirBindingBase : public ParamHandlerBindingModelBase(localQmax[i]) / static_cast(localQmax[j]); + jac[(bndIdx2 - bndIdx) * nSites] = + ka * yCp[i] * static_cast(localQmax[i]) / static_cast(localQmax[j]); // Getting to q_j: -bndIdx * nSites takes us to q_{0,site}, another +bndIdx2 to q_{j,site}. // This means jac[(bndIdx2 - bndIdx) * nSites] corresponds to q_{j,site}. @@ -369,13 +402,13 @@ typedef BiLangmuirBindingBase ExternalBiLangmuirBindi namespace binding { - void registerBiLangmuirModel(std::unordered_map>& bindings) - { - bindings[BiLangmuirBinding::identifier()] = []() { return new BiLangmuirBinding(); }; - bindings[ExternalBiLangmuirBinding::identifier()] = []() { return new ExternalBiLangmuirBinding(); }; - } -} // namespace binding +void registerBiLangmuirModel(std::unordered_map>& bindings) +{ + bindings[BiLangmuirBinding::identifier()] = []() { return new BiLangmuirBinding(); }; + bindings[ExternalBiLangmuirBinding::identifier()] = []() { return new ExternalBiLangmuirBinding(); }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/BiLangmuirLDFBinding.cpp b/src/libcadet/model/binding/BiLangmuirLDFBinding.cpp index 7794695a3..25be3211b 100644 --- a/src/libcadet/model/binding/BiLangmuirLDFBinding.cpp +++ b/src/libcadet/model/binding/BiLangmuirLDFBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -46,340 +46,372 @@ namespace cadet { - namespace model +namespace model +{ + +inline const char* BiLangmuirLDFParamHandler::identifier() CADET_NOEXCEPT +{ + return "MULTI_COMPONENT_BILANGMUIR_LDF"; +} + +inline bool BiLangmuirLDFParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) +{ + if ((_keq.size() != _kkin.size()) || (_keq.size() != _qMax.size()) || (_keq.size() < nComp)) + throw InvalidParameterException("MCBLLDF_KEQ, MCBLLDF_KKIN, and MCBLLDF_QMAX have to have the same size"); + + return true; +} + +inline const char* ExtBiLangmuirLDFParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_MULTI_COMPONENT_BILANGMUIR_LDF"; +} + +inline bool ExtBiLangmuirLDFParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) +{ + if ((_keq.size() != _kkin.size()) || (_keq.size() != _qMax.size()) || (_keq.size() < nComp)) + throw InvalidParameterException( + "EXT_MCBLLDF_KEQ, EXT_MCBLLDF_KKIN, and EXT_MCBLLDF_QMAX have to have the same size"); + + return true; +} + +/** + * @brief Defines the multi component Bi-Langmuir binding model + * @details Implements the Bi-Langmuir adsorption model: \f[ \begin{align} + * \frac{\mathrm{d}q_i^A}{\mathrm{d}t} &= k_{a,i}^A c_{p,i} q_{\text{max},i}^A \left( 1 - \sum_j + * \frac{q_j^A}{q_{\text{max},j}^A} \right) - k_{d,i}^A q_i^A \\ + * \frac{\mathrm{d}q_i^B}{\mathrm{d}t} &= k_{a,i}^B c_{p,i} q_{\text{max},i}^B \left( 1 - \sum_j + * \frac{q_j^B}{q_{\text{max},j}^B} \right) - k_{d,i}^B q_i^B \\ \vodts & \vdots \end{align} \f] Here, several different + * types of binding sites @f$ q^A @f$, @f$ q^B @f$, etc. are considered. A molecule can either bind to site A or B (or + * C, etc.). A direct exchange between the different binding sites does not occur. While components without bound state + * (i.e., non-binding components) are supported, all other components must have the same number of bound states (i.e., + * binding sites). + * + * Internal state vector order is component-major. The state vector is composed of all components and within + * each component all bound states are listed. + * + * See @cite Guiochon2006. + * @tparam ParamHandler_t Type that can add support for external function dependence + */ +template class BiLangmuirLDFBindingBase : public ParamHandlerBindingModelBase +{ +public: + BiLangmuirLDFBindingBase() + { + } + virtual ~BiLangmuirLDFBindingBase() CADET_NOEXCEPT { + } - inline const char* BiLangmuirLDFParamHandler::identifier() CADET_NOEXCEPT { return "MULTI_COMPONENT_BILANGMUIR_LDF"; } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } + + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) + { + const bool res = BindingModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); - inline bool BiLangmuirLDFParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) + unsigned int numSlices = 0; + for (unsigned int i = 0; i < nComp; ++i) { - if ((_keq.size() != _kkin.size()) || (_keq.size() != _qMax.size()) || (_keq.size() < nComp)) - throw InvalidParameterException("MCBLLDF_KEQ, MCBLLDF_KKIN, and MCBLLDF_QMAX have to have the same size"); + if (nBound[i] == 0) + continue; - return true; + if (numSlices == 0) + numSlices = nBound[i]; + + if (nBound[i] != numSlices) + throw InvalidParameterException("Bi-Langmuir LDF binding model requires all components to have the " + "same number of bound states or zero"); } - inline const char* ExtBiLangmuirLDFParamHandler::identifier() CADET_NOEXCEPT { return "EXT_MULTI_COMPONENT_BILANGMUIR_LDF"; } + _numBindingComp = numBindingComponents(_nBoundStates, _nComp); - inline bool ExtBiLangmuirLDFParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) - { - if ((_keq.size() != _kkin.size()) || (_keq.size() != _qMax.size()) || (_keq.size() < nComp)) - throw InvalidParameterException("EXT_MCBLLDF_KEQ, EXT_MCBLLDF_KKIN, and EXT_MCBLLDF_QMAX have to have the same size"); + // Allocate space for parameters + _paramHandler.reserve(nComp * numSlices, numSlices, nComp, nBound); - return true; - } + return res; + } + virtual bool hasSalt() const CADET_NOEXCEPT + { + return false; + } + virtual bool supportsMultistate() const CADET_NOEXCEPT + { + return true; + } + virtual bool supportsNonBinding() const CADET_NOEXCEPT + { + return true; + } - /** - * @brief Defines the multi component Bi-Langmuir binding model - * @details Implements the Bi-Langmuir adsorption model: \f[ \begin{align} - * \frac{\mathrm{d}q_i^A}{\mathrm{d}t} &= k_{a,i}^A c_{p,i} q_{\text{max},i}^A \left( 1 - \sum_j \frac{q_j^A}{q_{\text{max},j}^A} \right) - k_{d,i}^A q_i^A \\ - * \frac{\mathrm{d}q_i^B}{\mathrm{d}t} &= k_{a,i}^B c_{p,i} q_{\text{max},i}^B \left( 1 - \sum_j \frac{q_j^B}{q_{\text{max},j}^B} \right) - k_{d,i}^B q_i^B \\ - * \vodts & \vdots - * \end{align} \f] - * Here, several different types of binding sites @f$ q^A @f$, @f$ q^B @f$, etc. are considered. A molecule can either bind to - * site A or B (or C, etc.). A direct exchange between the different binding sites does not occur. - * While components without bound state (i.e., non-binding components) are supported, all other components must have - * the same number of bound states (i.e., binding sites). - * - * Internal state vector order is component-major. The state vector is composed of all components and within each component - * all bound states are listed. - * - * See @cite Guiochon2006. - * @tparam ParamHandler_t Type that can add support for external function dependence - */ - template - class BiLangmuirLDFBindingBase : public ParamHandlerBindingModelBase - { - public: + /*virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yCp, double const* y, double* dResDt, LinearBufferAllocator workSpace) const + { + if (!this->hasQuasiStationaryReactions()) + return; - BiLangmuirLDFBindingBase() { } - virtual ~BiLangmuirLDFBindingBase() CADET_NOEXCEPT { } + if (!ParamHandler_t::dependsOnTime()) + return; - static const char* identifier() { return ParamHandler_t::identifier(); } + // Update external function and compute time derivative of parameters + typename ParamHandler_t::ParamsHandle p; + typename ParamHandler_t::ParamsHandle dpDt; + std::tie(p, dpDt) = _paramHandler.updateTimeDerivative(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) - { - const bool res = BindingModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); + // Protein flux: -k_{a,i}^j * c_{p,i} * (1 - \sum q_i^j / q_{max,i}^j) + k_{d,i}^j * q_i^j - unsigned int numSlices = 0; - for (unsigned int i = 0; i < nComp; ++i) - { - if (nBound[i] == 0) - continue; + const unsigned int nSites = p->kA.slices(); - if (numSlices == 0) - numSlices = nBound[i]; + // Ordering of the states is (q_{comp,state}) + // q_{0,0}, q{0,1}, q_{0,2}, q_{1,0}, q_{1,1}, q_{1,2}, ... + // A state corresponds to a type of binding site. It is assumed that all components have either 0 + // or the same number of states. Thus, a component is either non-binding or has nSites bound states. + // + // The same ordering is used for the fluxes. That is, q_{0,0}, q_{1,0} and q_{0,1}, q_{1,1} and ... each + // form one Langmuir binding model system. - if (nBound[i] != numSlices) - throw InvalidParameterException("Bi-Langmuir LDF binding model requires all components to have the same number of bound states or zero"); - } + // Loop over all binding site types + for (unsigned int site = 0; site < nSites; ++site, ++y, ++dResDt) + { + // y, and dResDt point to q_{0,site} - _numBindingComp = numBindingComponents(_nBoundStates, _nComp); + // Get parameter slice for current binding site type + active const* const localKa = p->kA[site]; + // active const* const localKd = p->kD[site]; + active const* const localQmax = p->qMax[site]; + active const* const localKaT = dpDt->kA[site]; + active const* const localKdT = dpDt->kD[site]; + active const* const localQmaxT = dpDt->qMax[site]; - // Allocate space for parameters - _paramHandler.reserve(nComp * numSlices, numSlices, nComp, nBound); + double qSum = 1.0; + double qSumT = 0.0; + unsigned int bndIdx = 0; - return res; - } + // bndIdx is used as a counter inside one binding site type + // Getting from one component to another requires a step size of nSites (stride) - virtual bool hasSalt() const CADET_NOEXCEPT { return false; } - virtual bool supportsMultistate() const CADET_NOEXCEPT { return true; } - virtual bool supportsNonBinding() const CADET_NOEXCEPT { return true; } + for (int i = 0; i < _nComp; ++i) + { + // Skip components without bound states (bound state index bndIdx is not advanced) + if (_nBoundStates[i] == 0) + continue; + + const double summand = y[bndIdx * nSites] / static_cast(localQmax[i]); + qSum -= summand; + qSumT += summand / static_cast(localQmax[i]) * static_cast(localQmaxT[i]); - /*virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yCp, double const* y, double* dResDt, LinearBufferAllocator workSpace) const + // Next bound component + ++bndIdx; + } + + bndIdx = 0; + for (int i = 0; i < _nComp; ++i) { - if (!this->hasQuasiStationaryReactions()) - return; + // Skip components without bound states (bound state index bndIdx is not advanced) + if (_nBoundStates[i] == 0) + continue; + + // Residual + dResDt[bndIdx * nSites] = static_cast(localKdT[i]) * y[bndIdx * nSites] + - yCp[i] * (static_cast(localKaT[i]) * static_cast(localQmax[i]) * qSum + + static_cast(localKa[i]) * static_cast(localQmaxT[i]) * qSum + + static_cast(localKa[i]) * static_cast(localQmax[i]) * qSumT); + + // Next bound component + ++bndIdx; + } + } + }*/ - if (!ParamHandler_t::dependsOnTime()) - return; + CADET_BINDINGMODELBASE_BOILERPLATE - // Update external function and compute time derivative of parameters - typename ParamHandler_t::ParamsHandle p; - typename ParamHandler_t::ParamsHandle dpDt; - std::tie(p, dpDt) = _paramHandler.updateTimeDerivative(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); +protected: + using ParamHandlerBindingModelBase::_paramHandler; + using ParamHandlerBindingModelBase::_reactionQuasistationarity; + using ParamHandlerBindingModelBase::_nComp; + using ParamHandlerBindingModelBase::_nBoundStates; - // Protein flux: -k_{a,i}^j * c_{p,i} * (1 - \sum q_i^j / q_{max,i}^j) + k_{d,i}^j * q_i^j + unsigned int _numBindingComp; //!< Number of binding components - const unsigned int nSites = p->kA.slices(); + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } - // Ordering of the states is (q_{comp,state}) - // q_{0,0}, q{0,1}, q_{0,2}, q_{1,0}, q_{1,1}, q_{1,2}, ... - // A state corresponds to a type of binding site. It is assumed that all components have either 0 - // or the same number of states. Thus, a component is either non-binding or has nSites bound states. - // - // The same ordering is used for the fluxes. That is, q_{0,0}, q_{1,0} and q_{0,1}, q_{1,1} and ... each - // form one Langmuir binding model system. + template + int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + { + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); - // Loop over all binding site types - for (unsigned int site = 0; site < nSites; ++site, ++y, ++dResDt) - { - // y, and dResDt point to q_{0,site} - - // Get parameter slice for current binding site type - active const* const localKa = p->kA[site]; - // active const* const localKd = p->kD[site]; - active const* const localQmax = p->qMax[site]; - active const* const localKaT = dpDt->kA[site]; - active const* const localKdT = dpDt->kD[site]; - active const* const localQmaxT = dpDt->qMax[site]; - - double qSum = 1.0; - double qSumT = 0.0; - unsigned int bndIdx = 0; - - // bndIdx is used as a counter inside one binding site type - // Getting from one component to another requires a step size of nSites (stride) - - for (int i = 0; i < _nComp; ++i) - { - // Skip components without bound states (bound state index bndIdx is not advanced) - if (_nBoundStates[i] == 0) - continue; - - const double summand = y[bndIdx * nSites] / static_cast(localQmax[i]); - qSum -= summand; - qSumT += summand / static_cast(localQmax[i]) * static_cast(localQmaxT[i]); - - // Next bound component - ++bndIdx; - } - - bndIdx = 0; - for (int i = 0; i < _nComp; ++i) - { - // Skip components without bound states (bound state index bndIdx is not advanced) - if (_nBoundStates[i] == 0) - continue; - - // Residual - dResDt[bndIdx * nSites] = static_cast(localKdT[i]) * y[bndIdx * nSites] - - yCp[i] * (static_cast(localKaT[i]) * static_cast(localQmax[i]) * qSum - + static_cast(localKa[i]) * static_cast(localQmaxT[i]) * qSum - + static_cast(localKa[i]) * static_cast(localQmax[i]) * qSumT); - - // Next bound component - ++bndIdx; - } - } - }*/ + // Protein flux: \frac{dq_{i,j}}{dt} = k_{kin,i,j}(q_{i,j}^*-q_{i,j}) with + // q_{i,j}^*=\frac{q_{m,i,j} k_{eq,i,j} c_i}{1 + \sum_{k=1}^{n_{comp}}{k_{eq,k,j} c_k}} - CADET_BINDINGMODELBASE_BOILERPLATE + const unsigned int nSites = p->keq.slices(); - protected: - using ParamHandlerBindingModelBase::_paramHandler; - using ParamHandlerBindingModelBase::_reactionQuasistationarity; - using ParamHandlerBindingModelBase::_nComp; - using ParamHandlerBindingModelBase::_nBoundStates; + // Ordering of the states is (q_{comp,state}) + // q_{0,0}, q{0,1}, q_{0,2}, q_{1,0}, q_{1,1}, q_{1,2}, ... + // A state corresponds to a type of binding site. It is assumed that all components have either 0 + // or the same number of states. Thus, a component is either non-binding or has nSites bound states. + // + // The same ordering is used for the fluxes. That is, q_{0,0}, q_{1,0} and q_{0,1}, q_{1,1} and ... each + // form one Langmuir binding model system. - unsigned int _numBindingComp; //!< Number of binding components + // Loop over all binding site types + for (unsigned int site = 0; site < nSites; ++site, ++y, ++res) + { + // y, and res point to q_{0,site} - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + // Get parameter slice for current binding site type + active const* const localKeq = p->keq[site]; + active const* const localKkin = p->kkin[site]; + active const* const localQmax = p->qMax[site]; - template - int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const - { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + ResidualType cpSum = 1.0; + unsigned int bndIdx = 0; - // Protein flux: \frac{dq_{i,j}}{dt} = k_{kin,i,j}(q_{i,j}^*-q_{i,j}) with - //q_{i,j}^*=\frac{q_{m,i,j} k_{eq,i,j} c_i}{1 + \sum_{k=1}^{n_{comp}}{k_{eq,k,j} c_k}} + // bndIdx is used as a counter inside one binding site type + // Getting from one component to another requires a step size of nSites (stride) - const unsigned int nSites = p->keq.slices(); + for (int i = 0; i < _nComp; ++i) + { + // Skip components without bound states (bound state index bndIdx is not advanced) + if (_nBoundStates[i] == 0) + continue; - // Ordering of the states is (q_{comp,state}) - // q_{0,0}, q{0,1}, q_{0,2}, q_{1,0}, q_{1,1}, q_{1,2}, ... - // A state corresponds to a type of binding site. It is assumed that all components have either 0 - // or the same number of states. Thus, a component is either non-binding or has nSites bound states. - // - // The same ordering is used for the fluxes. That is, q_{0,0}, q_{1,0} and q_{0,1}, q_{1,1} and ... each - // form one Langmuir binding model system. + cpSum += yCp[i] * static_cast(localKeq[i]); - // Loop over all binding site types - for (unsigned int site = 0; site < nSites; ++site, ++y, ++res) - { - // y, and res point to q_{0,site} + // Next bound component + ++bndIdx; + } - // Get parameter slice for current binding site type - active const* const localKeq = p->keq[site]; - active const* const localKkin = p->kkin[site]; - active const* const localQmax = p->qMax[site]; + bndIdx = 0; + for (int i = 0; i < _nComp; ++i) + { + // Skip components without bound states (bound state index bndIdx is not advanced) + if (_nBoundStates[i] == 0) + continue; - ResidualType cpSum = 1.0; - unsigned int bndIdx = 0; + // Residual + res[bndIdx * nSites] = static_cast(localKkin[i]) * + (y[bndIdx * nSites] - static_cast(localKeq[i]) * yCp[i] * + static_cast(localQmax[i]) / cpSum); - // bndIdx is used as a counter inside one binding site type - // Getting from one component to another requires a step size of nSites (stride) + // Next bound component + ++bndIdx; + } + } - for (int i = 0; i < _nComp; ++i) - { - // Skip components without bound states (bound state index bndIdx is not advanced) - if (_nBoundStates[i] == 0) - continue; + return 0; + } - cpSum += yCp[i] * static_cast(localKeq[i]); + template + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + { + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); - // Next bound component - ++bndIdx; - } + // Protein flux: \frac{dq_{i,j}}{dt} = k_{kin,i,j}(q_{i,j}^*-q_{i,j}) with + // q_{i,j}^*=\frac{q_{m,i,j} k_{eq,i,j} c_i}{1 + \sum_{k=1}^{n_{comp}}{k_{eq,k,j} c_k}} - bndIdx = 0; - for (int i = 0; i < _nComp; ++i) - { - // Skip components without bound states (bound state index bndIdx is not advanced) - if (_nBoundStates[i] == 0) - continue; + // Ordering of the states is (q_{comp,state}, example uses 2 components, 3 binding sites) + // q_{0,0}, q{0,1}, q_{0,2}, q_{1,0}, q_{1,1}, q_{1,2}, ... + // Ordering of the fluxes is the same, that is, we need nSites steps to jump from one flux of a Langmuir + // binding model system to the next. - // Residual - res[bndIdx * nSites] = static_cast(localKkin[i]) * (y[bndIdx * nSites] - static_cast(localKeq[i]) * yCp[i] * static_cast(localQmax[i]) / cpSum); + const int nSites = static_cast(p->keq.slices()); - // Next bound component - ++bndIdx; - } - } + // Loop over all binding site types + for (int site = 0; site < nSites; ++site, ++y) + { + // Get parameter slice for current binding site type + active const* const localKeq = p->keq[site]; + active const* const localKkin = p->kkin[site]; + active const* const localQmax = p->qMax[site]; + + double cpSum = 1.0; + int bndIdx = 0; + for (int i = 0; i < _nComp; ++i) + { + // Skip components without bound states (bound state index bndIdx is not advanced) + if (_nBoundStates[i] == 0) + continue; + + cpSum += yCp[i] * static_cast(localKeq[i]); - return 0; + // Next bound component + ++bndIdx; } - template - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + bndIdx = 0; + for (int i = 0; i < _nComp; ++i) { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + // Skip components without bound states (bound state index bndIdx is not advanced) + if (_nBoundStates[i] == 0) + continue; + + const double keq = static_cast(localKeq[i]); + const double kkin = static_cast(localKkin[i]); + + // dres_i / dc_{p,i} + jac[i - site - offsetCp - nSites * bndIdx] = -kkin * keq * static_cast(localQmax[i]) / cpSum; + // Getting to c_{p,i}: -nSites * bndIdx takes us to q_{0,site}, another -site to q_{0,0}. From there, we + // take a -offsetCp to reach c_{p,0} and a +i to arrive at c_{p,i}. + // This means jac[i - site - offsetCp - nSites * bndIdx] corresponds to c_{p,i}. + + // Fill dres_i / dc_j + const double commonFactor = kkin * keq * static_cast(localQmax[i]) * yCp[i] / (cpSum * cpSum); + int bndIdx2 = 0; + for (int j = 0; j < _nComp; ++j) + { + // Skip components without bound states (bound state index bndIdx2 is not advanced) + if (_nBoundStates[j] == 0) + continue; - // Protein flux: \frac{dq_{i,j}}{dt} = k_{kin,i,j}(q_{i,j}^*-q_{i,j}) with - //q_{i,j}^*=\frac{q_{m,i,j} k_{eq,i,j} c_i}{1 + \sum_{k=1}^{n_{comp}}{k_{eq,k,j} c_k}} + // dres_i / dc_j + jac[j - site - offsetCp - nSites * bndIdx] += static_cast(localKeq[j]) * commonFactor; + // Getting to c_{p,j}: -nSites * bndIdx takes us to q_{0,site}, another -site to q_{0,0}. From + // there, we + // take a -offsetCp to reach c_{p,0} and a +j to arrive at c_{p,j}. + // This means jac[j - site - offsetCp - nSites * bndIdx] corresponds to c_{p,j}. - // Ordering of the states is (q_{comp,state}, example uses 2 components, 3 binding sites) - // q_{0,0}, q{0,1}, q_{0,2}, q_{1,0}, q_{1,1}, q_{1,2}, ... - // Ordering of the fluxes is the same, that is, we need nSites steps to jump from one flux of a Langmuir - // binding model system to the next. + ++bndIdx2; + } - const int nSites = static_cast(p->keq.slices()); + // Add to dres_i / dq_{i,site} + jac[0] += kkin; - // Loop over all binding site types - for (int site = 0; site < nSites; ++site, ++y) - { - // Get parameter slice for current binding site type - active const* const localKeq = p->keq[site]; - active const* const localKkin = p->kkin[site]; - active const* const localQmax = p->qMax[site]; - - double cpSum = 1.0; - int bndIdx = 0; - for (int i = 0; i < _nComp; ++i) - { - // Skip components without bound states (bound state index bndIdx is not advanced) - if (_nBoundStates[i] == 0) - continue; - - cpSum += yCp[i] * static_cast(localKeq[i]); - - // Next bound component - ++bndIdx; - } - - bndIdx = 0; - for (int i = 0; i < _nComp; ++i) - { - // Skip components without bound states (bound state index bndIdx is not advanced) - if (_nBoundStates[i] == 0) - continue; - - const double keq = static_cast(localKeq[i]); - const double kkin = static_cast(localKkin[i]); - - // dres_i / dc_{p,i} - jac[i - site - offsetCp - nSites * bndIdx] = -kkin * keq * static_cast(localQmax[i]) / cpSum; - // Getting to c_{p,i}: -nSites * bndIdx takes us to q_{0,site}, another -site to q_{0,0}. From there, we - // take a -offsetCp to reach c_{p,0} and a +i to arrive at c_{p,i}. - // This means jac[i - site - offsetCp - nSites * bndIdx] corresponds to c_{p,i}. - - // Fill dres_i / dc_j - const double commonFactor = kkin * keq * static_cast(localQmax[i]) * yCp[i] / (cpSum * cpSum); - int bndIdx2 = 0; - for (int j = 0; j < _nComp; ++j) - { - // Skip components without bound states (bound state index bndIdx2 is not advanced) - if (_nBoundStates[j] == 0) - continue; - - // dres_i / dc_j - jac[j - site - offsetCp - nSites * bndIdx] += static_cast(localKeq[j]) * commonFactor; - // Getting to c_{p,j}: -nSites * bndIdx takes us to q_{0,site}, another -site to q_{0,0}. From there, we - // take a -offsetCp to reach c_{p,0} and a +j to arrive at c_{p,j}. - // This means jac[j - site - offsetCp - nSites * bndIdx] corresponds to c_{p,j}. - - ++bndIdx2; - } - - // Add to dres_i / dq_{i,site} - jac[0] += kkin; - - // Advance to next flux and Jacobian row - ++bndIdx; - jac += nSites; - // Note that there is a spacing of nSites between the fluxes inside one binding site type - } - // We are at the end of the flux block q_{_numBindingComp,site} and need to jump back to the beginning - // by using -_numBindingComp * nSites steps. From there, we take one step forward to arrive at q_{0,site+1}. - jac -= _numBindingComp * nSites - 1; - } + // Advance to next flux and Jacobian row + ++bndIdx; + jac += nSites; + // Note that there is a spacing of nSites between the fluxes inside one binding site type } - }; + // We are at the end of the flux block q_{_numBindingComp,site} and need to jump back to the beginning + // by using -_numBindingComp * nSites steps. From there, we take one step forward to arrive at q_{0,site+1}. + jac -= _numBindingComp * nSites - 1; + } + } +}; - typedef BiLangmuirLDFBindingBase BiLangmuirLDFBinding; - typedef BiLangmuirLDFBindingBase ExternalBiLangmuirLDFBinding; +typedef BiLangmuirLDFBindingBase BiLangmuirLDFBinding; +typedef BiLangmuirLDFBindingBase ExternalBiLangmuirLDFBinding; - namespace binding - { - void registerBiLangmuirLDFModel(std::unordered_map>& bindings) - { - bindings[BiLangmuirLDFBinding::identifier()] = []() { return new BiLangmuirLDFBinding(); }; - bindings[ExternalBiLangmuirLDFBinding::identifier()] = []() { return new ExternalBiLangmuirLDFBinding(); }; - } - } // namespace binding +namespace binding +{ +void registerBiLangmuirLDFModel(std::unordered_map>& bindings) +{ + bindings[BiLangmuirLDFBinding::identifier()] = []() { return new BiLangmuirLDFBinding(); }; + bindings[ExternalBiLangmuirLDFBinding::identifier()] = []() { return new ExternalBiLangmuirLDFBinding(); }; +} +} // namespace binding - } // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/BiStericMassActionBinding.cpp b/src/libcadet/model/binding/BiStericMassActionBinding.cpp index 23cb9f434..f2ef0c4c7 100644 --- a/src/libcadet/model/binding/BiStericMassActionBinding.cpp +++ b/src/libcadet/model/binding/BiStericMassActionBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -39,7 +39,8 @@ ], "constantParameters": [ - { "type": "ReferenceConcentrationBoundStateDependentParameter", "varName": ["refC0", "refQ"], "objName": "refConcentration", "confPrefix": "BISMA_"} + { "type": "ReferenceConcentrationBoundStateDependentParameter", "varName": ["refC0", "refQ"], "objName": +"refConcentration", "confPrefix": "BISMA_"} ] } */ @@ -60,13 +61,17 @@ namespace cadet namespace model { -inline const char* BiSMAParamHandler::identifier() CADET_NOEXCEPT { return "BI_STERIC_MASS_ACTION"; } +inline const char* BiSMAParamHandler::identifier() CADET_NOEXCEPT +{ + return "BI_STERIC_MASS_ACTION"; +} inline bool BiSMAParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { const unsigned int numStates = firstNonEmptyBoundStates(nBoundStates, nComp); - if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _sigma.size()) || (_kA.size() < nComp * numStates)) + if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _sigma.size()) || + (_kA.size() < nComp * numStates)) throw InvalidParameterException("BISMA_KA, BISMA_KD, BISMA_NU, and BISMA_SIGMA have to have the same size"); if (_lambda.size() != numStates) throw InvalidParameterException("BISMA_LAMBDA has to have as many elements as there are binding sites"); @@ -81,13 +86,17 @@ inline bool BiSMAParamHandler::validateConfig(unsigned int nComp, unsigned int c return true; } -inline const char* ExtBiSMAParamHandler::identifier() CADET_NOEXCEPT { return "EXT_BI_STERIC_MASS_ACTION"; } +inline const char* ExtBiSMAParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_BI_STERIC_MASS_ACTION"; +} inline bool ExtBiSMAParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { const unsigned int numStates = firstNonEmptyBoundStates(nBoundStates, nComp); - if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _sigma.size()) || (_kA.size() < nComp * numStates)) + if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _sigma.size()) || + (_kA.size() < nComp * numStates)) throw InvalidParameterException("BISMA_KA, BISMA_KD, BISMA_NU, and BISMA_SIGMA have to have the same size"); if (_lambda.size() != numStates) throw InvalidParameterException("BISMA_LAMBDA has to have as many elements as there are binding sites"); @@ -102,24 +111,24 @@ inline bool ExtBiSMAParamHandler::validateConfig(unsigned int nComp, unsigned in return true; } - /** * @brief Defines the Bi-SMA binding model - * @details Implements the Bi-SMA adsorption model, which introduces multiple different binding sites and assumes an SMA model for each site: \f[ \begin{align} + * @details Implements the Bi-SMA adsorption model, which introduces multiple different binding sites and assumes an SMA + * model for each site: \f[ \begin{align} * q_0^A &= \Lambda^A - \sum_{j} \nu_j^A q_j^A \\ * q_0^B &= \Lambda^B - \sum_{j} \nu_j^B q_j^B \\ * \vodts & \vdots - * \frac{\mathrm{d}q_i^A}{\mathrm{d}t} &= k_{a,i}^A c_{p,i} \left( \Lambda^A - \sum_j\left( \nu_j^A + \sigma_j^A \right) q_j^A \right)^{\nu_i^A} - k_{d,i}^A q_i^A c_{p,0}^{\nu_i^A} \\ - * \frac{\mathrm{d}q_i^B}{\mathrm{d}t} &= k_{a,i}^B c_{p,i} \left( \Lambda^B - \sum_j\left( \nu_j^B + \sigma_j^B \right) q_j^B \right)^{\nu_i^B} - k_{d,i}^B q_i^B c_{p,0}^{\nu_i^B} \\ - * \vodts & \vdots - * \end{align} \f] - * Here, several different types of binding sites @f$ q^A @f$, @f$ q^B @f$, etc. are considered. A molecule can either bind to - * site A or B (or C, etc.). A direct exchange between the different binding sites does not occur. - * While components without bound state (i.e., non-binding components) are supported, all other components must have - * the same number of bound states (i.e., binding sites). Component @c 0 is assumed to be salt. - * - * Since all algebraic variables have to be in one contiguous block, the order of the bound state variables is as follows: \f[ \begin{align} - * q_0^A, q_0^B, q_0^C, \dots, q_1^A, q_2^A, q_3^A, \dots, q_1^B, q_2^B, q_3^B, \dots + * \frac{\mathrm{d}q_i^A}{\mathrm{d}t} &= k_{a,i}^A c_{p,i} \left( \Lambda^A - \sum_j\left( \nu_j^A + + * \sigma_j^A \right) q_j^A \right)^{\nu_i^A} - k_{d,i}^A q_i^A c_{p,0}^{\nu_i^A} \\ + * \frac{\mathrm{d}q_i^B}{\mathrm{d}t} &= k_{a,i}^B c_{p,i} \left( \Lambda^B - \sum_j\left( \nu_j^B + + * \sigma_j^B \right) q_j^B \right)^{\nu_i^B} - k_{d,i}^B q_i^B c_{p,0}^{\nu_i^B} \\ \vodts & \vdots \end{align} \f] + * Here, several different types of binding sites @f$ q^A @f$, @f$ q^B @f$, etc. are considered. A molecule can + * either bind to site A or B (or C, etc.). A direct exchange between the different binding sites does not occur. While + * components without bound state (i.e., non-binding components) are supported, all other components must have the same + * number of bound states (i.e., binding sites). Component @c 0 is assumed to be salt. + * + * Since all algebraic variables have to be in one contiguous block, the order of the bound state variables is + * as follows: \f[ \begin{align} q_0^A, q_0^B, q_0^C, \dots, q_1^A, q_2^A, q_3^A, \dots, q_1^B, q_2^B, q_3^B, \dots * \end{align} \f] * First, all the salt components are collected in one block as they are always algebraic. * Then the other components for each bound state follow. @@ -129,13 +138,20 @@ template class BiStericMassActionBindingBase : public ParamHandlerBindingModelBase { public: + BiStericMassActionBindingBase() + { + } + virtual ~BiStericMassActionBindingBase() CADET_NOEXCEPT + { + } - BiStericMassActionBindingBase() { } - virtual ~BiStericMassActionBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { const bool res = BindingModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); @@ -149,7 +165,8 @@ class BiStericMassActionBindingBase : public ParamHandlerBindingModelBaselambda.size(); // Compute salt component from given bound states q_j @@ -182,7 +214,7 @@ class BiStericMassActionBindingBase : public ParamHandlerBindingModelBase q_0 == (Lambda - Sum[nu_j * q_j, j]) / nu_0 y[bndSite] = static_cast(p->lambda[bndSite]); @@ -208,7 +240,8 @@ class BiStericMassActionBindingBase : public ParamHandlerBindingModelBase int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { using CpStateParamType = typename DoubleActivePromoter::type; using StateParamType = typename DoubleActivePromoter::type; - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); const unsigned int numStates = p->lambda.size(); @@ -257,8 +294,8 @@ class BiStericMassActionBindingBase : public ParamHandlerBindingModelBase(p->refC0[bndSite]); const ParamType refQ = static_cast(p->refQ[bndSite]); - // Salt flux: q_0 - Lambda + Sum[nu_j * q_j, j] == 0 - // <=> q_0 == Lambda - Sum[nu_j * q_j, j] + // Salt flux: q_0 - Lambda + Sum[nu_j * q_j, j] == 0 + // <=> q_0 == Lambda - Sum[nu_j * q_j, j] // Also compute \bar{q}_0 = q_0 - Sum[sigma_j * q_j, j] res[0] = static_cast(curNu[0]) * y[0] - static_cast(p->lambda[bndSite]); StateParamType q0_bar = static_cast(curNu[0]) * y[0]; @@ -295,7 +332,8 @@ class BiStericMassActionBindingBase : public ParamHandlerBindingModelBase(curKd[i]) * y[bndIdx * numStates] * c0_pow_nu_divRef - static_cast(curKa[i]) * yCp[i] * q0_bar_pow_nu_divRef; + res[bndIdx * numStates] = static_cast(curKd[i]) * y[bndIdx * numStates] * c0_pow_nu_divRef - + static_cast(curKa[i]) * yCp[i] * q0_bar_pow_nu_divRef; // Next bound component ++bndIdx; @@ -305,9 +343,11 @@ class BiStericMassActionBindingBase : public ParamHandlerBindingModelBase - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); const unsigned int numStates = p->lambda.size(); @@ -364,27 +404,31 @@ class BiStericMassActionBindingBase : public ParamHandlerBindingModelBase(curKa[i]); const double kd = static_cast(curKd[i]); const double nu_over_nu0 = static_cast(curNu[i]) / static_cast(curNu[0]); - const double c0_pow_nu = pow(yCp0_divRef, nu_over_nu0); + const double c0_pow_nu = pow(yCp0_divRef, nu_over_nu0); const double q0_bar_pow_nu = pow(q0_bar_divRef, nu_over_nu0); - const double c0_pow_nu_m1_divRef = pow(yCp0_divRef, nu_over_nu0 - 1.0); + const double c0_pow_nu_m1_divRef = pow(yCp0_divRef, nu_over_nu0 - 1.0); const double q0_bar_pow_nu_m1_divRef = pow(q0_bar_divRef, nu_over_nu0 - 1.0) / refQ; // dres_i / dc_{p,0} - curJac[-bndSite - offsetCp - numStates * bndIdx] = kd * y[bndIdx * numStates] * nu_over_nu0 * c0_pow_nu_m1_divRef / refC0; + curJac[-bndSite - offsetCp - numStates * bndIdx] = + kd * y[bndIdx * numStates] * nu_over_nu0 * c0_pow_nu_m1_divRef / refC0; // dres_i / dc_{p,i} curJac[i - bndSite - offsetCp - numStates * bndIdx] = -ka * q0_bar_pow_nu; // dres_i / dq_{0,bndSite} - curJac[-bndIdx * numStates] = -ka * yCp[i] * nu_over_nu0 * q0_bar_pow_nu_m1_divRef * static_cast(curNu[0]); + curJac[-bndIdx * numStates] = + -ka * yCp[i] * nu_over_nu0 * q0_bar_pow_nu_m1_divRef * static_cast(curNu[0]); // Fill dres_i / dq_{j,bndSite} int bndIdx2 = 1; @@ -395,7 +439,8 @@ class BiStericMassActionBindingBase : public ParamHandlerBindingModelBase(curSigma[j])); + curJac[(bndIdx2 - bndIdx) * numStates] = + -ka * yCp[i] * nu_over_nu0 * q0_bar_pow_nu_m1_divRef * (-static_cast(curSigma[j])); // Getting to q_j: -bndIdx * numStates takes us to q_{0,bndSite}, another +bndIdx2 * numStates to // q_{j,bndSite}. This means curJac[(bndIdx2 - bndIdx) * numStates] corresponds to q_{j,bndSite}. @@ -414,19 +459,20 @@ class BiStericMassActionBindingBase : public ParamHandlerBindingModelBase BiStericMassActionBinding; typedef BiStericMassActionBindingBase ExternalBiStericMassActionBinding; namespace binding { - void registerBiStericMassActionModel(std::unordered_map>& bindings) - { - bindings[BiStericMassActionBinding::identifier()] = []() { return new BiStericMassActionBinding(); }; - bindings[ExternalBiStericMassActionBinding::identifier()] = []() { return new ExternalBiStericMassActionBinding(); }; - } -} // namespace binding +void registerBiStericMassActionModel(std::unordered_map>& bindings) +{ + bindings[BiStericMassActionBinding::identifier()] = []() { return new BiStericMassActionBinding(); }; + bindings[ExternalBiStericMassActionBinding::identifier()] = []() { + return new ExternalBiStericMassActionBinding(); + }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/BindingModelBase.cpp b/src/libcadet/model/binding/BindingModelBase.cpp index 84782237b..17bbb05c3 100644 --- a/src/libcadet/model/binding/BindingModelBase.cpp +++ b/src/libcadet/model/binding/BindingModelBase.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -30,12 +30,17 @@ namespace cadet namespace model { -BindingModelBase::BindingModelBase() : _nComp(0), _nBoundStates(nullptr), _reactionQuasistationarity(0, false), _hasQuasiStationary(false), _hasDynamic(true) { } +BindingModelBase::BindingModelBase() + : _nComp(0), _nBoundStates(nullptr), _reactionQuasistationarity(0, false), _hasQuasiStationary(false), + _hasDynamic(true) +{ +} BindingModelBase::~BindingModelBase() CADET_NOEXCEPT { } -bool BindingModelBase::configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) +bool BindingModelBase::configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { _nComp = nComp; _nBoundStates = nBound; @@ -51,12 +56,14 @@ bool BindingModelBase::configureModelDiscretization(IParameterProvider& paramPro if (vecKin.size() == 1) { // Treat an array with a single element as scalar - std::fill(_reactionQuasistationarity.begin(), _reactionQuasistationarity.end(), !static_cast(vecKin[0])); + std::fill(_reactionQuasistationarity.begin(), _reactionQuasistationarity.end(), + !static_cast(vecKin[0])); } else if (vecKin.size() < _reactionQuasistationarity.size()) { // Error on too few elements - throw InvalidParameterException("IS_KINETIC has to have at least " + std::to_string(_reactionQuasistationarity.size()) + " elements"); + throw InvalidParameterException("IS_KINETIC has to have at least " + + std::to_string(_reactionQuasistationarity.size()) + " elements"); } else { @@ -70,8 +77,10 @@ bool BindingModelBase::configureModelDiscretization(IParameterProvider& paramPro std::fill(_reactionQuasistationarity.begin(), _reactionQuasistationarity.end(), !kineticBinding); } - _hasQuasiStationary = std::any_of(_reactionQuasistationarity.begin(), _reactionQuasistationarity.end(), [](int i) -> bool { return i; }); - _hasDynamic = std::any_of(_reactionQuasistationarity.begin(), _reactionQuasistationarity.end(), [](int i) -> bool { return !static_cast(i); }); + _hasQuasiStationary = std::any_of(_reactionQuasistationarity.begin(), _reactionQuasistationarity.end(), + [](int i) -> bool { return i; }); + _hasDynamic = std::any_of(_reactionQuasistationarity.begin(), _reactionQuasistationarity.end(), + [](int i) -> bool { return !static_cast(i); }); return true; } @@ -83,7 +92,8 @@ bool BindingModelBase::configure(IParameterProvider& paramProvider, UnitOpIdx un return configureImpl(paramProvider, unitOpIdx, parTypeIdx); } -void BindingModelBase::fillBoundPhaseInitialParameters(ParameterId* params, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) const CADET_NOEXCEPT +void BindingModelBase::fillBoundPhaseInitialParameters(ParameterId* params, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx) const CADET_NOEXCEPT { unsigned int ctr = 0; for (int c = 0; c < _nComp; ++c) @@ -97,7 +107,9 @@ std::unordered_map BindingModelBase::getAllParameterValues( { std::unordered_map data; std::transform(_parameters.begin(), _parameters.end(), std::inserter(data, data.end()), - [](const std::pair& p) { return std::make_pair(p.first, static_cast(*p.second)); }); + [](const std::pair& p) { + return std::make_pair(p.first, static_cast(*p.second)); + }); return data; } @@ -139,6 +151,6 @@ active* BindingModelBase::getParameter(const ParameterId& pId) return nullptr; } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/BindingModelBase.hpp b/src/libcadet/model/binding/BindingModelBase.hpp index 6f392a339..8ab54be94 100644 --- a/src/libcadet/model/binding/BindingModelBase.hpp +++ b/src/libcadet/model/binding/BindingModelBase.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines a BindingModelBase class. */ @@ -46,16 +46,23 @@ namespace model class BindingModelBase : public IBindingModel { public: - BindingModelBase(); virtual ~BindingModelBase() CADET_NOEXCEPT; - virtual bool requiresConfiguration() const CADET_NOEXCEPT { return true; } - virtual bool usesParamProviderInDiscretizationConfig() const CADET_NOEXCEPT { return true; } + virtual bool requiresConfiguration() const CADET_NOEXCEPT + { + return true; + } + virtual bool usesParamProviderInDiscretizationConfig() const CADET_NOEXCEPT + { + return true; + } virtual bool configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx); - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset); - virtual void fillBoundPhaseInitialParameters(ParameterId* params, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) const CADET_NOEXCEPT; + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset); + virtual void fillBoundPhaseInitialParameters(ParameterId* params, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx) const CADET_NOEXCEPT; virtual std::unordered_map getAllParameterValues() const; virtual bool hasParameter(const ParameterId& pId) const; @@ -66,12 +73,25 @@ class BindingModelBase : public IBindingModel virtual active* getParameter(const ParameterId& pId); - virtual bool hasSalt() const CADET_NOEXCEPT { return false; } - virtual bool supportsMultistate() const CADET_NOEXCEPT { return false; } - virtual bool supportsNonBinding() const CADET_NOEXCEPT { return true; } + virtual bool hasSalt() const CADET_NOEXCEPT + { + return false; + } + virtual bool supportsMultistate() const CADET_NOEXCEPT + { + return false; + } + virtual bool supportsNonBinding() const CADET_NOEXCEPT + { + return true; + } - virtual bool requiresWorkspace() const CADET_NOEXCEPT { return !implementsAnalyticJacobian(); } - virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT + virtual bool requiresWorkspace() const CADET_NOEXCEPT + { + return !implementsAnalyticJacobian(); + } + virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT { if (implementsAnalyticJacobian()) return 0; @@ -81,16 +101,38 @@ class BindingModelBase : public IBindingModel return requiredAdVars * sizeof(active) + alignof(active); } - virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) { } + virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) + { + } - virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yCp, double const* y, double* dResDt, LinearBufferAllocator workSpace) const { } + virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yCp, double const* y, double* dResDt, + LinearBufferAllocator workSpace) const + { + } - virtual int const* reactionQuasiStationarity() const CADET_NOEXCEPT { return _reactionQuasistationarity.data(); } - virtual bool hasQuasiStationaryReactions() const CADET_NOEXCEPT { return _hasQuasiStationary; } - virtual bool hasDynamicReactions() const CADET_NOEXCEPT { return _hasDynamic; } + virtual int const* reactionQuasiStationarity() const CADET_NOEXCEPT + { + return _reactionQuasistationarity.data(); + } + virtual bool hasQuasiStationaryReactions() const CADET_NOEXCEPT + { + return _hasQuasiStationary; + } + virtual bool hasDynamicReactions() const CADET_NOEXCEPT + { + return _hasDynamic; + } - virtual bool preConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const { return true; } - virtual void postConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const { } + virtual bool preConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, + double const* yCp, LinearBufferAllocator workSpace) const + { + return true; + } + virtual void postConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, + double const* yCp, LinearBufferAllocator workSpace) const + { + } virtual unsigned int requiredADdirs() const CADET_NOEXCEPT { @@ -102,17 +144,19 @@ class BindingModelBase : public IBindingModel CADET_BINDINGMODEL_JACOBIAN_BOILERPLATE protected: - int _nComp; //!< Number of components - unsigned int const* _nBoundStates; //!< Array with number of bound states for each component - std::vector _reactionQuasistationarity; //!< Determines whether each bound state is quasi-stationary (@c true) or not (@c false) - bool _hasQuasiStationary; //!< Caches whether the model contains quasi-stationary reaction fluxes - bool _hasDynamic; //!< Caches whether the model contains dynamic reaction fluxes + int _nComp; //!< Number of components + unsigned int const* _nBoundStates; //!< Array with number of bound states for each component + std::vector _reactionQuasistationarity; //!< Determines whether each bound state is quasi-stationary (@c true) + //!< or not (@c false) + bool _hasQuasiStationary; //!< Caches whether the model contains quasi-stationary reaction fluxes + bool _hasDynamic; //!< Caches whether the model contains dynamic reaction fluxes std::unordered_map _parameters; //!< Map used to translate ParameterIds to actual variables /** * @brief Returns whether the function analyticJacobian() is implemented - * @details The Jacobian can be calculated using automatic differentiation (AD) if analyticJacobian() is not implemented. + * @details The Jacobian can be calculated using automatic differentiation (AD) if analyticJacobian() is not + * implemented. * @return @c true if analyticJacobian() is implemented, otherwise @c false */ virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT = 0; @@ -130,7 +174,8 @@ class BindingModelBase : public IBindingModel virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) = 0; template - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { const int nTotalBound = std::accumulate(_nBoundStates, _nBoundStates + _nComp, 0); active* const adVec = static_cast(workSpace.array(nTotalBound * 2 + _nComp)); @@ -153,28 +198,41 @@ class BindingModelBase : public IBindingModel } }; - /** * @brief Defines a BindingModel base class that can be used to implement binding models with parameter handlers * @details In addition to BindingModelBase, this class supports the ParameterHandler pattern, which allows * to easily add support for externally dependent parameters. * @tparam handler_t Type that can add support for external function dependence */ -template -class ParamHandlerBindingModelBase : public BindingModelBase +template class ParamHandlerBindingModelBase : public BindingModelBase { public: + ParamHandlerBindingModelBase() + { + } - ParamHandlerBindingModelBase() { } - - virtual const char* name() const CADET_NOEXCEPT { return handler_t::identifier(); } - virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) { _paramHandler.setExternalFunctions(extFuns, size); } - virtual bool dependsOnTime() const CADET_NOEXCEPT { return handler_t::dependsOnTime(); } - virtual bool requiresWorkspace() const CADET_NOEXCEPT { return handler_t::requiresWorkspace() || BindingModelBase::requiresWorkspace(); } + virtual const char* name() const CADET_NOEXCEPT + { + return handler_t::identifier(); + } + virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) + { + _paramHandler.setExternalFunctions(extFuns, size); + } + virtual bool dependsOnTime() const CADET_NOEXCEPT + { + return handler_t::dependsOnTime(); + } + virtual bool requiresWorkspace() const CADET_NOEXCEPT + { + return handler_t::requiresWorkspace() || BindingModelBase::requiresWorkspace(); + } - virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT + virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT { - return BindingModelBase::workspaceSize(nComp, totalNumBoundStates, nBoundStates) + _paramHandler.cacheSize(nComp, totalNumBoundStates, nBoundStates); + return BindingModelBase::workspaceSize(nComp, totalNumBoundStates, nBoundStates) + + _paramHandler.cacheSize(nComp, totalNumBoundStates, nBoundStates); } protected: @@ -192,20 +250,20 @@ class ParamHandlerBindingModelBase : public BindingModelBase } }; - /** - * @brief Inserts implementations for common functions required by BindingModelBase forwarding them to templatized functions + * @brief Inserts implementations for common functions required by BindingModelBase forwarding them to templatized + * functions * @details An implementation of IBindingModel has to provide some virtual functions. - * This macro provides the implementation of those functions by forwarding them to the templatized + * This macro provides the implementation of those functions by forwarding them to the templatized * functions residualImpl() and jacobianImpl(), which are assumed to be present in the class. - * + * * The implementation is inserted inline in the class declaration. */ -#define CADET_BINDINGMODELBASE_BOILERPLATE \ - CADET_BINDINGMODEL_RESIDUAL_BOILERPLATE \ +#define CADET_BINDINGMODELBASE_BOILERPLATE \ + CADET_BINDINGMODEL_RESIDUAL_BOILERPLATE \ CADET_BINDINGMODEL_JACOBIAN_BOILERPLATE } // namespace model } // namespace cadet -#endif // LIBCADET_BINDINGMODELBASE_HPP_ +#endif // LIBCADET_BINDINGMODELBASE_HPP_ diff --git a/src/libcadet/model/binding/BindingModelMacros.hpp b/src/libcadet/model/binding/BindingModelMacros.hpp index 20d691f6a..2b43c7ab4 100644 --- a/src/libcadet/model/binding/BindingModelMacros.hpp +++ b/src/libcadet/model/binding/BindingModelMacros.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines some useful macros for IBindingModel implementations. */ @@ -21,78 +21,82 @@ /** * @brief Inserts implementations of all flux() method variants * @details An IBindingModel implementation has to provide flux() methods for different variants of state and - * parameter type. This macro saves some time by providing those implementations. It assumes that the + * parameter type. This macro saves some time by providing those implementations. It assumes that the * implementation provides a templatized fluxImpl() function which realizes all required variants. - * + * * The implementation is inserted inline in the class declaration. */ -#define CADET_BINDINGMODEL_RESIDUAL_BOILERPLATE \ - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, \ - active const* yCp, active* res, LinearBufferAllocator workSpace, WithParamSensitivity) const \ - { \ - return fluxImpl(t, secIdx, colPos, y, yCp, res, workSpace); \ - } \ - \ - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, \ - active const* yCp, active* res, LinearBufferAllocator workSpace, WithoutParamSensitivity) const \ - { \ - return fluxImpl(t, secIdx, colPos, y, yCp, res, workSpace); \ - } \ - \ - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - double const* yCp, active* res, LinearBufferAllocator workSpace) const \ - { \ - return fluxImpl(t, secIdx, colPos, y, yCp, res, workSpace); \ - } \ - \ - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - double const* yCp, double* res, LinearBufferAllocator workSpace) const \ - { \ - return fluxImpl(t, secIdx, colPos, y, yCp, res, workSpace); \ +#define CADET_BINDINGMODEL_RESIDUAL_BOILERPLATE \ + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, active const* yCp, \ + active* res, LinearBufferAllocator workSpace, WithParamSensitivity) const \ + { \ + return fluxImpl(t, secIdx, colPos, y, yCp, res, workSpace); \ + } \ + \ + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, active const* yCp, \ + active* res, LinearBufferAllocator workSpace, WithoutParamSensitivity) const \ + { \ + return fluxImpl(t, secIdx, colPos, y, yCp, res, workSpace); \ + } \ + \ + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, \ + active* res, LinearBufferAllocator workSpace) const \ + { \ + return fluxImpl(t, secIdx, colPos, y, yCp, res, workSpace); \ + } \ + \ + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, \ + double* res, LinearBufferAllocator workSpace) const \ + { \ + return fluxImpl(t, secIdx, colPos, y, yCp, res, workSpace); \ } - /** * @brief Inserts implementations of all analyticJacobian() method variants * @details An IBindingModel implementation has to provide analyticJacobian() methods for different variants * of state and parameter type. This macro saves some time by providing those implementations. It * assumes that the implementation provides a templatized jacobianImpl() function that realizes all * required variants. - * + * * The implementation is inserted inline in the class declaration. */ #ifdef ENABLE_DG - #define CADET_BINDINGMODEL_JACOBIAN_BOILERPLATE \ - virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - int offsetCp, linalg::BandMatrix::RowIterator jac, LinearBufferAllocator workSpace) const \ - { \ - jacobianImpl(t, secIdx, colPos, y, y - offsetCp, offsetCp, jac, workSpace); \ - } \ - \ - virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - int offsetCp, linalg::DenseBandedRowIterator jac, LinearBufferAllocator workSpace) const \ - { \ - jacobianImpl(t, secIdx, colPos, y, y - offsetCp, offsetCp, jac, workSpace); \ - } \ - \ - virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - int offsetCp, linalg::BandedEigenSparseRowIterator jac, LinearBufferAllocator workSpace) const \ - { \ - jacobianImpl(t, secIdx, colPos, y, y - offsetCp, offsetCp, jac, workSpace); \ - } +#define CADET_BINDINGMODEL_JACOBIAN_BOILERPLATE \ + virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ + int offsetCp, linalg::BandMatrix::RowIterator jac, LinearBufferAllocator workSpace) \ + const \ + { \ + jacobianImpl(t, secIdx, colPos, y, y - offsetCp, offsetCp, jac, workSpace); \ + } \ + \ + virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ + int offsetCp, linalg::DenseBandedRowIterator jac, LinearBufferAllocator workSpace) \ + const \ + { \ + jacobianImpl(t, secIdx, colPos, y, y - offsetCp, offsetCp, jac, workSpace); \ + } \ + \ + virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ + int offsetCp, linalg::BandedEigenSparseRowIterator jac, \ + LinearBufferAllocator workSpace) const \ + { \ + jacobianImpl(t, secIdx, colPos, y, y - offsetCp, offsetCp, jac, workSpace); \ + } #else - #define CADET_BINDINGMODEL_JACOBIAN_BOILERPLATE \ - virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - int offsetCp, linalg::BandMatrix::RowIterator jac, LinearBufferAllocator workSpace) const \ - { \ - jacobianImpl(t, secIdx, colPos, y, y - offsetCp, offsetCp, jac, workSpace); \ - } \ - \ - virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - int offsetCp, linalg::DenseBandedRowIterator jac, LinearBufferAllocator workSpace) const \ - { \ - jacobianImpl(t, secIdx, colPos, y, y - offsetCp, offsetCp, jac, workSpace); \ - } +#define CADET_BINDINGMODEL_JACOBIAN_BOILERPLATE \ + virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ + int offsetCp, linalg::BandMatrix::RowIterator jac, LinearBufferAllocator workSpace) \ + const \ + { \ + jacobianImpl(t, secIdx, colPos, y, y - offsetCp, offsetCp, jac, workSpace); \ + } \ + \ + virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ + int offsetCp, linalg::DenseBandedRowIterator jac, LinearBufferAllocator workSpace) \ + const \ + { \ + jacobianImpl(t, secIdx, colPos, y, y - offsetCp, offsetCp, jac, workSpace); \ + } #endif -#endif // LIBCADET_BINDINGMODELMACROS_HPP_ +#endif // LIBCADET_BINDINGMODELMACROS_HPP_ diff --git a/src/libcadet/model/binding/ColloidalBinding.cpp b/src/libcadet/model/binding/ColloidalBinding.cpp index 94ead1237..f99a49917 100644 --- a/src/libcadet/model/binding/ColloidalBinding.cpp +++ b/src/libcadet/model/binding/ColloidalBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -36,17 +36,17 @@ { "type": "ScalarParameter", "varName": "kappaConst", "confName": "COL_KAPPA_CONST"}, { "type": "ScalarParameter", "varName": "cordNum", "confName": "COL_CORDNUM"}, { "type": "ScalarComponentDependentParameter", "varName": "kEqPhExp", "confName": "COL_LOGKEQ_PH_EXP"}, - { "type": "ScalarComponentDependentParameter", "varName": "kEqSaltPowerExp", "confName": "COL_LOGKEQ_SALT_POWEXP"}, - { "type": "ScalarComponentDependentParameter", "varName": "kEqSaltPowerFact", "confName": "COL_LOGKEQ_SALT_POWFACT"}, - { "type": "ScalarComponentDependentParameter", "varName": "kEqSaltExpFact", "confName": "COL_LOGKEQ_SALT_EXPFACT"}, - { "type": "ScalarComponentDependentParameter", "varName": "kEqSaltExpExp", "confName": "COL_LOGKEQ_SALT_EXPARGMULT"}, - { "type": "ScalarComponentDependentParameter", "varName": "bppPhExp", "confName": "COL_BPP_PH_EXP"}, - { "type": "ScalarComponentDependentParameter", "varName": "bppSaltPowerExp", "confName": "COL_BPP_SALT_POWEXP"}, - { "type": "ScalarComponentDependentParameter", "varName": "bppSaltPowerFact", "confName": "COL_BPP_SALT_POWFACT"}, - { "type": "ScalarComponentDependentParameter", "varName": "bppSaltExpFact", "confName": "COL_BPP_SALT_EXPFACT"}, - { "type": "ScalarComponentDependentParameter", "varName": "bppSaltExpExp", "confName": "COL_BPP_SALT_EXPARGMULT"}, - { "type": "ScalarComponentDependentParameter", "varName": "radius", "confName": "COL_PROTEIN_RADIUS"}, - { "type": "ScalarComponentDependentParameter", "varName": "kKin", "confName": "COL_KKIN"} + { "type": "ScalarComponentDependentParameter", "varName": "kEqSaltPowerExp", "confName": +"COL_LOGKEQ_SALT_POWEXP"}, { "type": "ScalarComponentDependentParameter", "varName": "kEqSaltPowerFact", "confName": +"COL_LOGKEQ_SALT_POWFACT"}, { "type": "ScalarComponentDependentParameter", "varName": "kEqSaltExpFact", "confName": +"COL_LOGKEQ_SALT_EXPFACT"}, { "type": "ScalarComponentDependentParameter", "varName": "kEqSaltExpExp", "confName": +"COL_LOGKEQ_SALT_EXPARGMULT"}, { "type": "ScalarComponentDependentParameter", "varName": "bppPhExp", "confName": +"COL_BPP_PH_EXP"}, { "type": "ScalarComponentDependentParameter", "varName": "bppSaltPowerExp", "confName": +"COL_BPP_SALT_POWEXP"}, { "type": "ScalarComponentDependentParameter", "varName": "bppSaltPowerFact", "confName": +"COL_BPP_SALT_POWFACT"}, { "type": "ScalarComponentDependentParameter", "varName": "bppSaltExpFact", "confName": +"COL_BPP_SALT_EXPFACT"}, { "type": "ScalarComponentDependentParameter", "varName": "bppSaltExpExp", "confName": +"COL_BPP_SALT_EXPARGMULT"}, { "type": "ScalarComponentDependentParameter", "varName": "radius", "confName": +"COL_PROTEIN_RADIUS"}, { "type": "ScalarComponentDependentParameter", "varName": "kKin", "confName": "COL_KKIN"} ], "constantParameters": [ @@ -69,84 +69,82 @@ namespace cadet namespace model { -inline const char* ColloidalParamHandler::identifier() CADET_NOEXCEPT { return "MULTI_COMPONENT_COLLOIDAL"; } +inline const char* ColloidalParamHandler::identifier() CADET_NOEXCEPT +{ + return "MULTI_COMPONENT_COLLOIDAL"; +} inline bool ColloidalParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kEqPhExp.size() != _kEqSaltPowerExp.size()) - || (_kEqPhExp.size() != _kEqSaltPowerFact.size()) - || (_kEqPhExp.size() != _kEqSaltExpFact.size()) - || (_kEqPhExp.size() != _kEqSaltExpExp.size()) - || (_kEqPhExp.size() != _radius.size()) - || (_kEqPhExp.size() != _kKin.size()) - || (_kEqPhExp.size() < nComp) - ) - throw InvalidParameterException("COL_LOGKEQ_PH_EXP, COL_LOGKEQ_SALT_POWEXP, COL_LOGKEQ_SALT_POWFACT, COL_LOGKEQ_SALT_EXPFACT, COL_LOGKEQ_SALT_EXPMULT, COL_PROTEIN_RADIUS, and COL_KKIN have to have the same size"); - - if ((_kEqPhExp.size() != _bppPhExp.size()) - || (_kEqPhExp.size() != _bppSaltPowerExp.size()) - || (_kEqPhExp.size() != _bppSaltPowerFact.size()) - || (_kEqPhExp.size() != _bppSaltExpFact.size()) - || (_kEqPhExp.size() != _bppSaltExpExp.size()) - || (_kEqPhExp.size() < nComp) - ) - throw InvalidParameterException("COL_LOGKEQ_PH_EXP, COL_BPP_PH_EXP, COL_BPP_SALT_POWEXP, COL_BPP_SALT_POWFACT, COL_BPP_SALT_EXPFACT, and COL_BPP_SALT_EXPMULT have to have the same size"); + if ((_kEqPhExp.size() != _kEqSaltPowerExp.size()) || (_kEqPhExp.size() != _kEqSaltPowerFact.size()) || + (_kEqPhExp.size() != _kEqSaltExpFact.size()) || (_kEqPhExp.size() != _kEqSaltExpExp.size()) || + (_kEqPhExp.size() != _radius.size()) || (_kEqPhExp.size() != _kKin.size()) || (_kEqPhExp.size() < nComp)) + throw InvalidParameterException( + "COL_LOGKEQ_PH_EXP, COL_LOGKEQ_SALT_POWEXP, COL_LOGKEQ_SALT_POWFACT, COL_LOGKEQ_SALT_EXPFACT, " + "COL_LOGKEQ_SALT_EXPMULT, COL_PROTEIN_RADIUS, and COL_KKIN have to have the same size"); + + if ((_kEqPhExp.size() != _bppPhExp.size()) || (_kEqPhExp.size() != _bppSaltPowerExp.size()) || + (_kEqPhExp.size() != _bppSaltPowerFact.size()) || (_kEqPhExp.size() != _bppSaltExpFact.size()) || + (_kEqPhExp.size() != _bppSaltExpExp.size()) || (_kEqPhExp.size() < nComp)) + throw InvalidParameterException("COL_LOGKEQ_PH_EXP, COL_BPP_PH_EXP, COL_BPP_SALT_POWEXP, COL_BPP_SALT_POWFACT, " + "COL_BPP_SALT_EXPFACT, and COL_BPP_SALT_EXPMULT have to have the same size"); return true; } -inline const char* ExtColloidalParamHandler::identifier() CADET_NOEXCEPT { return "EXT_MULTI_COMPONENT_COLLOIDAL"; } +inline const char* ExtColloidalParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_MULTI_COMPONENT_COLLOIDAL"; +} inline bool ExtColloidalParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kEqPhExp.size() != _kEqSaltPowerExp.size()) - || (_kEqPhExp.size() != _kEqSaltPowerFact.size()) - || (_kEqPhExp.size() != _kEqSaltExpFact.size()) - || (_kEqPhExp.size() != _kEqSaltExpExp.size()) - || (_kEqPhExp.size() != _radius.size()) - || (_kEqPhExp.size() != _kKin.size()) - || (_kEqPhExp.size() < nComp) - ) - throw InvalidParameterException("EXTCOL_LOGKEQ_PH_EXP, EXTCOL_LOGKEQ_SALT_POWEXP, EXTCOL_LOGKEQ_SALT_POWFACT, EXTCOL_LOGKEQ_SALT_EXPFACT, EXTCOL_LOGKEQ_SALT_EXPMULT, COL_PROTEIN_RADIUS, and EXTCOL_KKIN have to have the same size"); - - if ((_kEqPhExp.size() != _bppPhExp.size()) - || (_kEqPhExp.size() != _bppSaltPowerExp.size()) - || (_kEqPhExp.size() != _bppSaltPowerFact.size()) - || (_kEqPhExp.size() != _bppSaltExpFact.size()) - || (_kEqPhExp.size() != _bppSaltExpExp.size()) - || (_kEqPhExp.size() < nComp) - ) - throw InvalidParameterException("EXTCOL_LOGKEQ_PH_EXP, EXTCOL_BPP_PH_EXP, EXTCOL_BPP_SALT_POWEXP, EXTCOL_BPP_SALT_POWFACT, EXTCOL_BPP_SALT_EXPFACT, and EXTCOL_BPP_SALT_EXPMULT have to have the same size"); + if ((_kEqPhExp.size() != _kEqSaltPowerExp.size()) || (_kEqPhExp.size() != _kEqSaltPowerFact.size()) || + (_kEqPhExp.size() != _kEqSaltExpFact.size()) || (_kEqPhExp.size() != _kEqSaltExpExp.size()) || + (_kEqPhExp.size() != _radius.size()) || (_kEqPhExp.size() != _kKin.size()) || (_kEqPhExp.size() < nComp)) + throw InvalidParameterException( + "EXTCOL_LOGKEQ_PH_EXP, EXTCOL_LOGKEQ_SALT_POWEXP, EXTCOL_LOGKEQ_SALT_POWFACT, EXTCOL_LOGKEQ_SALT_EXPFACT, " + "EXTCOL_LOGKEQ_SALT_EXPMULT, COL_PROTEIN_RADIUS, and EXTCOL_KKIN have to have the same size"); + + if ((_kEqPhExp.size() != _bppPhExp.size()) || (_kEqPhExp.size() != _bppSaltPowerExp.size()) || + (_kEqPhExp.size() != _bppSaltPowerFact.size()) || (_kEqPhExp.size() != _bppSaltExpFact.size()) || + (_kEqPhExp.size() != _bppSaltExpExp.size()) || (_kEqPhExp.size() < nComp)) + throw InvalidParameterException( + "EXTCOL_LOGKEQ_PH_EXP, EXTCOL_BPP_PH_EXP, EXTCOL_BPP_SALT_POWEXP, EXTCOL_BPP_SALT_POWFACT, " + "EXTCOL_BPP_SALT_EXPFACT, and EXTCOL_BPP_SALT_EXPMULT have to have the same size"); return true; } - /** * @brief Defines the multi component colloidal binding model - * @details Implements the colloidal adsorption model: \f[ \begin{align} - * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} q_{\text{max},i} \left( 1 - \sum_j \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} q_i - * \end{align} \f] - * Multiple bound states are not supported. + * @details Implements the colloidal adsorption model: \f[ \begin{align} + * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} q_{\text{max},i} \left( 1 - \sum_j + * \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} q_i \end{align} \f] Multiple bound states are not supported. * Components without bound state (i.e., non-binding components) are supported. * The first component (index @c 0) is salt, the second component (index @c 1) * can be pH. Both components have to be non-binding. * @tparam ParamHandler_t Type that can add support for external function dependence */ -template -class ColloidalBindingBase : public ParamHandlerBindingModelBase +template class ColloidalBindingBase : public ParamHandlerBindingModelBase { public: - ColloidalBindingBase() : _startIdx(1), _rFactor(1.9174253548654209e-24) { // _rFactor: 1.9174253548654209e-24 = 2 / (N_Avogadro * sqrt(3)) } - virtual ~ColloidalBindingBase() CADET_NOEXCEPT { } + virtual ~ColloidalBindingBase() CADET_NOEXCEPT + { + } - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yCp, double const* y, double* dResDt, LinearBufferAllocator workSpace) const + virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yCp, double const* y, double* dResDt, + LinearBufferAllocator workSpace) const { if (!this->hasQuasiStationaryReactions()) return; @@ -168,11 +166,15 @@ class ColloidalBindingBase : public ParamHandlerBindingModelBase int _startIdx; const double _rFactor; - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return false; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return false; + } virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) { - const bool res = ParamHandlerBindingModelBase::configureImpl(paramProvider, unitOpIdx, parTypeIdx); + const bool res = + ParamHandlerBindingModelBase::configureImpl(paramProvider, unitOpIdx, parTypeIdx); if (_nComp <= 1) throw InvalidParameterException("No protein component present"); @@ -201,12 +203,13 @@ class ColloidalBindingBase : public ParamHandlerBindingModelBase template int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { using CpStateParamType = typename DoubleActivePromoter::type; using StateParamType = typename DoubleActivePromoter::type; - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); ResidualType qSum = 0.0; int bndIdx = 0; @@ -234,7 +237,10 @@ class ColloidalBindingBase : public ParamHandlerBindingModelBase if (_nBoundStates[i] == 0) continue; - StateParamType logKeq = pow(cpSalt, -static_cast(p->kEqSaltPowerExp[i])) * static_cast(p->kEqSaltPowerFact[i]) + exp(cpSalt * static_cast(p->kEqSaltExpExp[i])) * static_cast(p->kEqSaltExpFact[i]); + StateParamType logKeq = pow(cpSalt, -static_cast(p->kEqSaltPowerExp[i])) * + static_cast(p->kEqSaltPowerFact[i]) + + exp(cpSalt * static_cast(p->kEqSaltExpExp[i])) * + static_cast(p->kEqSaltExpFact[i]); if (phEnabled) { @@ -251,7 +257,9 @@ class ColloidalBindingBase : public ParamHandlerBindingModelBase } else { - const CpStateParamType kappa = 1 / (pow(cpSalt, static_cast(p->kappaExp)) * static_cast(p->kappaFact) + static_cast(p->kappaConst)); + const CpStateParamType kappa = + 1 / (pow(cpSalt, static_cast(p->kappaExp)) * static_cast(p->kappaFact) + + static_cast(p->kappaConst)); const StateParamType R = sqrt(_rFactor * static_cast(p->phi) / qSum); const StateParamType Sfactor = static_cast(p->cordNum) * 0.125 / qSum * (3.0 / R + kappa); @@ -262,8 +270,14 @@ class ColloidalBindingBase : public ParamHandlerBindingModelBase if (_nBoundStates[i] == 0) continue; - StateParamType logKeq = pow(cpSalt, -static_cast(p->kEqSaltPowerExp[i])) * static_cast(p->kEqSaltPowerFact[i]) + exp(cpSalt * static_cast(p->kEqSaltExpExp[i])) * static_cast(p->kEqSaltExpFact[i]); - StateParamType bpp_i = pow(cpSalt, static_cast(p->bppSaltPowerExp[i])) * static_cast(p->bppSaltPowerFact[i]) + exp(cpSalt * static_cast(p->bppSaltExpExp[i])) * static_cast(p->bppSaltExpFact[i]); + StateParamType logKeq = pow(cpSalt, -static_cast(p->kEqSaltPowerExp[i])) * + static_cast(p->kEqSaltPowerFact[i]) + + exp(cpSalt * static_cast(p->kEqSaltExpExp[i])) * + static_cast(p->kEqSaltExpFact[i]); + StateParamType bpp_i = pow(cpSalt, static_cast(p->bppSaltPowerExp[i])) * + static_cast(p->bppSaltPowerFact[i]) + + exp(cpSalt * static_cast(p->bppSaltExpExp[i])) * + static_cast(p->bppSaltExpFact[i]); if (phEnabled) { @@ -281,14 +295,18 @@ class ColloidalBindingBase : public ParamHandlerBindingModelBase if (_nBoundStates[j] == 0) continue; - StateParamType bpp_j = pow(cpSalt, static_cast(p->bppSaltPowerExp[j])) * static_cast(p->bppSaltPowerFact[j]) + exp(cpSalt * static_cast(p->bppSaltExpExp[j])) * static_cast(p->bppSaltExpFact[j]); + StateParamType bpp_j = pow(cpSalt, static_cast(p->bppSaltPowerExp[j])) * + static_cast(p->bppSaltPowerFact[j]) + + exp(cpSalt * static_cast(p->bppSaltExpExp[j])) * + static_cast(p->bppSaltExpFact[j]); if (phEnabled) { const CpStateType& pH = yCp[1]; bpp_j *= pow(pH, static_cast(p->bppPhExp[j])); } - const ParamType radSum = static_cast(p->radius[i]) + static_cast(p->radius[j]); + const ParamType radSum = + static_cast(p->radius[i]) + static_cast(p->radius[j]); S_i += y[bndIdx2] * sqrt(bpp_i * bpp_j) * radSum * exp(-kappa * (R - radSum)); // Next bound component @@ -307,9 +325,11 @@ class ColloidalBindingBase : public ParamHandlerBindingModelBase } template - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); double qSum = 0.0; int bndIdx = 0; @@ -337,14 +357,23 @@ class ColloidalBindingBase : public ParamHandlerBindingModelBase if (_nBoundStates[i] == 0) continue; - double kKinKeq = pow(cpSalt, -static_cast(p->kEqSaltPowerExp[i])) * static_cast(p->kEqSaltPowerFact[i]) + exp(cpSalt * static_cast(p->kEqSaltExpExp[i])) * static_cast(p->kEqSaltExpFact[i]); - double logKeq_dSalt = -static_cast(p->kEqSaltPowerExp[i]) * pow(cpSalt, -static_cast(p->kEqSaltPowerExp[i]) - 1.0) * static_cast(p->kEqSaltPowerFact[i]) + exp(cpSalt * static_cast(p->kEqSaltExpExp[i])) * static_cast(p->kEqSaltExpFact[i]) * static_cast(p->kEqSaltExpExp[i]); + double kKinKeq = + pow(cpSalt, -static_cast(p->kEqSaltPowerExp[i])) * + static_cast(p->kEqSaltPowerFact[i]) + + exp(cpSalt * static_cast(p->kEqSaltExpExp[i])) * static_cast(p->kEqSaltExpFact[i]); + double logKeq_dSalt = -static_cast(p->kEqSaltPowerExp[i]) * + pow(cpSalt, -static_cast(p->kEqSaltPowerExp[i]) - 1.0) * + static_cast(p->kEqSaltPowerFact[i]) + + exp(cpSalt * static_cast(p->kEqSaltExpExp[i])) * + static_cast(p->kEqSaltExpFact[i]) * + static_cast(p->kEqSaltExpExp[i]); if (phEnabled) { const double pH = yCp[1]; const double pHfactor = pow(pH, static_cast(p->kEqPhExp[i])); - const double logKeq_dPh = pow(pH, static_cast(p->kEqPhExp[i]) - 1.0) * static_cast(p->kEqPhExp[i]); + const double logKeq_dPh = + pow(pH, static_cast(p->kEqPhExp[i]) - 1.0) * static_cast(p->kEqPhExp[i]); kKinKeq *= pHfactor; logKeq_dSalt *= pHfactor; @@ -372,14 +401,18 @@ class ColloidalBindingBase : public ParamHandlerBindingModelBase } else { - const double kappa_denom = pow(cpSalt, static_cast(p->kappaExp)) * static_cast(p->kappaFact) + static_cast(p->kappaConst); + const double kappa_denom = + pow(cpSalt, static_cast(p->kappaExp)) * static_cast(p->kappaFact) + + static_cast(p->kappaConst); const double kappa = 1 / kappa_denom; - const double kappa_dSalt = -1 / sqr(kappa_denom) * pow(cpSalt, static_cast(p->kappaExp) - 1.0) * static_cast(p->kappaExp) * static_cast(p->kappaFact); + const double kappa_dSalt = -1 / sqr(kappa_denom) * pow(cpSalt, static_cast(p->kappaExp) - 1.0) * + static_cast(p->kappaExp) * static_cast(p->kappaFact); const double R = sqrt(_rFactor * static_cast(p->phi) / qSum); const double R_dq = -0.5 * R / qSum; const double Sfactor = static_cast(p->cordNum) * 0.125 / qSum * (3.0 / R + kappa); const double Sfactor_dSalt = static_cast(p->cordNum) * 0.125 / qSum * kappa_dSalt; - const double Sfactor_dq = -static_cast(p->cordNum) * 0.125 * ((3.0 / R + kappa) / qSum + 3.0 / sqr(R) * R_dq) / qSum; + const double Sfactor_dq = + -static_cast(p->cordNum) * 0.125 * ((3.0 / R + kappa) / qSum + 3.0 / sqr(R) * R_dq) / qSum; bndIdx = 0; for (int i = _startIdx; i < _nComp; ++i) @@ -391,21 +424,37 @@ class ColloidalBindingBase : public ParamHandlerBindingModelBase // dres_i / dc_{p,i} jac[i - bndIdx - offsetCp] = -static_cast(p->kKin[i]); - double logKeq = pow(cpSalt, -static_cast(p->kEqSaltPowerExp[i])) * static_cast(p->kEqSaltPowerFact[i]) + exp(cpSalt * static_cast(p->kEqSaltExpExp[i])) * static_cast(p->kEqSaltExpFact[i]); - double logKeq_dSalt = -static_cast(p->kEqSaltPowerExp[i]) * pow(cpSalt, -static_cast(p->kEqSaltPowerExp[i]) - 1.0) * static_cast(p->kEqSaltPowerFact[i]) + exp(cpSalt * static_cast(p->kEqSaltExpExp[i])) * static_cast(p->kEqSaltExpFact[i]) * static_cast(p->kEqSaltExpExp[i]); + double logKeq = + pow(cpSalt, -static_cast(p->kEqSaltPowerExp[i])) * + static_cast(p->kEqSaltPowerFact[i]) + + exp(cpSalt * static_cast(p->kEqSaltExpExp[i])) * static_cast(p->kEqSaltExpFact[i]); + double logKeq_dSalt = -static_cast(p->kEqSaltPowerExp[i]) * + pow(cpSalt, -static_cast(p->kEqSaltPowerExp[i]) - 1.0) * + static_cast(p->kEqSaltPowerFact[i]) + + exp(cpSalt * static_cast(p->kEqSaltExpExp[i])) * + static_cast(p->kEqSaltExpFact[i]) * + static_cast(p->kEqSaltExpExp[i]); double logKeq_dPh = 0.0; if (phEnabled) { const double pH = yCp[1]; - logKeq_dPh = pow(pH, static_cast(p->kEqPhExp[i]) - 1.0) * static_cast(p->kEqPhExp[i]) * logKeq; + logKeq_dPh = pow(pH, static_cast(p->kEqPhExp[i]) - 1.0) * + static_cast(p->kEqPhExp[i]) * logKeq; const double pHfactor = pow(pH, static_cast(p->kEqPhExp[i])); logKeq *= pHfactor; logKeq_dSalt *= pHfactor; } - double bpp_i = pow(cpSalt, static_cast(p->bppSaltPowerExp[i])) * static_cast(p->bppSaltPowerFact[i]) + exp(cpSalt * static_cast(p->bppSaltExpExp[i])) * static_cast(p->bppSaltExpFact[i]); - double bpp_i_dSalt = pow(cpSalt, static_cast(p->bppSaltPowerExp[i]) - 1.0) * static_cast(p->bppSaltPowerExp[i]) * static_cast(p->bppSaltPowerFact[i]) + exp(cpSalt * static_cast(p->bppSaltExpExp[i])) * static_cast(p->bppSaltExpFact[i]) * static_cast(p->bppSaltExpExp[i]); + double bpp_i = + pow(cpSalt, static_cast(p->bppSaltPowerExp[i])) * + static_cast(p->bppSaltPowerFact[i]) + + exp(cpSalt * static_cast(p->bppSaltExpExp[i])) * static_cast(p->bppSaltExpFact[i]); + double bpp_i_dSalt = + pow(cpSalt, static_cast(p->bppSaltPowerExp[i]) - 1.0) * + static_cast(p->bppSaltPowerExp[i]) * static_cast(p->bppSaltPowerFact[i]) + + exp(cpSalt * static_cast(p->bppSaltExpExp[i])) * static_cast(p->bppSaltExpFact[i]) * + static_cast(p->bppSaltExpExp[i]); double bpp_i_dPh = bpp_i; if (phEnabled) { @@ -413,7 +462,8 @@ class ColloidalBindingBase : public ParamHandlerBindingModelBase const double phFactor = pow(pH, static_cast(p->bppPhExp[i])); bpp_i *= phFactor; bpp_i_dSalt *= phFactor; - bpp_i_dPh *= pow(pH, static_cast(p->bppPhExp[i]) - 1.0) * static_cast(p->bppPhExp[i]); + bpp_i_dPh *= + pow(pH, static_cast(p->bppPhExp[i]) - 1.0) * static_cast(p->bppPhExp[i]); } double S_i_sum = 0.0; @@ -426,8 +476,15 @@ class ColloidalBindingBase : public ParamHandlerBindingModelBase if (_nBoundStates[j] == 0) continue; - double bpp_j = pow(cpSalt, static_cast(p->bppSaltPowerExp[j])) * static_cast(p->bppSaltPowerFact[j]) + exp(cpSalt * static_cast(p->bppSaltExpExp[j])) * static_cast(p->bppSaltExpFact[j]); - double bpp_j_dSalt = pow(cpSalt, static_cast(p->bppSaltPowerExp[j]) - 1.0) * static_cast(p->bppSaltPowerExp[j]) * static_cast(p->bppSaltPowerFact[j]) + exp(cpSalt * static_cast(p->bppSaltExpExp[j])) * static_cast(p->bppSaltExpFact[j]) * static_cast(p->bppSaltExpExp[j]); + double bpp_j = pow(cpSalt, static_cast(p->bppSaltPowerExp[j])) * + static_cast(p->bppSaltPowerFact[j]) + + exp(cpSalt * static_cast(p->bppSaltExpExp[j])) * + static_cast(p->bppSaltExpFact[j]); + double bpp_j_dSalt = + pow(cpSalt, static_cast(p->bppSaltPowerExp[j]) - 1.0) * + static_cast(p->bppSaltPowerExp[j]) * static_cast(p->bppSaltPowerFact[j]) + + exp(cpSalt * static_cast(p->bppSaltExpExp[j])) * + static_cast(p->bppSaltExpFact[j]) * static_cast(p->bppSaltExpExp[j]); double bpp_j_dPh = bpp_j; if (phEnabled) { @@ -435,7 +492,8 @@ class ColloidalBindingBase : public ParamHandlerBindingModelBase const double phFactor = pow(pH, static_cast(p->bppPhExp[j])); bpp_j *= phFactor; bpp_j_dSalt *= phFactor; - bpp_j_dPh *= pow(pH, static_cast(p->bppPhExp[j]) - 1.0) * static_cast(p->bppPhExp[j]); + bpp_j_dPh *= + pow(pH, static_cast(p->bppPhExp[j]) - 1.0) * static_cast(p->bppPhExp[j]); } const double sqrtBpp_ij = sqrt(bpp_i * bpp_j); @@ -473,7 +531,10 @@ class ColloidalBindingBase : public ParamHandlerBindingModelBase if (_nBoundStates[j] == 0) continue; - double bpp_j = pow(cpSalt, static_cast(p->bppSaltPowerExp[j])) * static_cast(p->bppSaltPowerFact[j]) + exp(cpSalt * static_cast(p->bppSaltExpExp[j])) * static_cast(p->bppSaltExpFact[j]); + double bpp_j = pow(cpSalt, static_cast(p->bppSaltPowerExp[j])) * + static_cast(p->bppSaltPowerFact[j]) + + exp(cpSalt * static_cast(p->bppSaltExpExp[j])) * + static_cast(p->bppSaltExpFact[j]); if (phEnabled) { const double pH = yCp[1]; @@ -501,7 +562,7 @@ class ColloidalBindingBase : public ParamHandlerBindingModelBase ++jac; } } - } + } }; typedef ColloidalBindingBase ColloidalBinding; @@ -509,13 +570,13 @@ typedef ColloidalBindingBase ExternalColloidalBinding; namespace binding { - void registerColloidalModel(std::unordered_map>& bindings) - { - bindings[ColloidalBinding::identifier()] = []() { return new ColloidalBinding(); }; - bindings[ExternalColloidalBinding::identifier()] = []() { return new ExternalColloidalBinding(); }; - } -} // namespace binding +void registerColloidalModel(std::unordered_map>& bindings) +{ + bindings[ColloidalBinding::identifier()] = []() { return new ColloidalBinding(); }; + bindings[ExternalColloidalBinding::identifier()] = []() { return new ExternalColloidalBinding(); }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/DummyBinding.cpp b/src/libcadet/model/binding/DummyBinding.cpp index 04f594fb7..52136e786 100644 --- a/src/libcadet/model/binding/DummyBinding.cpp +++ b/src/libcadet/model/binding/DummyBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -30,16 +30,32 @@ namespace model class DummyBinding : public IBindingModel { public: + DummyBinding() : _stateQuasistationarity(0, false) + { + } + virtual ~DummyBinding() CADET_NOEXCEPT + { + } - DummyBinding() : _stateQuasistationarity(0, false) { } - virtual ~DummyBinding() CADET_NOEXCEPT { } - - static const char* identifier() { return "NONE"; } - virtual const char* name() const CADET_NOEXCEPT { return "NONE"; } - virtual bool requiresConfiguration() const CADET_NOEXCEPT { return false; } - virtual bool usesParamProviderInDiscretizationConfig() const CADET_NOEXCEPT { return false; } + static const char* identifier() + { + return "NONE"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return "NONE"; + } + virtual bool requiresConfiguration() const CADET_NOEXCEPT + { + return false; + } + virtual bool usesParamProviderInDiscretizationConfig() const CADET_NOEXCEPT + { + return false; + } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { _nComp = nComp; _nBoundStates = nBound; @@ -52,13 +68,15 @@ class DummyBinding : public IBindingModel return true; } - virtual void fillBoundPhaseInitialParameters(ParameterId* params, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) const CADET_NOEXCEPT + virtual void fillBoundPhaseInitialParameters(ParameterId* params, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx) const CADET_NOEXCEPT { unsigned int ctr = 0; for (int c = 0; c < _nComp; ++c) { for (unsigned int bp = 0; bp < _nBoundStates[c]; ++bp, ++ctr) - params[ctr] = makeParamId(hashString("INIT_Q"), unitOpIdx, c, parTypeIdx, bp, ReactionIndep, SectionIndep); + params[ctr] = + makeParamId(hashString("INIT_Q"), unitOpIdx, c, parTypeIdx, bp, ReactionIndep, SectionIndep); } } @@ -92,83 +110,134 @@ class DummyBinding : public IBindingModel return nullptr; } - virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT + virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT { return 0; } - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, - active const* y, active const* yCp, active* res, LinearBufferAllocator workSpace, WithParamSensitivity) const + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, active const* yCp, + active* res, LinearBufferAllocator workSpace, WithParamSensitivity) const { return 0; } - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, - active const* y, active const* yCp, active* res, LinearBufferAllocator workSpace, WithoutParamSensitivity) const + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, active const* yCp, + active* res, LinearBufferAllocator workSpace, WithoutParamSensitivity) const { return 0; } - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, - double const* y, double const* yCp, active* res, LinearBufferAllocator workSpace) const + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + active* res, LinearBufferAllocator workSpace) const { return 0; } - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, - double const* y, double const* yCp, double* res, LinearBufferAllocator workSpace) const + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + double* res, LinearBufferAllocator workSpace) const { return 0; } - virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) { } + virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) + { + } - virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, int offsetCp, linalg::BandMatrix::RowIterator jac, LinearBufferAllocator workSpace) const + virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + int offsetCp, linalg::BandMatrix::RowIterator jac, + LinearBufferAllocator workSpace) const { } - virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, int offsetCp, linalg::DenseBandedRowIterator jac, LinearBufferAllocator workSpace) const + virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + int offsetCp, linalg::DenseBandedRowIterator jac, + LinearBufferAllocator workSpace) const { } #ifdef ENABLE_DG - virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, int offsetCp, linalg::BandedEigenSparseRowIterator jac, LinearBufferAllocator workSpace) const + virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + int offsetCp, linalg::BandedEigenSparseRowIterator jac, + LinearBufferAllocator workSpace) const { } #endif - virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yCp, double const* y, double* dResDt, LinearBufferAllocator workSpace) const { } + virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yCp, double const* y, double* dResDt, + LinearBufferAllocator workSpace) const + { + } - virtual bool hasSalt() const CADET_NOEXCEPT { return false; } - virtual bool supportsMultistate() const CADET_NOEXCEPT { return true; } - virtual bool supportsNonBinding() const CADET_NOEXCEPT { return true; } - virtual bool hasQuasiStationaryReactions() const CADET_NOEXCEPT { return false; } - virtual bool hasDynamicReactions() const CADET_NOEXCEPT { return true; } - virtual bool dependsOnTime() const CADET_NOEXCEPT { return false; } - virtual bool requiresWorkspace() const CADET_NOEXCEPT { return false; } - virtual int const* reactionQuasiStationarity() const CADET_NOEXCEPT { return _stateQuasistationarity.data(); } - virtual unsigned int requiredADdirs() const CADET_NOEXCEPT { return 0; } + virtual bool hasSalt() const CADET_NOEXCEPT + { + return false; + } + virtual bool supportsMultistate() const CADET_NOEXCEPT + { + return true; + } + virtual bool supportsNonBinding() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasQuasiStationaryReactions() const CADET_NOEXCEPT + { + return false; + } + virtual bool hasDynamicReactions() const CADET_NOEXCEPT + { + return true; + } + virtual bool dependsOnTime() const CADET_NOEXCEPT + { + return false; + } + virtual bool requiresWorkspace() const CADET_NOEXCEPT + { + return false; + } + virtual int const* reactionQuasiStationarity() const CADET_NOEXCEPT + { + return _stateQuasistationarity.data(); + } + virtual unsigned int requiredADdirs() const CADET_NOEXCEPT + { + return 0; + } - virtual bool preConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const { return false; } - virtual void postConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const { } + virtual bool preConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, + double const* yCp, LinearBufferAllocator workSpace) const + { + return false; + } + virtual void postConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, + double const* yCp, LinearBufferAllocator workSpace) const + { + } protected: - int _nComp; //!< Number of components - unsigned int const* _nBoundStates; //!< Array with number of bound states for each component - std::vector _stateQuasistationarity; //!< Determines whether each bound state is quasi-stationary (@c true) or not (@c false) + int _nComp; //!< Number of components + unsigned int const* _nBoundStates; //!< Array with number of bound states for each component + std::vector _stateQuasistationarity; //!< Determines whether each bound state is quasi-stationary (@c true) or + //!< not (@c false) - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } }; namespace binding { - void registerDummyModel(std::unordered_map>& bindings) - { - bindings[DummyBinding::identifier()] = []() { return new DummyBinding(); }; - bindings["DUMMY"] = []() { return new DummyBinding(); }; - } -} // namespace binding +void registerDummyModel(std::unordered_map>& bindings) +{ + bindings[DummyBinding::identifier()] = []() { return new DummyBinding(); }; + bindings["DUMMY"] = []() { return new DummyBinding(); }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/ExtendedMobilePhaseModulatorLangmuirBinding.cpp b/src/libcadet/model/binding/ExtendedMobilePhaseModulatorLangmuirBinding.cpp index 77139bd89..0e9516cd7 100644 --- a/src/libcadet/model/binding/ExtendedMobilePhaseModulatorLangmuirBinding.cpp +++ b/src/libcadet/model/binding/ExtendedMobilePhaseModulatorLangmuirBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -53,42 +53,49 @@ namespace cadet namespace model { -inline const char* EMPMLangmuirParamHandler::identifier() CADET_NOEXCEPT { return "EXTENDED_MOBILE_PHASE_MODULATOR"; } +inline const char* EMPMLangmuirParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXTENDED_MOBILE_PHASE_MODULATOR"; +} inline bool EMPMLangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() != _gamma.size()) - || (_kA.size() != _beta.size()) || (_kA.size() < nComp)) - throw InvalidParameterException("EMPM_KA, EMPM_KD, EMPM_QMAX, EMPM_GAMMA, and EMPM_BETA have to have the same size"); + if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() != _gamma.size()) || + (_kA.size() != _beta.size()) || (_kA.size() < nComp)) + throw InvalidParameterException( + "EMPM_KA, EMPM_KD, EMPM_QMAX, EMPM_GAMMA, and EMPM_BETA have to have the same size"); return true; } -inline const char* ExtEMPMLangmuirParamHandler::identifier() CADET_NOEXCEPT { return "EXT_EXTENDED_MOBILE_PHASE_MODULATOR"; } +inline const char* ExtEMPMLangmuirParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_EXTENDED_MOBILE_PHASE_MODULATOR"; +} inline bool ExtEMPMLangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() != _gamma.size()) - || (_kA.size() != _beta.size()) || (_kA.size() < nComp)) - throw InvalidParameterException("EMPM_KA, EMPM_KD, EMPM_QMAX, EMPM_GAMMA, and EMPM_BETA have to have the same size"); + if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() != _gamma.size()) || + (_kA.size() != _beta.size()) || (_kA.size() < nComp)) + throw InvalidParameterException( + "EMPM_KA, EMPM_KD, EMPM_QMAX, EMPM_GAMMA, and EMPM_BETA have to have the same size"); return true; } - /** * @brief Defines the mobile phase modulator Langmuir binding model - * @details Implements the mobile phase modulator Langmuir adsorption model: \f[ \begin{align} + * @details Implements the mobile phase modulator Langmuir adsorption model: \f[ \begin{align} * \frac{\mathrm{d}q_0}{\mathrm{d}t} &= 0 \\ - * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} e^{\gamma_i c_{p,0}} q_{\text{max},i} \left( 1 - \sum_j \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} c_{p,0}^{\beta_i} q_i - * \end{align} \f] - * While @f$ \gamma @f$ describes hydrophobicity, @f$ \beta @f$ accounts for ion-exchange characteristics. - * Multiple bound states are not supported. Component @c 0 is assumed to be salt, which is also assumed to be inert. - * Components without bound state (i.e., non-binding components) are supported. - * + * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} e^{\gamma_i c_{p,0}} q_{\text{max},i} \left( 1 - + * \sum_j \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} c_{p,0}^{\beta_i} q_i \end{align} \f] While @f$ \gamma @f$ + * describes hydrophobicity, @f$ \beta @f$ accounts for ion-exchange characteristics. Multiple bound states are not + * supported. Component @c 0 is assumed to be salt, which is also assumed to be inert. Components without bound state + * (i.e., non-binding components) are supported. + * * Note that the first flux is only used if salt (component @c 0) has a bound state. * It is reasonable to set the number of bound states for salt to @c 0 in order to save time and memory. - * + * * See @cite Melander1989 and @cite Karlsson2004. * @tparam ParamHandler_t Type that can add support for external function dependence */ @@ -96,21 +103,32 @@ template class ExtendedMobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindingModelBase { public: + ExtendedMobilePhaseModulatorLangmuirBindingBase() + { + } + virtual ~ExtendedMobilePhaseModulatorLangmuirBindingBase() CADET_NOEXCEPT + { + } - ExtendedMobilePhaseModulatorLangmuirBindingBase() { } - virtual ~ExtendedMobilePhaseModulatorLangmuirBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - virtual bool hasSalt() const CADET_NOEXCEPT { return true; } + virtual bool hasSalt() const CADET_NOEXCEPT + { + return true; + } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { const bool res = BindingModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); _mode = paramProvider.getIntArray("EMPM_COMP_MODE"); if (_mode.size() < nComp) - throw InvalidParameterException("Not enough elements in EMPM_COMP_MODE (expected " + std::to_string(nComp) + ", got " + std::to_string(_mode.size()) + ")"); + throw InvalidParameterException("Not enough elements in EMPM_COMP_MODE (expected " + std::to_string(nComp) + + ", got " + std::to_string(_mode.size()) + ")"); _idxModifier = -1; for (int i = 0; i < static_cast(_mode.size()); ++i) @@ -118,11 +136,14 @@ class ExtendedMobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindi if (_mode[i] == static_cast(CompMode::Modifier)) { if (_idxModifier >= 0) - throw InvalidParameterException("EMPM: Component " + std::to_string(_idxModifier) + " already set as modifier (requested comp " + std::to_string(i) + ")"); + throw InvalidParameterException("EMPM: Component " + std::to_string(_idxModifier) + + " already set as modifier (requested comp " + std::to_string(i) + + ")"); _idxModifier = i; } - else if ((_mode[i] != static_cast(CompMode::Linear)) && (_mode[i] != static_cast(CompMode::Langmuir))) + else if ((_mode[i] != static_cast(CompMode::Linear)) && + (_mode[i] != static_cast(CompMode::Langmuir))) throw InvalidParameterException("Unknown EMPM_COMP_MODE for component " + std::to_string(i)); } @@ -152,18 +173,23 @@ class ExtendedMobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindi }; std::vector _mode; //!< Mode of each component (e.g., linear or modified Langmuir) - int _idxModifier; //!< Index of the modifier component + int _idxModifier; //!< Index of the modifier component - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } template int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Salt flux: 0 - // Protein fluxes: -k_{a,i} * exp(\gamma_i * c_{p,0}) * c_{p,i} * q_{max,i} * (1 - \sum q_i / q_{max,i}) + k_{d,i} * c_{p,0}^\beta_i * q_i) + // Protein fluxes: -k_{a,i} * exp(\gamma_i * c_{p,0}) * c_{p,i} * q_{max,i} * (1 - \sum q_i / q_{max,i}) + + // k_{d,i} * c_{p,0}^\beta_i * q_i) ResidualType qSum = 1.0; int bndIdx = 0; @@ -200,9 +226,14 @@ class ExtendedMobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindi else if (_mode[i] == static_cast(CompMode::Langmuir)) { if (_idxModifier >= 0) - res[bndIdx] = static_cast(p->kD[i]) * pow(yCp[_idxModifier], static_cast(p->beta[i])) * y[bndIdx] - static_cast(p->kA[i]) * exp(yCp[_idxModifier] * static_cast(p->gamma[i])) * yCp[i] * static_cast(p->qMax[i]) * qSum; + res[bndIdx] = static_cast(p->kD[i]) * + pow(yCp[_idxModifier], static_cast(p->beta[i])) * y[bndIdx] - + static_cast(p->kA[i]) * + exp(yCp[_idxModifier] * static_cast(p->gamma[i])) * yCp[i] * + static_cast(p->qMax[i]) * qSum; else - res[bndIdx] = static_cast(p->kD[i]) * y[bndIdx] - static_cast(p->kA[i]) * yCp[i] * static_cast(p->qMax[i]) * qSum; + res[bndIdx] = static_cast(p->kD[i]) * y[bndIdx] - + static_cast(p->kA[i]) * yCp[i] * static_cast(p->qMax[i]) * qSum; } else if (_mode[i] == static_cast(CompMode::Modifier)) res[bndIdx] = 0.0; @@ -215,9 +246,11 @@ class ExtendedMobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindi } template - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); double qSum = 1.0; int bndIdx = 0; @@ -240,7 +273,8 @@ class ExtendedMobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindi ++bndIdx; } - // Protein fluxes: -k_{a,i} * exp(\gamma_i * c_{p,0}) * c_{p,i} * q_{max,i} * (1 - \sum q_i / q_{max,i}) + k_{d,i} * c_{p,0}^\beta_i * q_i) + // Protein fluxes: -k_{a,i} * exp(\gamma_i * c_{p,0}) * c_{p,i} * q_{max,i} * (1 - \sum q_i / q_{max,i}) + + // k_{d,i} * c_{p,0}^\beta_i * q_i) bndIdx = 0; for (int i = 0; i < _nComp; ++i) { @@ -260,7 +294,8 @@ class ExtendedMobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindi const double gamma = static_cast(p->gamma[i]); const double beta = static_cast(p->beta[i]); const double qMax = static_cast(p->qMax[i]); - const double ka = (_idxModifier >= 0) ? static_cast(p->kA[i]) * exp(gamma * yCp[_idxModifier]) : static_cast(p->kA[i]); + const double ka = (_idxModifier >= 0) ? static_cast(p->kA[i]) * exp(gamma * yCp[_idxModifier]) + : static_cast(p->kA[i]); const double kdRaw = static_cast(p->kD[i]); if (_mode[i] == static_cast(CompMode::Linear)) @@ -297,7 +332,8 @@ class ExtendedMobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindi // dres_i / dq_j jac[bndIdx2 - bndIdx] = ka * yCp[i] * qMax / static_cast(p->qMax[j]); - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - + // bndIdx] corresponds to q_j. ++bndIdx2; } @@ -305,8 +341,11 @@ class ExtendedMobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindi if (_idxModifier >= 0) { // dres_i / dc_{p,mod} - jac[-bndIdx - offsetCp + _idxModifier] = -ka * yCp[i] * qMax * qSum * gamma + kdRaw * beta * y[bndIdx] * pow(yCp[_idxModifier], beta - 1.0); - // Getting to c_{p,mod}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0}, and +_idxModifier to c_{p,mod}. + jac[-bndIdx - offsetCp + _idxModifier] = + -ka * yCp[i] * qMax * qSum * gamma + + kdRaw * beta * y[bndIdx] * pow(yCp[_idxModifier], beta - 1.0); + // Getting to c_{p,mod}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0}, and +_idxModifier to + // c_{p,mod}. // This means jac[bndIdx - offsetCp + _idxModifier] corresponds to c_{p,mod}. // Add to dres_i / dq_i @@ -314,7 +353,6 @@ class ExtendedMobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindi } else jac[0] += kdRaw; - } // Advance to next flux and Jacobian row @@ -324,19 +362,25 @@ class ExtendedMobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindi } }; - -typedef ExtendedMobilePhaseModulatorLangmuirBindingBase ExtendedMobilePhaseModulatorLangmuirBinding; -typedef ExtendedMobilePhaseModulatorLangmuirBindingBase ExternalExtendedMobilePhaseModulatorLangmuirBinding; +typedef ExtendedMobilePhaseModulatorLangmuirBindingBase + ExtendedMobilePhaseModulatorLangmuirBinding; +typedef ExtendedMobilePhaseModulatorLangmuirBindingBase + ExternalExtendedMobilePhaseModulatorLangmuirBinding; namespace binding { - void registerExtendedMobilePhaseModulatorLangmuirModel(std::unordered_map>& bindings) - { - bindings[ExtendedMobilePhaseModulatorLangmuirBinding::identifier()] = []() { return new ExtendedMobilePhaseModulatorLangmuirBinding(); }; - bindings[ExternalExtendedMobilePhaseModulatorLangmuirBinding::identifier()] = []() { return new ExternalExtendedMobilePhaseModulatorLangmuirBinding(); }; - } -} // namespace binding +void registerExtendedMobilePhaseModulatorLangmuirModel( + std::unordered_map>& bindings) +{ + bindings[ExtendedMobilePhaseModulatorLangmuirBinding::identifier()] = []() { + return new ExtendedMobilePhaseModulatorLangmuirBinding(); + }; + bindings[ExternalExtendedMobilePhaseModulatorLangmuirBinding::identifier()] = []() { + return new ExternalExtendedMobilePhaseModulatorLangmuirBinding(); + }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/FreundlichLDFBinding.cpp b/src/libcadet/model/binding/FreundlichLDFBinding.cpp index 9e7d6c67f..8c089d98b 100644 --- a/src/libcadet/model/binding/FreundlichLDFBinding.cpp +++ b/src/libcadet/model/binding/FreundlichLDFBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -45,154 +45,172 @@ namespace cadet { - namespace model - { +namespace model +{ - inline const char* FreundlichLDFParamHandler::identifier() CADET_NOEXCEPT { return "FREUNDLICH_LDF"; } +inline const char* FreundlichLDFParamHandler::identifier() CADET_NOEXCEPT +{ + return "FREUNDLICH_LDF"; +} - inline bool FreundlichLDFParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) - { - if ((_kkin.size() != _kF.size()) || (_kkin.size() != _n.size()) || (_kkin.size() < nComp)) - throw InvalidParameterException("FLDF_KKIN, FLDF_KF and FLDF_N, have to have the same size"); +inline bool FreundlichLDFParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) +{ + if ((_kkin.size() != _kF.size()) || (_kkin.size() != _n.size()) || (_kkin.size() < nComp)) + throw InvalidParameterException("FLDF_KKIN, FLDF_KF and FLDF_N, have to have the same size"); - return true; - } + return true; +} - inline const char* ExtFreundlichLDFParamHandler::identifier() CADET_NOEXCEPT { return "EXT_FREUNDLICH_LDF"; } +inline const char* ExtFreundlichLDFParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_FREUNDLICH_LDF"; +} - inline bool ExtFreundlichLDFParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) - { - if ((_kkin.size() != _kF.size()) || (_kkin.size() != _n.size()) || (_kkin.size() < nComp)) - throw InvalidParameterException("FLDF_KKIN, FLDF_KF and FLDF_N, have to have the same size"); +inline bool ExtFreundlichLDFParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) +{ + if ((_kkin.size() != _kF.size()) || (_kkin.size() != _n.size()) || (_kkin.size() < nComp)) + throw InvalidParameterException("FLDF_KKIN, FLDF_KF and FLDF_N, have to have the same size"); + return true; +} - return true; - } +/** + * @brief Defines the multi component Freundlich LDF (Linear Driving Force) isotherm + * @details Implements the Freundlich LDF adsorption model: \f[ \begin{align} + * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{f,i} c_{p,i}^{\frac{1}{n_{i}}} \end{align} \f] + * Components without bound state (i.e., non-binding components) are supported. + * @tparam ParamHandler_t Type that can add support for external function dependence + */ +template class FreundlichLDFBindingBase : public ParamHandlerBindingModelBase +{ +public: + FreundlichLDFBindingBase() + { + } + virtual ~FreundlichLDFBindingBase() CADET_NOEXCEPT + { + } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - /** - * @brief Defines the multi component Freundlich LDF (Linear Driving Force) isotherm - * @details Implements the Freundlich LDF adsorption model: \f[ \begin{align} - * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{f,i} c_{p,i}^{\frac{1}{n_{i}}} \end{align} \f] - * Components without bound state (i.e., non-binding components) are supported. - * @tparam ParamHandler_t Type that can add support for external function dependence - */ - template - class FreundlichLDFBindingBase : public ParamHandlerBindingModelBase - { - public: + CADET_BINDINGMODELBASE_BOILERPLATE + +protected: + using ParamHandlerBindingModelBase::_paramHandler; + using ParamHandlerBindingModelBase::_reactionQuasistationarity; + using ParamHandlerBindingModelBase::_nComp; + using ParamHandlerBindingModelBase::_nBoundStates; + + const double _threshold = 1e-14; - FreundlichLDFBindingBase() { } - virtual ~FreundlichLDFBindingBase() CADET_NOEXCEPT { } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } + + template + int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + { + using std::abs; + using std::pow; - static const char* identifier() { return ParamHandler_t::identifier(); } + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); - CADET_BINDINGMODELBASE_BOILERPLATE + // Protein Flux: dq/dt = k_kin * (q* - q) with q* = k_F * c^(1/n) + unsigned int bndIdx = 0; - protected: - using ParamHandlerBindingModelBase::_paramHandler; - using ParamHandlerBindingModelBase::_reactionQuasistationarity; - using ParamHandlerBindingModelBase::_nComp; - using ParamHandlerBindingModelBase::_nBoundStates; + for (int i = 0; i < _nComp; ++i) + { + // Skip components without bound states (bound state index bndIdx is not advanced) + if (_nBoundStates[i] == 0) + continue; - const double _threshold = 1e-14; - - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + const ParamType n_param = static_cast(p->n[i]); + const ParamType kF = static_cast(p->kF[i]); + const ParamType kkin = static_cast(p->kkin[i]); - template - int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + // Residual + if ((n_param > 1) && (abs(yCp[i]) < _threshold)) { - using std::abs; - using std::pow; - - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); - - // Protein Flux: dq/dt = k_kin * (q* - q) with q* = k_F * c^(1/n) - unsigned int bndIdx = 0; - - for (int i = 0; i < _nComp; ++i) - { - // Skip components without bound states (bound state index bndIdx is not advanced) - if (_nBoundStates[i] == 0) - continue; - - const ParamType n_param = static_cast(p->n[i]); - const ParamType kF = static_cast(p->kF[i]); - const ParamType kkin = static_cast(p->kkin[i]); - - // Residual - if ((n_param > 1) && (abs(yCp[i]) < _threshold)) - { - const ParamType alpha_1 = ((2.0 * n_param - 1.0) / n_param) * kF * pow(_threshold, (1.0 - n_param) / n_param); - const ParamType alpha_2 = ((1.0 - n_param) / n_param) * kF * pow(_threshold, (1.0 - 2.0 * n_param) / n_param); - res[bndIdx] = kkin * (y[bndIdx] - yCp[i] * (alpha_1 + alpha_2 * yCp[i])); - } - else - { - res[bndIdx] = kkin * (y[bndIdx] - kF * pow(abs(yCp[i]), 1.0 / n_param)); - } - - // Next bound component - ++bndIdx; - } - - return 0; + const ParamType alpha_1 = + ((2.0 * n_param - 1.0) / n_param) * kF * pow(_threshold, (1.0 - n_param) / n_param); + const ParamType alpha_2 = + ((1.0 - n_param) / n_param) * kF * pow(_threshold, (1.0 - 2.0 * n_param) / n_param); + res[bndIdx] = kkin * (y[bndIdx] - yCp[i] * (alpha_1 + alpha_2 * yCp[i])); } - - template - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + else { - using std::abs; - using std::pow; - - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); - - int bndIdx = 0; - for (int i = 0; i < _nComp; ++i) - { - // Skip components without bound states (bound state index bndIdx is not advanced) - if (_nBoundStates[i] == 0) - continue; - - const double n_param = static_cast(p->n[i]); - const double kF = static_cast(p->kF[i]); - const double kkin = static_cast(p->kkin[i]); - - // dres / dq_i - jac[0] = kkin; - - // dres / dc_{p,i} - if ((n_param > 1) && (abs(yCp[i]) < _threshold)) - { - double const alpha_1 = ((2.0 * n_param - 1.0) / n_param) * kF * pow(_threshold, (1.0 - n_param) / n_param); - double const alpha_2 = ((1.0 - n_param) / n_param) * kF * pow(_threshold, (1.0 - 2.0 * n_param) / n_param); - jac[i - bndIdx - offsetCp] = -kkin * (alpha_1 + 2.0 * alpha_2 * yCp[i]); - } - else - { - jac[i - bndIdx - offsetCp] = -(1.0 / n_param) * kkin * kF * pow(abs(yCp[i]), (1.0 - n_param) / n_param); - } - - ++bndIdx; - ++jac; - } + res[bndIdx] = kkin * (y[bndIdx] - kF * pow(abs(yCp[i]), 1.0 / n_param)); } - }; + // Next bound component + ++bndIdx; + } - typedef FreundlichLDFBindingBase FreundlichLDFBinding; - typedef FreundlichLDFBindingBase ExternalFreundlichLDFBinding; + return 0; + } - namespace binding + template + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + { + using std::abs; + using std::pow; + + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + + int bndIdx = 0; + for (int i = 0; i < _nComp; ++i) { - void registerFreundlichLDFModel(std::unordered_map>& bindings) + // Skip components without bound states (bound state index bndIdx is not advanced) + if (_nBoundStates[i] == 0) + continue; + + const double n_param = static_cast(p->n[i]); + const double kF = static_cast(p->kF[i]); + const double kkin = static_cast(p->kkin[i]); + + // dres / dq_i + jac[0] = kkin; + + // dres / dc_{p,i} + if ((n_param > 1) && (abs(yCp[i]) < _threshold)) + { + double const alpha_1 = + ((2.0 * n_param - 1.0) / n_param) * kF * pow(_threshold, (1.0 - n_param) / n_param); + double const alpha_2 = + ((1.0 - n_param) / n_param) * kF * pow(_threshold, (1.0 - 2.0 * n_param) / n_param); + jac[i - bndIdx - offsetCp] = -kkin * (alpha_1 + 2.0 * alpha_2 * yCp[i]); + } + else { - bindings[FreundlichLDFBinding::identifier()] = []() { return new FreundlichLDFBinding(); }; - bindings[ExternalFreundlichLDFBinding::identifier()] = []() { return new ExternalFreundlichLDFBinding(); }; + jac[i - bndIdx - offsetCp] = -(1.0 / n_param) * kkin * kF * pow(abs(yCp[i]), (1.0 - n_param) / n_param); } - } // namespace binding - } // namespace model + ++bndIdx; + ++jac; + } + } +}; + +typedef FreundlichLDFBindingBase FreundlichLDFBinding; +typedef FreundlichLDFBindingBase ExternalFreundlichLDFBinding; + +namespace binding +{ +void registerFreundlichLDFModel(std::unordered_map>& bindings) +{ + bindings[FreundlichLDFBinding::identifier()] = []() { return new FreundlichLDFBinding(); }; + bindings[ExternalFreundlichLDFBinding::identifier()] = []() { return new ExternalFreundlichLDFBinding(); }; +} +} // namespace binding + +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/GeneralizedIonExchangeBinding.cpp b/src/libcadet/model/binding/GeneralizedIonExchangeBinding.cpp index 43a802df3..5713f2c09 100644 --- a/src/libcadet/model/binding/GeneralizedIonExchangeBinding.cpp +++ b/src/libcadet/model/binding/GeneralizedIonExchangeBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -49,8 +49,9 @@ ], "constantParameters": [ - { "type": "ReferenceConcentrationParameter", "varName": ["refC0", "refQ"], "objName": "refConcentration", "confPrefix": "GIEX_"}, - { "type": "ReferenceConcentrationParameter", "varName": ["refPhC0", "refPhQ"], "objName": "refConcentrationPh", "confPrefix": "GIEX_PH", "skipConfig": true} + { "type": "ReferenceConcentrationParameter", "varName": ["refC0", "refQ"], "objName": "refConcentration", +"confPrefix": "GIEX_"}, { "type": "ReferenceConcentrationParameter", "varName": ["refPhC0", "refPhQ"], "objName": +"refConcentrationPh", "confPrefix": "GIEX_PH", "skipConfig": true} ] } */ @@ -72,7 +73,10 @@ namespace cadet namespace model { -inline const char* GIEXParamHandler::identifier() CADET_NOEXCEPT { return "GENERALIZED_ION_EXCHANGE"; } +inline const char* GIEXParamHandler::identifier() CADET_NOEXCEPT +{ + return "GENERALIZED_ION_EXCHANGE"; +} inline bool GIEXParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { @@ -95,7 +99,10 @@ inline bool GIEXParamHandler::validateConfig(unsigned int nComp, unsigned int co return true; } -inline const char* ExtGIEXParamHandler::identifier() CADET_NOEXCEPT { return "EXT_GENERALIZED_ION_EXCHANGE"; } +inline const char* ExtGIEXParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_GENERALIZED_ION_EXCHANGE"; +} inline bool ExtGIEXParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { @@ -118,20 +125,22 @@ inline bool ExtGIEXParamHandler::validateConfig(unsigned int nComp, unsigned int return true; } - /** * @brief Defines the generalized ion exchange binding model - * @details Implements the generalized ion exchange binding model, which is based on the steric mass action model: \f[ \begin{align} + * @details Implements the generalized ion exchange binding model, which is based on the steric mass action model: \f[ + * \begin{align} * q_0 &= \Lambda - \sum_{j \geq 2} \nu_j(\mathrm{pH}) q_j \\ - * \frac{\partial q_i}{\partial t} &= k_{a,i}(c_p, q, \mathrm{pH}) \left(\Lambda - \sum_{j \geq 2} \left(\nu_j(\mathrm{pH}) + \sigma_{j}\right) q_j \right)^{\nu_i(\mathrm{pH})} c_{p,i} - k_{d,i}(c_p, q, \mathrm{pH}) c_{p,0}^{\nu_i(pH)} q_i \\ + * \frac{\partial q_i}{\partial t} &= k_{a,i}(c_p, q, \mathrm{pH}) \left(\Lambda - \sum_{j \geq 2} + * \left(\nu_j(\mathrm{pH}) + \sigma_{j}\right) q_j \right)^{\nu_i(\mathrm{pH})} c_{p,i} - k_{d,i}(c_p, q, \mathrm{pH}) + * c_{p,0}^{\nu_i(pH)} q_i \\ * \nu_i(\mathrm{pH}) &= \nu_{i,0} + \mathrm{pH} \nu_{i,1} + \mathrm{pH}^2 \nu_{i,2} \\ - * k_{a,i}\left(c_p, q, \mathrm{pH}\right) &= k_{a,i,0} \exp\left(k_{a,i,1} \mathrm{pH} + k_{a,i,2} \mathrm{pH}^2 + k_{a,i,\mathrm{salt}} c_{p,0} + k_{a,i,\mathrm{prot}} c_{p,i}\right) \\ - * k_{d,i}\left(c_p, q, \mathrm{pH}\right) &= k_{d,i,0} \exp\left(k_{d,i,1} \mathrm{pH} + k_{d,i,2} \mathrm{pH}^2 + k_{d,i,\mathrm{salt}} c_{p,0} + k_{d,i,\mathrm{prot}} c_{p,i}\right) - * \end{align} \f] - * Component @c 0 is assumed to be salt. Component @c 1 is a second non-binding modifier component (e.g., pH). - * Multiple bound states are not supported. Components without bound state (i.e., non-binding components) - * are supported. - * + * k_{a,i}\left(c_p, q, \mathrm{pH}\right) &= k_{a,i,0} \exp\left(k_{a,i,1} \mathrm{pH} + k_{a,i,2} + * \mathrm{pH}^2 + k_{a,i,\mathrm{salt}} c_{p,0} + k_{a,i,\mathrm{prot}} c_{p,i}\right) \\ k_{d,i}\left(c_p, q, + * \mathrm{pH}\right) &= k_{d,i,0} \exp\left(k_{d,i,1} \mathrm{pH} + k_{d,i,2} \mathrm{pH}^2 + k_{d,i,\mathrm{salt}} + * c_{p,0} + k_{d,i,\mathrm{prot}} c_{p,i}\right) \end{align} \f] Component @c 0 is assumed to be salt. Component @c 1 + * is a second non-binding modifier component (e.g., pH). Multiple bound states are not supported. Components without + * bound state (i.e., non-binding components) are supported. + * * See @cite Huuk2017. * @tparam ParamHandler_t Type that can add support for external function dependence */ @@ -139,23 +148,32 @@ template class GeneralizedIonExchangeBindingBase : public ParamHandlerBindingModelBase { public: + GeneralizedIonExchangeBindingBase() + { + } + virtual ~GeneralizedIonExchangeBindingBase() CADET_NOEXCEPT + { + } - GeneralizedIonExchangeBindingBase() { } - virtual ~GeneralizedIonExchangeBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { const bool res = BindingModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); // Guarantee that salt has exactly one bound state if (nBound[0] != 1) - throw InvalidParameterException("Generalized ion exchange binding model requires exactly one bound state for salt component"); + throw InvalidParameterException( + "Generalized ion exchange binding model requires exactly one bound state for salt component"); // Guarantee that modifier component is non-binding if (nBound[1] != 0) - throw InvalidParameterException("Generalized ion exchange binding model requires non-binding modifier component (NBOUND[1] = 0)"); + throw InvalidParameterException( + "Generalized ion exchange binding model requires non-binding modifier component (NBOUND[1] = 0)"); // First flux is salt, which is always quasi-stationary _reactionQuasistationarity[0] = true; @@ -163,14 +181,28 @@ class GeneralizedIonExchangeBindingBase : public ParamHandlerBindingModelBase(p->nu[j]) + pH * (static_cast(p->nuLin[j]) + pH * static_cast(p->nuQuad[j])); + const double nu_j = static_cast(p->nu[j]) + + pH * (static_cast(p->nuLin[j]) + pH * static_cast(p->nuQuad[j])); y[0] -= nu_j * y[bndIdx]; @@ -200,13 +233,13 @@ class GeneralizedIonExchangeBindingBase : public ParamHandlerBindingModelBase::_nComp; using ParamHandlerBindingModelBase::_nBoundStates; - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) { - const bool valid = ParamHandlerBindingModelBase::configureImpl(paramProvider, unitOpIdx, parTypeIdx); + const bool valid = + ParamHandlerBindingModelBase::configureImpl(paramProvider, unitOpIdx, parTypeIdx); - if (paramProvider.exists(std::string(_paramHandler.prefixInConfiguration()) + "GIEX_PHREFC0") && paramProvider.exists(std::string(_paramHandler.prefixInConfiguration()) + "GIEX_PHREFQ")) + if (paramProvider.exists(std::string(_paramHandler.prefixInConfiguration()) + "GIEX_PHREFC0") && + paramProvider.exists(std::string(_paramHandler.prefixInConfiguration()) + "GIEX_PHREFQ")) { // Parameters are present, use them _paramHandler.refConcentrationPh().configure("GIEX_PH", paramProvider, _nComp, _nBoundStates); @@ -238,18 +276,19 @@ class GeneralizedIonExchangeBindingBase : public ParamHandlerBindingModelBase int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { using CpStateParamType = typename DoubleActivePromoter::type; using StateParamType = typename DoubleActivePromoter::type; - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Pseudo component 1 is pH const CpStateType pH = yCp[1]; - // Salt flux: nu_0 * q_0 - Lambda + Sum[nu_j * q_j, j] == 0 - // <=> nu_0 * q_0 == Lambda - Sum[nu_j * q_j, j] + // Salt flux: nu_0 * q_0 - Lambda + Sum[nu_j * q_j, j] == 0 + // <=> nu_0 * q_0 == Lambda - Sum[nu_j * q_j, j] // Also compute \bar{q}_0 = nu_0 * q_0 - Sum[sigma_j * q_j, j] res[0] = static_cast(p->nu[0]) * y[0] - static_cast(p->lambda); StateParamType q0_bar = static_cast(p->nu[0]) * y[0]; @@ -261,7 +300,9 @@ class GeneralizedIonExchangeBindingBase : public ParamHandlerBindingModelBase(p->nu[j]) + pH * (static_cast(p->nuLin[j]) + pH * static_cast(p->nuQuad[j])); + const CpStateParamType nu_j = + static_cast(p->nu[j]) + + pH * (static_cast(p->nuLin[j]) + pH * static_cast(p->nuQuad[j])); res[0] += nu_j * y[bndIdx]; q0_bar -= static_cast(p->sigma[j]) * y[bndIdx]; @@ -279,7 +320,8 @@ class GeneralizedIonExchangeBindingBase : public ParamHandlerBindingModelBase(p->nu[i]) + pH * (static_cast(p->nuLin[i]) + pH * static_cast(p->nuQuad[i]))) / static_cast(p->nu[0]); -// const CpStateParamType c0_pow_nu = pow(yCp0_divRef, nu_i_over_nu0); -// const StateParamType q0_bar_pow_nu = pow(q0_bar_divRef, nu_i_over_nu0); - const CpStateParamType nu_i_0_over_nu0 = static_cast(p->nu[i]) / static_cast(p->nu[0]); - const CpStateParamType nu_i_pH_over_nu0 = pH * (static_cast(p->nuLin[i]) + pH * static_cast(p->nuQuad[i])) / static_cast(p->nu[0]); - const CpStateParamType c0_pow_nu = pow(yCp0_divRef, nu_i_0_over_nu0) * pow(yCp0_ph_divRef, nu_i_pH_over_nu0); - const StateParamType q0_bar_pow_nu = pow(q0_bar_divRef, nu_i_0_over_nu0) * pow(q0_bar_ph_divRef, nu_i_pH_over_nu0); - - // k_{a,i}(c_p, q, \mathrm{pH}) = k_{a,i,0} \exp(k_{a,i,1} \mathrm{pH} + k_{a,i,2} \mathrm{pH}^2 + k_{a,i,\mathrm{salt}} c_{p,0} + k_{a,i,\mathrm{prot}} c_{p,i}) - const CpStateParamType ka_i = static_cast(p->kA[i]) * - exp(pH * (static_cast(p->kALin[i]) + pH * static_cast(p->kAQuad[i])) - + static_cast(p->kASalt[i]) * yCp0_divRef + static_cast(p->kAProt[i]) * yCp[i] - ); - const CpStateParamType kd_i = static_cast(p->kD[i]) * - exp(pH * (static_cast(p->kDLin[i]) + pH * static_cast(p->kDQuad[i])) - + static_cast(p->kDSalt[i]) * yCp0_divRef + static_cast(p->kDProt[i]) * yCp[i] - ); + // const CpStateParamType nu_i_over_nu0 = (static_cast(p->nu[i]) + pH * + //(static_cast(p->nuLin[i]) + pH * static_cast(p->nuQuad[i]))) / + // static_cast(p->nu[0]); const CpStateParamType c0_pow_nu = pow(yCp0_divRef, + // nu_i_over_nu0); const StateParamType q0_bar_pow_nu = pow(q0_bar_divRef, nu_i_over_nu0); + const CpStateParamType nu_i_0_over_nu0 = + static_cast(p->nu[i]) / static_cast(p->nu[0]); + const CpStateParamType nu_i_pH_over_nu0 = + pH * (static_cast(p->nuLin[i]) + pH * static_cast(p->nuQuad[i])) / + static_cast(p->nu[0]); + const CpStateParamType c0_pow_nu = + pow(yCp0_divRef, nu_i_0_over_nu0) * pow(yCp0_ph_divRef, nu_i_pH_over_nu0); + const StateParamType q0_bar_pow_nu = + pow(q0_bar_divRef, nu_i_0_over_nu0) * pow(q0_bar_ph_divRef, nu_i_pH_over_nu0); + + // k_{a,i}(c_p, q, \mathrm{pH}) = k_{a,i,0} \exp(k_{a,i,1} \mathrm{pH} + k_{a,i,2} \mathrm{pH}^2 + + // k_{a,i,\mathrm{salt}} c_{p,0} + k_{a,i,\mathrm{prot}} c_{p,i}) + const CpStateParamType ka_i = + static_cast(p->kA[i]) * + exp(pH * (static_cast(p->kALin[i]) + pH * static_cast(p->kAQuad[i])) + + static_cast(p->kASalt[i]) * yCp0_divRef + static_cast(p->kAProt[i]) * yCp[i]); + const CpStateParamType kd_i = + static_cast(p->kD[i]) * + exp(pH * (static_cast(p->kDLin[i]) + pH * static_cast(p->kDQuad[i])) + + static_cast(p->kDSalt[i]) * yCp0_divRef + static_cast(p->kDProt[i]) * yCp[i]); // Residual res[bndIdx] = kd_i * y[bndIdx] * c0_pow_nu - ka_i * yCp[i] * q0_bar_pow_nu; @@ -316,17 +365,20 @@ class GeneralizedIonExchangeBindingBase : public ParamHandlerBindingModelBase - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Pseudo component 1 is pH const double pH = yCp[1]; double q0_bar = static_cast(p->nu[0]) * y[0]; - // Getting to c_{p,0}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0}. This means jac[-bndIdx - offsetCp] corresponds to c_{p,0}. - // Getting to c_{p,i}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0} and a +i to c_{p,i}. + // Getting to c_{p,0}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0}. This means jac[-bndIdx - offsetCp] + // corresponds to c_{p,0}. Getting to c_{p,i}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0} and a +i to + // c_{p,i}. // This means jac[i - bndIdx - offsetCp] corresponds to c_{p,i}. // Salt flux: nu_0 * q_0 - Lambda + Sum[nu_j * q_j, j] == 0 @@ -338,10 +390,12 @@ class GeneralizedIonExchangeBindingBase : public ParamHandlerBindingModelBase(p->nu[j]) + pH * (static_cast(p->nuLin[j]) + pH * static_cast(p->nuQuad[j])); + const double nu_j = static_cast(p->nu[j]) + + pH * (static_cast(p->nuLin[j]) + pH * static_cast(p->nuQuad[j])); jac[bndIdx] = nu_j; - jac[1 - offsetCp] += (static_cast(p->nuLin[j]) + 2.0 * pH * static_cast(p->nuQuad[j])) * y[bndIdx]; + jac[1 - offsetCp] += + (static_cast(p->nuLin[j]) + 2.0 * pH * static_cast(p->nuQuad[j])) * y[bndIdx]; // Calculate \bar{q}_0 = nu_0 * q_0 - Sum[sigma_j * q_j, j] q0_bar -= static_cast(p->sigma[j]) * y[bndIdx]; @@ -374,33 +428,41 @@ class GeneralizedIonExchangeBindingBase : public ParamHandlerBindingModelBase(p->kA[i]); const double kd = static_cast(p->kD[i]); const double nu_0 = static_cast(p->nu[i]) / static_cast(p->nu[0]); - const double nu_pH = pH * (static_cast(p->nuLin[i]) + pH * static_cast(p->nuQuad[i])) / static_cast(p->nu[0]); + const double nu_pH = pH * (static_cast(p->nuLin[i]) + pH * static_cast(p->nuQuad[i])) / + static_cast(p->nu[0]); const double nu = nu_0 + nu_pH; - const double dNuDpH = (static_cast(p->nuLin[i]) + 2.0 * pH * static_cast(p->nuQuad[i])) / static_cast(p->nu[0]); + const double dNuDpH = (static_cast(p->nuLin[i]) + 2.0 * pH * static_cast(p->nuQuad[i])) / + static_cast(p->nu[0]); - const double c0_pow_nu = pow(yCp0_divRef, nu_0) * pow(yCp0_ph_divRef, nu_pH); + const double c0_pow_nu = pow(yCp0_divRef, nu_0) * pow(yCp0_ph_divRef, nu_pH); const double q0_bar_pow_nu = pow(q0_bar_divRef, nu_0) * pow(q0_bar_ph_divRef, nu_pH); - const double c0_pow_nu_m1_divRef = nu * pow(yCp0_divRef, nu_0 - 1.0) * pow(yCp0_ph_divRef, nu_pH) / refC0; - const double q0_bar_pow_nu_m1_divRef = nu * pow(q0_bar_divRef, nu_0 - 1.0) * pow(q0_bar_ph_divRef, nu_pH) / refQ; - - // k_{a,i}(c_p, q, \mathrm{pH}) = k_{a,i,0} \exp(k_{a,i,1} \mathrm{pH} + k_{a,i,2} \mathrm{pH}^2 + k_{a,i,\mathrm{salt}} c_{p,0} + k_{a,i,\mathrm{prot}} c_{p,i}) - const double ka_i = ka * - exp(pH * (static_cast(p->kALin[i]) + pH * static_cast(p->kAQuad[i])) - + static_cast(p->kASalt[i]) * yCp0_divRef + static_cast(p->kAProt[i]) * yCp[i] - ); - const double dKaDpH = ka_i * (static_cast(p->kALin[i]) + 2.0 * pH * static_cast(p->kAQuad[i])); - const double kd_i = kd * - exp(pH * (static_cast(p->kDLin[i]) + pH * static_cast(p->kDQuad[i])) - + static_cast(p->kDSalt[i]) * yCp0_divRef + static_cast(p->kDProt[i]) * yCp[i] - ); - const double dKdDpH = kd_i * (static_cast(p->kDLin[i]) + 2.0 * pH * static_cast(p->kDQuad[i])); + const double c0_pow_nu_m1_divRef = nu * pow(yCp0_divRef, nu_0 - 1.0) * pow(yCp0_ph_divRef, nu_pH) / refC0; + const double q0_bar_pow_nu_m1_divRef = + nu * pow(q0_bar_divRef, nu_0 - 1.0) * pow(q0_bar_ph_divRef, nu_pH) / refQ; + + // k_{a,i}(c_p, q, \mathrm{pH}) = k_{a,i,0} \exp(k_{a,i,1} \mathrm{pH} + k_{a,i,2} \mathrm{pH}^2 + + // k_{a,i,\mathrm{salt}} c_{p,0} + k_{a,i,\mathrm{prot}} c_{p,i}) + const double ka_i = + ka * exp(pH * (static_cast(p->kALin[i]) + pH * static_cast(p->kAQuad[i])) + + static_cast(p->kASalt[i]) * yCp0_divRef + static_cast(p->kAProt[i]) * yCp[i]); + const double dKaDpH = + ka_i * (static_cast(p->kALin[i]) + 2.0 * pH * static_cast(p->kAQuad[i])); + const double kd_i = + kd * exp(pH * (static_cast(p->kDLin[i]) + pH * static_cast(p->kDQuad[i])) + + static_cast(p->kDSalt[i]) * yCp0_divRef + static_cast(p->kDProt[i]) * yCp[i]); + const double dKdDpH = + kd_i * (static_cast(p->kDLin[i]) + 2.0 * pH * static_cast(p->kDQuad[i])); // dres_i / dc_{p,0} - jac[-bndIdx - offsetCp] = kd_i * y[bndIdx] * (c0_pow_nu_m1_divRef + c0_pow_nu * static_cast(p->kDSalt[i]) / refC0) - ka_i * yCp[i] * q0_bar_pow_nu * static_cast(p->kASalt[i]) / refC0; + jac[-bndIdx - offsetCp] = + kd_i * y[bndIdx] * (c0_pow_nu_m1_divRef + c0_pow_nu * static_cast(p->kDSalt[i]) / refC0) - + ka_i * yCp[i] * q0_bar_pow_nu * static_cast(p->kASalt[i]) / refC0; // dres_i / dc_{p,1} - jac[1 - bndIdx - offsetCp] = y[bndIdx] * c0_pow_nu * (dKdDpH + kd_i * std::log(yCp0_ph_divRef) * dNuDpH) - yCp[i] * q0_bar_pow_nu * (dKaDpH + ka_i * std::log(q0_bar_ph_divRef) * dNuDpH); + jac[1 - bndIdx - offsetCp] = y[bndIdx] * c0_pow_nu * (dKdDpH + kd_i * std::log(yCp0_ph_divRef) * dNuDpH) - + yCp[i] * q0_bar_pow_nu * (dKaDpH + ka_i * std::log(q0_bar_ph_divRef) * dNuDpH); // dres_i / dc_{p,i} - jac[i - bndIdx - offsetCp] = -ka_i * q0_bar_pow_nu * (1.0 + yCp[i] * static_cast(p->kAProt[i])) + kd_i * y[bndIdx] * c0_pow_nu * static_cast(p->kDProt[i]); + jac[i - bndIdx - offsetCp] = -ka_i * q0_bar_pow_nu * (1.0 + yCp[i] * static_cast(p->kAProt[i])) + + kd_i * y[bndIdx] * c0_pow_nu * static_cast(p->kDProt[i]); // dres_i / dq_0 jac[-bndIdx] = -ka_i * yCp[i] * q0_bar_pow_nu_m1_divRef * static_cast(p->nu[0]); @@ -414,7 +476,8 @@ class GeneralizedIonExchangeBindingBase : public ParamHandlerBindingModelBase(p->sigma[j])); - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] + // corresponds to q_j. ++bndIdx2; } @@ -429,19 +492,21 @@ class GeneralizedIonExchangeBindingBase : public ParamHandlerBindingModelBase GeneralizedIonExchangeBinding; typedef GeneralizedIonExchangeBindingBase ExternalGeneralizedIonExchangeBinding; namespace binding { - void registerGeneralizedIonExchangeModel(std::unordered_map>& bindings) - { - bindings[GeneralizedIonExchangeBinding::identifier()] = []() { return new GeneralizedIonExchangeBinding(); }; - bindings[ExternalGeneralizedIonExchangeBinding::identifier()] = []() { return new ExternalGeneralizedIonExchangeBinding(); }; - } -} // namespace binding +void registerGeneralizedIonExchangeModel( + std::unordered_map>& bindings) +{ + bindings[GeneralizedIonExchangeBinding::identifier()] = []() { return new GeneralizedIonExchangeBinding(); }; + bindings[ExternalGeneralizedIonExchangeBinding::identifier()] = []() { + return new ExternalGeneralizedIonExchangeBinding(); + }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/HICConstantWaterActivityBinding.cpp b/src/libcadet/model/binding/HICConstantWaterActivityBinding.cpp index 3b9c0a931..98ba9afbf 100644 --- a/src/libcadet/model/binding/HICConstantWaterActivityBinding.cpp +++ b/src/libcadet/model/binding/HICConstantWaterActivityBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -41,7 +41,8 @@ ], "constantParameters": [ - { "type": "ScalarParameter", "varName": "waterActivity", "default": 0.1, "confName": "HICCWA_WATER_ACTIVITY"} + { "type": "ScalarParameter", "varName": "waterActivity", "default": 0.1, "confName": +"HICCWA_WATER_ACTIVITY"} ] } */ @@ -62,35 +63,42 @@ namespace cadet namespace model { -inline const char* HICCWAParamHandler::identifier() CADET_NOEXCEPT { return "HIC_CONSTANT_WATER_ACTIVITY"; } +inline const char* HICCWAParamHandler::identifier() CADET_NOEXCEPT +{ + return "HIC_CONSTANT_WATER_ACTIVITY"; +} inline bool HICCWAParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _qMax.size()) || (_kA.size() < nComp)) + if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _qMax.size()) || + (_kA.size() < nComp)) throw InvalidParameterException("HICCWA_KA, HICCWA_KD, HICCWA_NU, and HICCWA_QMAX have to have the same size"); return true; } -inline const char* ExtHICCWAParamHandler::identifier() CADET_NOEXCEPT { return "EXT_HIC_CONSTANT_WATER_ACTIVITY"; } +inline const char* ExtHICCWAParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_HIC_CONSTANT_WATER_ACTIVITY"; +} inline bool ExtHICCWAParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _qMax.size()) || (_kA.size() < nComp)) + if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _qMax.size()) || + (_kA.size() < nComp)) throw InvalidParameterException("KA, KD, NU, and QMAX have to have the same size"); return true; } - /** * @brief Defines the HIC Isotherm assuming a constant water activity as described by Jäpel and Buyel, 2022 * @details Implements the the HIC Isotherm assuming a constant water activity: \f[ \begin{align} * \beta &= \beta_0 e^{c_{p,0}\beta_1}\\ - * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} \left( 1 - \sum_j \frac{q_j}{q_{max,j}} \right)^{\nu_i} - k_{d,i} q_i 0.1^{\nu_i \beta} - * \end{align} \f] - * Component @c 0 is assumed to be salt without a bound state. Multiple bound states are not supported. - * Components without bound state (i.e., salt and non-binding components) are supported. + * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} \left( 1 - \sum_j \frac{q_j}{q_{max,j}} + *\right)^{\nu_i} - k_{d,i} q_i 0.1^{\nu_i \beta} \end{align} \f] Component @c 0 is assumed to be salt without a bound + *state. Multiple bound states are not supported. Components without bound state (i.e., salt and non-binding components) + *are supported. * * See @cite Jaepel2022. * @tparam ParamHandler_t Type that can add support for external function dependence @@ -99,19 +107,27 @@ template class ConstantWaterActivityBindingBase : public ParamHandlerBindingModelBase { public: + ConstantWaterActivityBindingBase() + { + } + virtual ~ConstantWaterActivityBindingBase() CADET_NOEXCEPT + { + } - ConstantWaterActivityBindingBase() { } - virtual ~ConstantWaterActivityBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { const bool res = BindingModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); // Guarantee that salt has no bound state if (nBound[0] != 0) - throw InvalidParameterException("HICCWA binding model requires exactly zero bound states for salt component"); + throw InvalidParameterException( + "HICCWA binding model requires exactly zero bound states for salt component"); // First flux is salt, which is always quasi-stationary _reactionQuasistationarity[0] = false; @@ -119,18 +135,33 @@ class ConstantWaterActivityBindingBase : public ParamHandlerBindingModelBase::_nComp; using ParamHandlerBindingModelBase::_nBoundStates; - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } template int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { using CpStateParamType = typename DoubleActivePromoter::type; using StateParamType = typename DoubleActivePromoter::type; using std::pow, std::exp; - typename ParamHandler_t::ParamsHandle const _p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const _p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); const ParamType beta0 = static_cast(_p->beta0); const ParamType beta1 = static_cast(_p->beta1); @@ -199,9 +234,11 @@ class ConstantWaterActivityBindingBase : public ParamHandlerBindingModelBase - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const _p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const _p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); const double beta0 = static_cast(_p->beta0); const double beta1 = static_cast(_p->beta1); @@ -237,7 +274,8 @@ class ConstantWaterActivityBindingBase : public ParamHandlerBindingModelBase(_p->waterActivity), nu * beta); // dres_i / dc_{p,0} - jac[-bndIdx - offsetCp] = std::log(static_cast(_p->waterActivity)) * beta0 * beta1 * kD * y[bndIdx] * nu * std::exp(beta1 * yCp[0]) * bulkWater; + jac[-bndIdx - offsetCp] = std::log(static_cast(_p->waterActivity)) * beta0 * beta1 * kD * + y[bndIdx] * nu * std::exp(beta1 * yCp[0]) * bulkWater; // dres_i / dc_{p,i} jac[i - bndIdx - offsetCp] = -kA * std::pow(qSum, nu); @@ -255,7 +293,8 @@ class ConstantWaterActivityBindingBase : public ParamHandlerBindingModelBase(_p->qMax[j])); - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] + // corresponds to q_j. ++bndIdx2; } @@ -275,13 +314,16 @@ typedef ConstantWaterActivityBindingBase ExternalConstant namespace binding { - void registerHICConstantWaterActivityModel(std::unordered_map>& bindings) - { - bindings[ConstantWaterActivityBinding::identifier()] = []() { return new ConstantWaterActivityBinding(); }; - bindings[ExternalConstantWaterActivityBinding::identifier()] = []() { return new ExternalConstantWaterActivityBinding(); }; - } -} // namespace binding +void registerHICConstantWaterActivityModel( + std::unordered_map>& bindings) +{ + bindings[ConstantWaterActivityBinding::identifier()] = []() { return new ConstantWaterActivityBinding(); }; + bindings[ExternalConstantWaterActivityBinding::identifier()] = []() { + return new ExternalConstantWaterActivityBinding(); + }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/HICWaterOnHydrophobicSurfacesBinding.cpp b/src/libcadet/model/binding/HICWaterOnHydrophobicSurfacesBinding.cpp index 3ea0c495d..ee1bed875 100644 --- a/src/libcadet/model/binding/HICWaterOnHydrophobicSurfacesBinding.cpp +++ b/src/libcadet/model/binding/HICWaterOnHydrophobicSurfacesBinding.cpp @@ -58,32 +58,40 @@ namespace cadet namespace model { -inline const char* HICWHSParamHandler::identifier() CADET_NOEXCEPT { return "HIC_WATER_ON_HYDROPHOBIC_SURFACES"; } +inline const char* HICWHSParamHandler::identifier() CADET_NOEXCEPT +{ + return "HIC_WATER_ON_HYDROPHOBIC_SURFACES"; +} inline bool HICWHSParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _qMax.size()) || (_kA.size() < nComp)) + if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _qMax.size()) || + (_kA.size() < nComp)) throw InvalidParameterException("HICWHS_KA, HICWHS_KD, HICWHS_NU, and HICWHS_QMAX have to have the same size"); return true; } -inline const char* ExtHICWHSParamHandler::identifier() CADET_NOEXCEPT { return "EXT_HIC_WATER_ON_HYDROPHOBIC_SURFACES"; } +inline const char* ExtHICWHSParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_HIC_WATER_ON_HYDROPHOBIC_SURFACES"; +} inline bool ExtHICWHSParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _qMax.size()) || (_kA.size() < nComp)) + if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _qMax.size()) || + (_kA.size() < nComp)) throw InvalidParameterException("HICWHS_KA, HICWHS_KD, HICWHS_NU, and HICWHS_QMAX have to have the same size"); return true; } - /** * @brief Defines the HIC Isotherm as described by Wang et al., 2016 * @details Implements the "water on hydrophobic surfaces" model: \f[ \begin{align} * \beta &= \beta_0 e^{c_{p,0}\beta_1}\\ - * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} \left( 1 - \sum_j \frac{q_j}{q_{max,j}} \right)^{\nu_i} + * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} \left( 1 - \sum_j \frac{q_j}{q_{max,j}} + *\right)^{\nu_i} * - k_{d,i} q_i \left(\sum_j q_j \right)^{\nu_i \beta} * \end{align} \f] * Component @c 0 is assumed to be salt without a bound state. Multiple bound states are not supported. @@ -92,17 +100,23 @@ inline bool ExtHICWHSParamHandler::validateConfig(unsigned int nComp, unsigned i * See @cite Wang2016. * @tparam ParamHandler_t Type that can add support for external function dependence */ -template -class HICWHSBase : public ParamHandlerBindingModelBase +template class HICWHSBase : public ParamHandlerBindingModelBase { public: + HICWHSBase() + { + } + virtual ~HICWHSBase() CADET_NOEXCEPT + { + } - HICWHSBase() { } - virtual ~HICWHSBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { const bool res = BindingModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); @@ -113,18 +127,33 @@ class HICWHSBase : public ParamHandlerBindingModelBase return res; } - virtual bool hasSalt() const CADET_NOEXCEPT { return true; } - virtual bool supportsMultistate() const CADET_NOEXCEPT { return false; } - virtual bool supportsNonBinding() const CADET_NOEXCEPT { return true; } - virtual bool hasQuasiStationaryReactions() const CADET_NOEXCEPT { return false; } + virtual bool hasSalt() const CADET_NOEXCEPT + { + return true; + } + virtual bool supportsMultistate() const CADET_NOEXCEPT + { + return false; + } + virtual bool supportsNonBinding() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasQuasiStationaryReactions() const CADET_NOEXCEPT + { + return false; + } - virtual bool preConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const + virtual bool preConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, + double const* yCp, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); return true; } - virtual void postConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const + virtual void postConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, + double const* yCp, LinearBufferAllocator workSpace) const { preConsistentInitialState(t, secIdx, colPos, y, yCp, workSpace); } @@ -137,16 +166,20 @@ class HICWHSBase : public ParamHandlerBindingModelBase using ParamHandlerBindingModelBase::_nComp; using ParamHandlerBindingModelBase::_nBoundStates; - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } template int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { using CpStateParamType = typename DoubleActivePromoter::type; using StateParamType = typename DoubleActivePromoter::type; - typename ParamHandler_t::ParamsHandle const _p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const _p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); const ParamType beta0 = static_cast(_p->beta0); const ParamType beta1 = static_cast(_p->beta1); @@ -158,7 +191,7 @@ class HICWHSBase : public ParamHandlerBindingModelBase // qSumOverqMax could be calculated as 1-freeBindingSites, but is calculated explicitly for clarity StateParamType qSumOverqMax = 0.0; - + unsigned int bndIdx = 0; for (int i = 0; i < _nComp; ++i) { @@ -210,9 +243,11 @@ class HICWHSBase : public ParamHandlerBindingModelBase } template - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const _p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const _p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); auto beta0 = static_cast(_p->beta0); auto beta1 = static_cast(_p->beta1); @@ -261,8 +296,8 @@ class HICWHSBase : public ParamHandlerBindingModelBase if (qSum <= 0.0) { // Compute the Jacobian for the Taylor series defined above - - // dres_i / dc_{p,0} + + // dres_i / dc_{p,0} jac[-bndIdx - offsetCp] = 0; // dres_i / dc_{p,i} @@ -271,7 +306,7 @@ class HICWHSBase : public ParamHandlerBindingModelBase else { // todo - // dres_i / dc_{p,0} + // dres_i / dc_{p,0} jac[-bndIdx - offsetCp] = kD * y[bndIdx] * beta * beta1 * nu * pow(qSum, nu * beta) * std::log(qSum); // dres_i / dc_{p,i} @@ -297,11 +332,12 @@ class HICWHSBase : public ParamHandlerBindingModelBase { // dres_i / dq_j jac[bndIdx2 - bndIdx] = - -kA * yCp[i] * nu * pow(freeBindingSites, nu - 1) / (-static_cast(_p->qMax[j])) - + beta * kD * nu * y[bndIdx] * pow(qSum, nu * beta - 1); + -kA * yCp[i] * nu * pow(freeBindingSites, nu - 1) / (-static_cast(_p->qMax[j])) + + beta * kD * nu * y[bndIdx] * pow(qSum, nu * beta - 1); } - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] + // corresponds to q_j. ++bndIdx2; } @@ -329,13 +365,14 @@ typedef HICWHSBase ExternalHICWHS; namespace binding { - void registerHICWaterOnHydrophobicSurfacesModel(std::unordered_map>& bindings) - { - bindings[HICWHS::identifier()] = []() { return new HICWHS(); }; - bindings[ExternalHICWHS::identifier()] = []() { return new ExternalHICWHS(); }; - } -} // namespace binding +void registerHICWaterOnHydrophobicSurfacesModel( + std::unordered_map>& bindings) +{ + bindings[HICWHS::identifier()] = []() { return new HICWHS(); }; + bindings[ExternalHICWHS::identifier()] = []() { return new ExternalHICWHS(); }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/KumarLangmuirBinding.cpp b/src/libcadet/model/binding/KumarLangmuirBinding.cpp index d5997f6ea..7c122de9f 100644 --- a/src/libcadet/model/binding/KumarLangmuirBinding.cpp +++ b/src/libcadet/model/binding/KumarLangmuirBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -56,55 +56,72 @@ namespace cadet namespace model { -inline const char* KumarLangmuirParamHandler::identifier() CADET_NOEXCEPT { return "KUMAR_MULTI_COMPONENT_LANGMUIR"; } +inline const char* KumarLangmuirParamHandler::identifier() CADET_NOEXCEPT +{ + return "KUMAR_MULTI_COMPONENT_LANGMUIR"; +} inline bool KumarLangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kAct.size()) || (_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() != _nu.size()) || (_kA.size() < nComp)) - throw InvalidParameterException("KMCL_KA, KMCL_KD, KMCL_KACT, KMCL_NU, and KMCL_QMAX have to have the same size"); + if ((_kA.size() != _kAct.size()) || (_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || + (_kA.size() != _nu.size()) || (_kA.size() < nComp)) + throw InvalidParameterException( + "KMCL_KA, KMCL_KD, KMCL_KACT, KMCL_NU, and KMCL_QMAX have to have the same size"); return true; } -inline const char* ExtKumarLangmuirParamHandler::identifier() CADET_NOEXCEPT { return "EXT_KUMAR_MULTI_COMPONENT_LANGMUIR"; } +inline const char* ExtKumarLangmuirParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_KUMAR_MULTI_COMPONENT_LANGMUIR"; +} inline bool ExtKumarLangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kAct.size()) || (_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() != _nu.size()) || (_kA.size() < nComp)) - throw InvalidParameterException("KMCL_KA, KMCL_KD, KMCL_KACT, KMCL_NU, and KMCL_QMAX have to have the same size"); + if ((_kA.size() != _kAct.size()) || (_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || + (_kA.size() != _nu.size()) || (_kA.size() < nComp)) + throw InvalidParameterException( + "KMCL_KA, KMCL_KD, KMCL_KACT, KMCL_NU, and KMCL_QMAX have to have the same size"); return true; } - /** * @brief Defines the extended multi component Langmuir binding model used by Kumar et al. - * @details Implements the extended Langmuir adsorption model: \f[ \begin{align} - * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} \exp\left( \frac{k_{\text{act},i}}{T} \right) c_{p,i} q_{\text{max},i} \left( 1 - \sum_j \frac{q_j}{q_{\text{max},j}} \right) - \left( c_{p,0} \right)^{\nu_i} k_{d,i} q_i + * @details Implements the extended Langmuir adsorption model: \f[ \begin{align} + * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} \exp\left( \frac{k_{\text{act},i}}{T} \right) c_{p,i} + * q_{\text{max},i} \left( 1 - \sum_j \frac{q_j}{q_{\text{max},j}} \right) - \left( c_{p,0} \right)^{\nu_i} k_{d,i} q_i * \end{align} \f] - * The first component @f$ c_{p,0} @f$ is assumed to be salt and should be set as non-binding (@c 0 bound states). - * Multiple bound states are not supported. Components without bound state (i.e., non-binding components) are supported. - * - * In this model, the true adsorption rate @f$ k_{a,\text{true}} @f$ is governed by the Arrhenius law in order to - * take temperature into account @f[ k_{a,\text{true}} = k_{a,i} \exp\left( \frac{k_{\text{act},i}}{T} \right). @f] - * Here, @f$ k_{a,i} @f$ is the frequency or pre-exponential factor and @f[ k_{\text{act},i} = \frac{E}{R} @f] is - * the activation temperature (@f$ E @f$ denotes the activation energy and @f$ R @f$ the Boltzmann gas constant). + * The first component @f$ c_{p,0} @f$ is assumed to be salt and should be set as non-binding (@c 0 bound + * states). Multiple bound states are not supported. Components without bound state (i.e., non-binding components) are + * supported. + * + * In this model, the true adsorption rate @f$ k_{a,\text{true}} @f$ is governed by the Arrhenius law in order + * to take temperature into account @f[ k_{a,\text{true}} = k_{a,i} \exp\left( \frac{k_{\text{act},i}}{T} \right). @f] + * Here, @f$ k_{a,i} @f$ is the frequency or pre-exponential factor and @f[ k_{\text{act},i} = \frac{E}{R} @f] + * is the activation temperature (@f$ E @f$ denotes the activation energy and @f$ R @f$ the Boltzmann gas constant). * Desorption is modified by salt (component @c 0) which does not bind. The characteristic charge @f$ \nu @f$ * of the protein is taken into account by the power law. * See @cite Kumar2015 for details. * @tparam ParamHandler_t Type that can add support for external function dependence */ -template -class KumarLangmuirBindingBase : public ParamHandlerBindingModelBase +template class KumarLangmuirBindingBase : public ParamHandlerBindingModelBase { public: + KumarLangmuirBindingBase() + { + } + virtual ~KumarLangmuirBindingBase() CADET_NOEXCEPT + { + } - KumarLangmuirBindingBase() { } - virtual ~KumarLangmuirBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { const bool res = BindingModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); @@ -115,7 +132,10 @@ class KumarLangmuirBindingBase : public ParamHandlerBindingModelBase::_nComp; using ParamHandlerBindingModelBase::_nBoundStates; - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } template int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); - // Protein fluxes: -k_{a,i} * exp( k_{act,i} / T ) * c_{p,i} * q_{max,i} * (1 - \sum_j q_j / q_{max,j}) + (c_{p,0})^{\nu_i} * k_{d,i} * q_i + // Protein fluxes: -k_{a,i} * exp( k_{act,i} / T ) * c_{p,i} * q_{max,i} * (1 - \sum_j q_j / q_{max,j}) + + // (c_{p,0})^{\nu_i} * k_{d,i} * q_i ResidualType qSum = 1.0; unsigned int bndIdx = 0; for (int i = 1; i < _nComp; ++i) @@ -156,7 +181,8 @@ class KumarLangmuirBindingBase : public ParamHandlerBindingModelBase(p->kA[i]) * exp(static_cast(p->kAct[i]) / static_cast(p->temperature)); + const ResidualType ka = static_cast(p->kA[i]) * + exp(static_cast(p->kAct[i]) / static_cast(p->temperature)); const ResidualType kd = pow(yCp[0], static_cast(p->nu[i])) * static_cast(p->kD[i]); res[bndIdx] = kd * y[bndIdx] - ka * yCp[i] * static_cast(p->qMax[i]) * qSum; @@ -168,11 +194,14 @@ class KumarLangmuirBindingBase : public ParamHandlerBindingModelBase - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); - // Protein fluxes: -k_{a,i} * exp( k_{act,i} / T ) * c_{p,i} * q_{max,i} * (1 - \sum_j q_j / q_{max,j}) + (c_{p,0})^{\nu_i} * k_{d,i} * q_i + // Protein fluxes: -k_{a,i} * exp( k_{act,i} / T ) * c_{p,i} * q_{max,i} * (1 - \sum_j q_j / q_{max,j}) + + // (c_{p,0})^{\nu_i} * k_{d,i} * q_i double qSum = 1.0; int bndIdx = 0; for (int i = 1; i < _nComp; ++i) @@ -194,7 +223,8 @@ class KumarLangmuirBindingBase : public ParamHandlerBindingModelBase(p->kA[i]) * exp(static_cast(p->kAct[i]) / static_cast(p->temperature)); + const double ka = static_cast(p->kA[i]) * + exp(static_cast(p->kAct[i]) / static_cast(p->temperature)); const double kd = pow(yCp[0], static_cast(p->nu[i])) * static_cast(p->kD[i]); // dres_i / dc_{p,i} @@ -203,7 +233,8 @@ class KumarLangmuirBindingBase : public ParamHandlerBindingModelBase(p->kD[i]) * static_cast(p->nu[i]) * pow(yCp[0], static_cast(p->nu[i]) - 1.0) * y[bndIdx]; + jac[-bndIdx - offsetCp] = static_cast(p->kD[i]) * static_cast(p->nu[i]) * + pow(yCp[0], static_cast(p->nu[i]) - 1.0) * y[bndIdx]; // Fill dres_i / dq_j int bndIdx2 = 0; @@ -215,7 +246,8 @@ class KumarLangmuirBindingBase : public ParamHandlerBindingModelBase(p->qMax[i]) / static_cast(p->qMax[j]); - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] + // corresponds to q_j. ++bndIdx2; } @@ -235,13 +267,13 @@ typedef KumarLangmuirBindingBase ExternalKumarLang namespace binding { - void registerKumarLangmuirModel(std::unordered_map>& bindings) - { - bindings[KumarLangmuirBinding::identifier()] = []() { return new KumarLangmuirBinding(); }; - bindings[ExternalKumarLangmuirBinding::identifier()] = []() { return new ExternalKumarLangmuirBinding(); }; - } -} // namespace binding +void registerKumarLangmuirModel(std::unordered_map>& bindings) +{ + bindings[KumarLangmuirBinding::identifier()] = []() { return new KumarLangmuirBinding(); }; + bindings[ExternalKumarLangmuirBinding::identifier()] = []() { return new ExternalKumarLangmuirBinding(); }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/LangmuirBinding.cpp b/src/libcadet/model/binding/LangmuirBinding.cpp index 882a2af3d..2f6619c43 100644 --- a/src/libcadet/model/binding/LangmuirBinding.cpp +++ b/src/libcadet/model/binding/LangmuirBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -49,7 +49,10 @@ namespace cadet namespace model { -inline const char* LangmuirParamHandler::identifier() CADET_NOEXCEPT { return "MULTI_COMPONENT_LANGMUIR"; } +inline const char* LangmuirParamHandler::identifier() CADET_NOEXCEPT +{ + return "MULTI_COMPONENT_LANGMUIR"; +} inline bool LangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { @@ -59,7 +62,10 @@ inline bool LangmuirParamHandler::validateConfig(unsigned int nComp, unsigned in return true; } -inline const char* ExtLangmuirParamHandler::identifier() CADET_NOEXCEPT { return "EXT_MULTI_COMPONENT_LANGMUIR"; } +inline const char* ExtLangmuirParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_MULTI_COMPONENT_LANGMUIR"; +} inline bool ExtLangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { @@ -69,29 +75,34 @@ inline bool ExtLangmuirParamHandler::validateConfig(unsigned int nComp, unsigned return true; } - /** * @brief Defines the multi component Langmuir binding model - * @details Implements the Langmuir adsorption model: \f[ \begin{align} - * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} q_{\text{max},i} \left( 1 - \sum_j \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} q_i - * \end{align} \f] - * Multiple bound states are not supported. + * @details Implements the Langmuir adsorption model: \f[ \begin{align} + * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} q_{\text{max},i} \left( 1 - \sum_j + * \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} q_i \end{align} \f] Multiple bound states are not supported. * Components without bound state (i.e., non-binding components) are supported. - * + * * See @cite Langmuir1916. * @tparam ParamHandler_t Type that can add support for external function dependence */ -template -class LangmuirBindingBase : public ParamHandlerBindingModelBase +template class LangmuirBindingBase : public ParamHandlerBindingModelBase { public: + LangmuirBindingBase() + { + } + virtual ~LangmuirBindingBase() CADET_NOEXCEPT + { + } - LangmuirBindingBase() { } - virtual ~LangmuirBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yCp, double const* y, double* dResDt, LinearBufferAllocator workSpace) const + virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yCp, double const* y, double* dResDt, + LinearBufferAllocator workSpace) const { if (!this->hasQuasiStationaryReactions()) return; @@ -130,10 +141,10 @@ class LangmuirBindingBase : public ParamHandlerBindingModelBase continue; // Residual - dResDt[bndIdx] = static_cast(dpDt->kD[i]) * y[bndIdx] - - yCp[i] * (static_cast(dpDt->kA[i]) * static_cast(p->qMax[i]) * qSum - + static_cast(p->kA[i]) * static_cast(dpDt->qMax[i]) * qSum - + static_cast(p->kA[i]) * static_cast(p->qMax[i]) * qSumT); + dResDt[bndIdx] = static_cast(dpDt->kD[i]) * y[bndIdx] - + yCp[i] * (static_cast(dpDt->kA[i]) * static_cast(p->qMax[i]) * qSum + + static_cast(p->kA[i]) * static_cast(dpDt->qMax[i]) * qSum + + static_cast(p->kA[i]) * static_cast(p->qMax[i]) * qSumT); // Next bound component ++bndIdx; @@ -148,13 +159,17 @@ class LangmuirBindingBase : public ParamHandlerBindingModelBase using ParamHandlerBindingModelBase::_nComp; using ParamHandlerBindingModelBase::_nBoundStates; - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } template int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Protein fluxes: -k_{a,i} * c_{p,i} * q_{max,i} * (1 - \sum_j q_j / q_{max,j}) + k_{d,i} * q_i ResidualType qSum = 1.0; @@ -179,7 +194,8 @@ class LangmuirBindingBase : public ParamHandlerBindingModelBase continue; // Residual - res[bndIdx] = static_cast(p->kD[i]) * y[bndIdx] - static_cast(p->kA[i]) * yCp[i] * static_cast(p->qMax[i]) * qSum; + res[bndIdx] = static_cast(p->kD[i]) * y[bndIdx] - + static_cast(p->kA[i]) * yCp[i] * static_cast(p->qMax[i]) * qSum; // Next bound component ++bndIdx; @@ -189,9 +205,11 @@ class LangmuirBindingBase : public ParamHandlerBindingModelBase } template - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Protein fluxes: -k_{a,i} * c_{p,i} * q_{max,i} * (1 - \sum_j q_j / q_{max,j}) + k_{d,i} * q_i double qSum = 1.0; @@ -233,7 +251,8 @@ class LangmuirBindingBase : public ParamHandlerBindingModelBase // dres_i / dq_j jac[bndIdx2 - bndIdx] = ka * yCp[i] * static_cast(p->qMax[i]) / static_cast(p->qMax[j]); - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] + // corresponds to q_j. ++bndIdx2; } @@ -245,7 +264,7 @@ class LangmuirBindingBase : public ParamHandlerBindingModelBase ++bndIdx; ++jac; } - } + } }; typedef LangmuirBindingBase LangmuirBinding; @@ -253,13 +272,13 @@ typedef LangmuirBindingBase ExternalLangmuirBinding; namespace binding { - void registerLangmuirModel(std::unordered_map>& bindings) - { - bindings[LangmuirBinding::identifier()] = []() { return new LangmuirBinding(); }; - bindings[ExternalLangmuirBinding::identifier()] = []() { return new ExternalLangmuirBinding(); }; - } -} // namespace binding +void registerLangmuirModel(std::unordered_map>& bindings) +{ + bindings[LangmuirBinding::identifier()] = []() { return new LangmuirBinding(); }; + bindings[ExternalLangmuirBinding::identifier()] = []() { return new ExternalLangmuirBinding(); }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/LangmuirLDFBinding.cpp b/src/libcadet/model/binding/LangmuirLDFBinding.cpp index 44cf405c7..4cda556fa 100644 --- a/src/libcadet/model/binding/LangmuirLDFBinding.cpp +++ b/src/libcadet/model/binding/LangmuirLDFBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -49,7 +49,10 @@ namespace cadet namespace model { -inline const char* LangmuirLDFParamHandler::identifier() CADET_NOEXCEPT { return "MULTI_COMPONENT_LANGMUIR_LDF"; } +inline const char* LangmuirLDFParamHandler::identifier() CADET_NOEXCEPT +{ + return "MULTI_COMPONENT_LANGMUIR_LDF"; +} inline bool LangmuirLDFParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { @@ -59,39 +62,47 @@ inline bool LangmuirLDFParamHandler::validateConfig(unsigned int nComp, unsigned return true; } -inline const char* ExtLangmuirLDFParamHandler::identifier() CADET_NOEXCEPT { return "EXT_MULTI_COMPONENT_LANGMUIR_LDF"; } +inline const char* ExtLangmuirLDFParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_MULTI_COMPONENT_LANGMUIR_LDF"; +} inline bool ExtLangmuirLDFParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { if ((_keq.size() != _kkin.size()) || (_keq.size() != _qMax.size()) || (_keq.size() < nComp)) - throw InvalidParameterException("EXT_MCLLDF_KEQ, EXT_MCLLDF_KKIN, and EXT_MCLLDF_QMAX have to have the same size"); + throw InvalidParameterException( + "EXT_MCLLDF_KEQ, EXT_MCLLDF_KKIN, and EXT_MCLLDF_QMAX have to have the same size"); return true; } - /** * @brief Defines the multi component Langmuir binding model - * @details Implements the Langmuir adsorption model: \f[ \begin{align} - * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} q_{\text{max},i} \left( 1 - \sum_j \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} q_i - * \end{align} \f] - * Multiple bound states are not supported. + * @details Implements the Langmuir adsorption model: \f[ \begin{align} + * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} q_{\text{max},i} \left( 1 - \sum_j + * \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} q_i \end{align} \f] Multiple bound states are not supported. * Components without bound state (i.e., non-binding components) are supported. - * + * * See @cite Langmuir1916. * @tparam ParamHandler_t Type that can add support for external function dependence */ -template -class LangmuirLDFBindingBase : public ParamHandlerBindingModelBase +template class LangmuirLDFBindingBase : public ParamHandlerBindingModelBase { public: + LangmuirLDFBindingBase() + { + } + virtual ~LangmuirLDFBindingBase() CADET_NOEXCEPT + { + } - LangmuirLDFBindingBase() { } - virtual ~LangmuirLDFBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - /*virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yCp, double const* y, double* dResDt, LinearBufferAllocator workSpace) const + /*virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yCp, double const* y, double* dResDt, LinearBufferAllocator workSpace) const { if (!this->hasQuasiStationaryReactions()) return; @@ -130,10 +141,10 @@ class LangmuirLDFBindingBase : public ParamHandlerBindingModelBase(dpDt->kD[i]) * y[bndIdx] + dResDt[bndIdx] = static_cast(dpDt->kD[i]) * y[bndIdx] - yCp[i] * (static_cast(dpDt->kA[i]) * static_cast(p->qMax[i]) * cpSum - + static_cast(p->kA[i]) * static_cast(dpDt->qMax[i]) * cpSum - + static_cast(p->kA[i]) * static_cast(p->qMax[i]) * cpSumT); + + static_cast(p->kA[i]) * static_cast(dpDt->qMax[i]) * cpSum + + static_cast(p->kA[i]) * static_cast(p->qMax[i]) * cpSumT); // Next bound component ++bndIdx; @@ -148,13 +159,17 @@ class LangmuirLDFBindingBase : public ParamHandlerBindingModelBase::_nComp; using ParamHandlerBindingModelBase::_nBoundStates; - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } template int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Protein fluxes: k_{kin,i}q_i - k_{kin,i} \frac{q_{m,i}k_{eq,i}c_i}{1+\sum_{j=1}^{n_{comp}} k_{eq,j}c_j} ResidualType cpSum = 1.0; @@ -179,7 +194,9 @@ class LangmuirLDFBindingBase : public ParamHandlerBindingModelBase(p->kkin[i]) * (y[bndIdx] - static_cast(p->keq[i]) * yCp[i] * static_cast(p->qMax[i]) / cpSum); + res[bndIdx] = + static_cast(p->kkin[i]) * + (y[bndIdx] - static_cast(p->keq[i]) * yCp[i] * static_cast(p->qMax[i]) / cpSum); // Next bound component ++bndIdx; @@ -189,9 +206,11 @@ class LangmuirLDFBindingBase : public ParamHandlerBindingModelBase - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Protein fluxes: k_{kin,i}q_i - k_{kin,i} \frac{q_{m,i}k_{eq,i}c_i}{1+\sum_{j=1}^{n_{comp}} k_{eq,j}c_j} double cpSum = 1.0; @@ -236,7 +255,6 @@ class LangmuirLDFBindingBase : public ParamHandlerBindingModelBase(p->keq[j]) * commonFactor; // Getting to c_{p,j}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0} and a +j to c_{p,j}. // This means jac[j - bndIdx - offsetCp] corresponds to c_{p,j}. - ++bndIdx2; } @@ -248,7 +266,7 @@ class LangmuirLDFBindingBase : public ParamHandlerBindingModelBase LangmuirLDFBinding; @@ -256,13 +274,13 @@ typedef LangmuirLDFBindingBase ExternalLangmuirLDFBi namespace binding { - void registerLangmuirLDFModel(std::unordered_map>& bindings) - { - bindings[LangmuirLDFBinding::identifier()] = []() { return new LangmuirLDFBinding(); }; - bindings[ExternalLangmuirLDFBinding::identifier()] = []() { return new ExternalLangmuirLDFBinding(); }; - } -} // namespace binding +void registerLangmuirLDFModel(std::unordered_map>& bindings) +{ + bindings[LangmuirLDFBinding::identifier()] = []() { return new LangmuirLDFBinding(); }; + bindings[ExternalLangmuirLDFBinding::identifier()] = []() { return new ExternalLangmuirLDFBinding(); }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/LangmuirLDFCBinding.cpp b/src/libcadet/model/binding/LangmuirLDFCBinding.cpp index 822170d50..852afe98c 100644 --- a/src/libcadet/model/binding/LangmuirLDFCBinding.cpp +++ b/src/libcadet/model/binding/LangmuirLDFCBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -49,7 +49,10 @@ namespace cadet namespace model { -inline const char* LangmuirLDFCParamHandler::identifier() CADET_NOEXCEPT { return "MULTI_COMPONENT_LANGMUIR_LDF_LIQUID_PHASE"; } +inline const char* LangmuirLDFCParamHandler::identifier() CADET_NOEXCEPT +{ + return "MULTI_COMPONENT_LANGMUIR_LDF_LIQUID_PHASE"; +} inline bool LangmuirLDFCParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { @@ -59,39 +62,47 @@ inline bool LangmuirLDFCParamHandler::validateConfig(unsigned int nComp, unsigne return true; } -inline const char* ExtLangmuirLDFCParamHandler::identifier() CADET_NOEXCEPT { return "EXT_MULTI_COMPONENT_LANGMUIR_LDF_LIQUID_PHASE"; } +inline const char* ExtLangmuirLDFCParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_MULTI_COMPONENT_LANGMUIR_LDF_LIQUID_PHASE"; +} inline bool ExtLangmuirLDFCParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { if ((_keq.size() != _kkin.size()) || (_keq.size() != _qMax.size()) || (_keq.size() < nComp)) - throw InvalidParameterException("EXT_MCLLDFC_KEQ, EXT_MCLLDFC_KKIN, and EXT_MCLLDFC_QMAX have to have the same size"); + throw InvalidParameterException( + "EXT_MCLLDFC_KEQ, EXT_MCLLDFC_KKIN, and EXT_MCLLDFC_QMAX have to have the same size"); return true; } - /** * @brief Defines the multi component Langmuir binding model - * @details Implements the Langmuir adsorption model: \f[ \begin{align} - * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} q_{\text{max},i} \left( 1 - \sum_j \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} q_i - * \end{align} \f] - * Multiple bound states are not supported. + * @details Implements the Langmuir adsorption model: \f[ \begin{align} + * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} q_{\text{max},i} \left( 1 - \sum_j + * \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} q_i \end{align} \f] Multiple bound states are not supported. * Components without bound state (i.e., non-binding components) are supported. - * + * * See @cite Langmuir1916. * @tparam ParamHandler_t Type that can add support for external function dependence */ -template -class LangmuirLDFCBindingBase : public ParamHandlerBindingModelBase +template class LangmuirLDFCBindingBase : public ParamHandlerBindingModelBase { public: + LangmuirLDFCBindingBase() + { + } + virtual ~LangmuirLDFCBindingBase() CADET_NOEXCEPT + { + } - LangmuirLDFCBindingBase() { } - virtual ~LangmuirLDFCBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - /*virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yCp, double const* y, double* dResDt, LinearBufferAllocator workSpace) const + /*virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yCp, double const* y, double* dResDt, LinearBufferAllocator workSpace) const { if (!this->hasQuasiStationaryReactions()) return; @@ -130,10 +141,10 @@ class LangmuirLDFCBindingBase : public ParamHandlerBindingModelBase(dpDt->kD[i]) * y[bndIdx] + dResDt[bndIdx] = static_cast(dpDt->kD[i]) * y[bndIdx] - yCp[i] * (static_cast(dpDt->kA[i]) * static_cast(p->qMax[i]) * qSum - + static_cast(p->kA[i]) * static_cast(dpDt->qMax[i]) * qSum - + static_cast(p->kA[i]) * static_cast(p->qMax[i]) * qSumT); + + static_cast(p->kA[i]) * static_cast(dpDt->qMax[i]) * qSum + + static_cast(p->kA[i]) * static_cast(p->qMax[i]) * qSumT); // Next bound component ++bndIdx; @@ -148,13 +159,17 @@ class LangmuirLDFCBindingBase : public ParamHandlerBindingModelBase::_nComp; using ParamHandlerBindingModelBase::_nBoundStates; - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } template int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Protein fluxes: k_{kin,i}q_i - k_{kin,i} \frac{q_{m,i}k_{eq,i}c_i}{1+\sum_{j=1}^{n_{comp}} k_{eq,j}c_j} ResidualType qSum = 1.0; @@ -165,7 +180,7 @@ class LangmuirLDFCBindingBase : public ParamHandlerBindingModelBase(p->qMax[i]); + qSum -= y[bndIdx] / static_cast(p->qMax[i]); // Next bound component ++bndIdx; @@ -179,7 +194,9 @@ class LangmuirLDFCBindingBase : public ParamHandlerBindingModelBase(p->kkin[i]) * (yCp[i] - y[bndIdx] / (static_cast(p->keq[i]) * static_cast(p->qMax[i]) * qSum)); + res[bndIdx] = + -static_cast(p->kkin[i]) * + (yCp[i] - y[bndIdx] / (static_cast(p->keq[i]) * static_cast(p->qMax[i]) * qSum)); // Next bound component ++bndIdx; @@ -189,9 +206,11 @@ class LangmuirLDFCBindingBase : public ParamHandlerBindingModelBase - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Protein fluxes: k_{kin,i}q_i - k_{kin,i} \frac{q_{m,i}k_{eq,i}c_i}{1+\sum_{j=1}^{n_{comp}} k_{eq,j}c_j} double qSum = 1.0; @@ -232,8 +251,9 @@ class LangmuirLDFCBindingBase : public ParamHandlerBindingModelBase(p->qMax[j]) * qSum * qSum); + // dres_i / dq_j + jac[bndIdx2 - bndIdx] += + y[bndIdx] * kkin / (keq * qMax * static_cast(p->qMax[j]) * qSum * qSum); // Getting to q_j: -bndIdx takes us to q_{0}, another +bndIdx2 to q_{j}. // This means jac[bndIdx2 - bndIdx] corresponds to q_{j}. @@ -247,7 +267,7 @@ class LangmuirLDFCBindingBase : public ParamHandlerBindingModelBase LangmuirLDFCBinding; @@ -255,13 +275,13 @@ typedef LangmuirLDFCBindingBase ExternalLangmuirLDF namespace binding { - void registerLangmuirLDFCModel(std::unordered_map>& bindings) - { - bindings[LangmuirLDFCBinding::identifier()] = []() { return new LangmuirLDFCBinding(); }; - bindings[ExternalLangmuirLDFCBinding::identifier()] = []() { return new ExternalLangmuirLDFCBinding(); }; - } -} // namespace binding +void registerLangmuirLDFCModel(std::unordered_map>& bindings) +{ + bindings[LangmuirLDFCBinding::identifier()] = []() { return new LangmuirLDFCBinding(); }; + bindings[ExternalLangmuirLDFCBinding::identifier()] = []() { return new ExternalLangmuirLDFCBinding(); }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/LinearBinding.cpp b/src/libcadet/model/binding/LinearBinding.cpp index cc3140c8c..72f853543 100644 --- a/src/libcadet/model/binding/LinearBinding.cpp +++ b/src/libcadet/model/binding/LinearBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,18 +11,18 @@ // ============================================================================= /** - * @file + * @file * Implements the LinearBinding class. This is the simplest, self-contained, and * complete example of an IBindingModel implementation. A slightly more complicated * example is given by LangmuirBinding. - * + * * The implementation in this file intentionally does not use common code as provided * by BindingModelBase. Thus, it can be used as an example for learning how to * develop custom binding models and getting an impression of what happens behind * the curtains. For serious binding models, have a look at the LangmuirBinding model, * which makes use of common code. - * - * For implementing externally dependent binding models, the storage of the model + * + * For implementing externally dependent binding models, the storage of the model * parameters is encapsulated from the actual model implementation. The default * storage implementation just stores, configures, and registers the standard model * parameters and does not provide external dependence. A second implementation @@ -34,7 +34,7 @@ * are plugged into the binding model, which always uses the variable names of the * default storage (default storage => original variables, dependent storage => cache). * In this way, support for external dependence can be added with minimal code duplication. - * + * * Each binding model implementation should provide a function that registers all * binding model variants (default, external dependence) in a map. This function * is expected to be in the cadet::model::binding namespace and usually found at @@ -70,7 +70,6 @@ namespace model class LinearParamHandler : public ConstParamHandlerBase { public: - /** * @brief Holds actual parameter data */ @@ -87,9 +86,14 @@ class LinearParamHandler : public ConstParamHandlerBase * @brief Returns name of the binding model * @return Name of the binding model */ - static const char* identifier() { return "LINEAR"; } + static const char* identifier() + { + return "LINEAR"; + } - LinearParamHandler() CADET_NOEXCEPT : _kA(&_localParams.kA), _kD(&_localParams.kD) { } + LinearParamHandler() CADET_NOEXCEPT : _kA(&_localParams.kA), _kD(&_localParams.kD) + { + } /** * @brief Reads parameters and verifies them @@ -113,7 +117,8 @@ class LinearParamHandler : public ConstParamHandlerBase * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void registerParameters(std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParameters(std::unordered_map& parameters, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) { _kA.registerParam("LIN_KA", parameters, unitOpIdx, parTypeIdx, nComp, nBoundStates); _kD.registerParam("LIN_KD", parameters, unitOpIdx, parTypeIdx, nComp, nBoundStates); @@ -126,7 +131,8 @@ class LinearParamHandler : public ConstParamHandlerBase * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) \ + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) { _kA.reserve(numElem, numSlices, nComp, nBoundStates); _kD.reserve(numElem, numSlices, nComp, nBoundStates); @@ -143,7 +149,8 @@ class LinearParamHandler : public ConstParamHandlerBase * @param [in,out] workSpace Memory buffer for updated data * @return Externally dependent parameter values */ - inline ParamsHandle update(double t, unsigned int secIdx, const ColumnPosition& colPos, unsigned int nComp, unsigned int const* nBoundStates, LinearBufferAllocator& workSpace) const + inline ParamsHandle update(double t, unsigned int secIdx, const ColumnPosition& colPos, unsigned int nComp, + unsigned int const* nBoundStates, LinearBufferAllocator& workSpace) const { return &_localParams; } @@ -159,13 +166,15 @@ class LinearParamHandler : public ConstParamHandlerBase * @param [in,out] workSpace Memory buffer for updated data * @return Time derivatives of externally dependent parameters */ - inline std::tuple updateTimeDerivative(double t, unsigned int secIdx, const ColumnPosition& colPos, unsigned int nComp, unsigned int const* nBoundStates, LinearBufferAllocator& workSpace) const + inline std::tuple updateTimeDerivative(double t, unsigned int secIdx, + const ColumnPosition& colPos, unsigned int nComp, + unsigned int const* nBoundStates, + LinearBufferAllocator& workSpace) const { return std::make_tuple(&_localParams, nullptr); } protected: - /** * @brief Validates recently read parameters * @param [in] nComp Number of components @@ -193,7 +202,6 @@ class LinearParamHandler : public ConstParamHandlerBase class ExtLinearParamHandler : public ExternalParamHandlerBase { public: - /** * @brief Holds actual parameter data * @details The parameter data will be stored in a memory buffer. This requires @@ -212,13 +220,18 @@ class ExtLinearParamHandler : public ExternalParamHandlerBase typedef VariableParams params_t; typedef ConstBufferedScalar ParamsHandle; - ExtLinearParamHandler() CADET_NOEXCEPT { } + ExtLinearParamHandler() CADET_NOEXCEPT + { + } /** * @brief Returns name of the binding model * @return Name of the binding model */ - static const char* identifier() { return "EXT_LINEAR"; } + static const char* identifier() + { + return "EXT_LINEAR"; + } /** * @brief Reads parameters and verifies them @@ -232,7 +245,7 @@ class ExtLinearParamHandler : public ExternalParamHandlerBase { _kA.configure("LIN_KA", paramProvider, nComp, nBoundStates); _kD.configure("LIN_KD", paramProvider, nComp, nBoundStates); - + // Number of externally dependent parameters (2) needs to be given to ExternalParamHandlerBase::configure() ExternalParamHandlerBase::configure(paramProvider, 2); return validateConfig(nComp, nBoundStates); @@ -245,7 +258,8 @@ class ExtLinearParamHandler : public ExternalParamHandlerBase * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void registerParameters(std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) + inline void registerParameters(std::unordered_map& parameters, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx, unsigned int nComp, unsigned int const* nBoundStates) { _kA.registerParam("LIN_KA", parameters, unitOpIdx, parTypeIdx, nComp, nBoundStates); _kD.registerParam("LIN_KD", parameters, unitOpIdx, parTypeIdx, nComp, nBoundStates); @@ -258,7 +272,8 @@ class ExtLinearParamHandler : public ExternalParamHandlerBase * @param [in] nComp Number of components * @param [in] nBoundStates Array with number of bound states for each component */ - inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, unsigned int const* nBoundStates) \ + inline void reserve(unsigned int numElem, unsigned int numSlices, unsigned int nComp, + unsigned int const* nBoundStates) { _kA.reserve(numElem, numSlices, nComp, nBoundStates); _kD.reserve(numElem, numSlices, nComp, nBoundStates); @@ -276,7 +291,8 @@ class ExtLinearParamHandler : public ExternalParamHandlerBase * @param [in,out] workSpace Memory buffer for updated data * @return Externally dependent parameter values */ - inline ParamsHandle update(double t, unsigned int secIdx, const ColumnPosition& colPos, unsigned int nComp, unsigned int const* nBoundStates, LinearBufferAllocator& workSpace) const + inline ParamsHandle update(double t, unsigned int secIdx, const ColumnPosition& colPos, unsigned int nComp, + unsigned int const* nBoundStates, LinearBufferAllocator& workSpace) const { // Allocate params_t and buffer for function evaluation BufferedScalar localParams = workSpace.scalar(); @@ -307,7 +323,10 @@ class ExtLinearParamHandler : public ExternalParamHandlerBase * @param [in,out] workSpace Memory buffer for updated data * @return Tuple with externally dependent parameter values and their time derivatives */ - inline std::tuple updateTimeDerivative(double t, unsigned int secIdx, const ColumnPosition& colPos, unsigned int nComp, unsigned int const* nBoundStates, LinearBufferAllocator& workSpace) const + inline std::tuple updateTimeDerivative(double t, unsigned int secIdx, + const ColumnPosition& colPos, unsigned int nComp, + unsigned int const* nBoundStates, + LinearBufferAllocator& workSpace) const { // Allocate params_t for parameters and their time derivatives BufferedScalar localParams = workSpace.scalar(); @@ -326,15 +345,18 @@ class ExtLinearParamHandler : public ExternalParamHandlerBase _kA.update(cadet::util::dataOfLocalVersion(localParams->kA), extFunBuffer[0], nComp, nBoundStates); _kA.prepareCache(p->kA, workSpace); - _kA.updateTimeDerivative(cadet::util::dataOfLocalVersion(p->kA), extFunBuffer[0], extDerivBuffer[0], nComp, nBoundStates); + _kA.updateTimeDerivative(cadet::util::dataOfLocalVersion(p->kA), extFunBuffer[0], extDerivBuffer[0], nComp, + nBoundStates); _kD.prepareCache(localParams->kD, workSpace); _kD.update(cadet::util::dataOfLocalVersion(localParams->kD), extFunBuffer[1], nComp, nBoundStates); _kD.prepareCache(p->kD, workSpace); - _kD.updateTimeDerivative(cadet::util::dataOfLocalVersion(p->kD), extFunBuffer[1], extDerivBuffer[1], nComp, nBoundStates); + _kD.updateTimeDerivative(cadet::util::dataOfLocalVersion(p->kD), extFunBuffer[1], extDerivBuffer[1], nComp, + nBoundStates); - return std::make_tuple(std::move(localParams), std::move(p));; + return std::make_tuple(std::move(localParams), std::move(p)); + ; } /** @@ -345,7 +367,8 @@ class ExtLinearParamHandler : public ExternalParamHandlerBase * @param [in] nBoundStates Array with bound states for each component * @return Memory size in bytes */ - inline std::size_t cacheSize(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT + inline std::size_t cacheSize(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT { // Required buffer memory: // + params_t object @@ -353,13 +376,12 @@ class ExtLinearParamHandler : public ExternalParamHandlerBase // + buffer for external function time derivative evaluations (2 parameters) // + buffer for actual parameter data (memory for _kA data + memory for _kD data) // + buffer for parameter time derivatives (memory for _kA data + memory for _kD data) - return 2 * sizeof(params_t) + alignof(params_t) - + 2 * 2 * sizeof(double) + alignof(double) - + 2 * (_kA.additionalDynamicMemory(nComp, totalNumBoundStates, nBoundStates) + _kD.additionalDynamicMemory(nComp, totalNumBoundStates, nBoundStates)); + return 2 * sizeof(params_t) + alignof(params_t) + 2 * 2 * sizeof(double) + alignof(double) + + 2 * (_kA.additionalDynamicMemory(nComp, totalNumBoundStates, nBoundStates) + + _kD.additionalDynamicMemory(nComp, totalNumBoundStates, nBoundStates)); } protected: - /** * @brief Validates recently read parameters * @param [in] nComp Number of components @@ -379,30 +401,44 @@ class ExtLinearParamHandler : public ExternalParamHandlerBase ExternalScalarComponentDependentParameter _kD; //!< Handler for desorption rate }; - /** * @brief Defines the linear binding model * @details Implements the linear adsorption model \f$ \frac{\mathrm{d}q}{\mathrm{d}t} = k_a c_p - k_d q \f$. * Multiple bound states are not supported. Components without bound state (i.e., non-binding components) * are supported. - * + * * See @cite Guiochon2006 * @tparam ParamHandler_t Type that can add support for external function dependence */ -template -class LinearBindingBase : public IBindingModel +template class LinearBindingBase : public IBindingModel { public: + LinearBindingBase() : _nComp(0), _nBoundStates(nullptr), _reactionQuasistationarity(0, false) + { + } + virtual ~LinearBindingBase() CADET_NOEXCEPT + { + } - LinearBindingBase() : _nComp(0), _nBoundStates(nullptr), _reactionQuasistationarity(0, false) { } - virtual ~LinearBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } - virtual const char* name() const CADET_NOEXCEPT { return ParamHandler_t::identifier(); } - virtual bool requiresConfiguration() const CADET_NOEXCEPT { return true; } - virtual bool usesParamProviderInDiscretizationConfig() const CADET_NOEXCEPT { return true; } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } + virtual const char* name() const CADET_NOEXCEPT + { + return ParamHandler_t::identifier(); + } + virtual bool requiresConfiguration() const CADET_NOEXCEPT + { + return true; + } + virtual bool usesParamProviderInDiscretizationConfig() const CADET_NOEXCEPT + { + return true; + } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { _nComp = nComp; _nBoundStates = nBound; @@ -418,12 +454,14 @@ class LinearBindingBase : public IBindingModel if (vecKin.size() == 1) { // Treat an array with a single element as scalar - std::fill(_reactionQuasistationarity.begin(), _reactionQuasistationarity.end(), !static_cast(vecKin[0])); + std::fill(_reactionQuasistationarity.begin(), _reactionQuasistationarity.end(), + !static_cast(vecKin[0])); } else if (vecKin.size() < _reactionQuasistationarity.size()) { // Error on too few elements - throw InvalidParameterException("IS_KINETIC has to have at least " + std::to_string(_reactionQuasistationarity.size()) + " elements"); + throw InvalidParameterException("IS_KINETIC has to have at least " + + std::to_string(_reactionQuasistationarity.size()) + " elements"); } else { @@ -453,13 +491,15 @@ class LinearBindingBase : public IBindingModel return true; } - virtual void fillBoundPhaseInitialParameters(ParameterId* params, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) const CADET_NOEXCEPT + virtual void fillBoundPhaseInitialParameters(ParameterId* params, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx) const CADET_NOEXCEPT { unsigned int ctr = 0; for (int c = 0; c < _nComp; ++c) { for (unsigned int bp = 0; bp < _nBoundStates[c]; ++bp, ++ctr) - params[ctr] = makeParamId(hashString("INIT_Q"), unitOpIdx, c, parTypeIdx, bp, ReactionIndep, SectionIndep); + params[ctr] = + makeParamId(hashString("INIT_Q"), unitOpIdx, c, parTypeIdx, bp, ReactionIndep, SectionIndep); } } @@ -467,7 +507,9 @@ class LinearBindingBase : public IBindingModel { std::unordered_map data; std::transform(_parameters.begin(), _parameters.end(), std::inserter(data, data.end()), - [](const std::pair& p) { return std::make_pair(p.first, static_cast(*p.second)); }); + [](const std::pair& p) { + return std::make_pair(p.first, static_cast(*p.second)); + }); return data; } @@ -510,60 +552,72 @@ class LinearBindingBase : public IBindingModel return nullptr; } - virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT + virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT { return _paramHandler.cacheSize(nComp, totalNumBoundStates, nBoundStates); } - virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) { _paramHandler.setExternalFunctions(extFuns, size); } + virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) + { + _paramHandler.setExternalFunctions(extFuns, size); + } // The next three flux() function implementations and two analyticJacobian() function // implementations are usually hidden behind // CADET_BINDINGMODELBASE_BOILERPLATE // which just expands to the six implementations below. - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, - active const* y, active const* yCp, active* res, LinearBufferAllocator workSpace, WithParamSensitivity) const + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, active const* yCp, + active* res, LinearBufferAllocator workSpace, WithParamSensitivity) const { return fluxImpl(t, secIdx, colPos, y, yCp, res, workSpace); } - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, - active const* y, active const* yCp, active* res, LinearBufferAllocator workSpace, WithoutParamSensitivity) const + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, active const* yCp, + active* res, LinearBufferAllocator workSpace, WithoutParamSensitivity) const { return fluxImpl(t, secIdx, colPos, y, yCp, res, workSpace); } - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, - double const* y, double const* yCp, active* res, LinearBufferAllocator workSpace) const + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + active* res, LinearBufferAllocator workSpace) const { return fluxImpl(t, secIdx, colPos, y, yCp, res, workSpace); } - virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, - double const* y, double const* yCp, double* res, LinearBufferAllocator workSpace) const + virtual int flux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + double* res, LinearBufferAllocator workSpace) const { return fluxImpl(t, secIdx, colPos, y, yCp, res, workSpace); } - virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, int offsetCp, linalg::BandMatrix::RowIterator jac, LinearBufferAllocator workSpace) const + virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + int offsetCp, linalg::BandMatrix::RowIterator jac, + LinearBufferAllocator workSpace) const { jacobianImpl(t, secIdx, colPos, y, offsetCp, jac, workSpace); } - virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, int offsetCp, linalg::DenseBandedRowIterator jac, LinearBufferAllocator workSpace) const + virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + int offsetCp, linalg::DenseBandedRowIterator jac, + LinearBufferAllocator workSpace) const { jacobianImpl(t, secIdx, colPos, y, offsetCp, jac, workSpace); } #ifdef ENABLE_DG - virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, int offsetCp, linalg::BandedEigenSparseRowIterator jac, LinearBufferAllocator workSpace) const + virtual void analyticJacobian(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + int offsetCp, linalg::BandedEigenSparseRowIterator jac, + LinearBufferAllocator workSpace) const { jacobianImpl(t, secIdx, colPos, y, offsetCp, jac, workSpace); } #endif - virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yCp, double const* y, double* dResDt, LinearBufferAllocator workSpace) const + virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yCp, double const* y, double* dResDt, + LinearBufferAllocator workSpace) const { if (!hasQuasiStationaryReactions()) return; @@ -590,52 +644,83 @@ class LinearBindingBase : public IBindingModel } } - virtual bool preConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const + virtual bool preConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, + double const* yCp, LinearBufferAllocator workSpace) const { // Due to mass conservation, we need to run a nonlinear solver (although the model is simple). return true; } - virtual void postConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const + virtual void postConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, + double const* yCp, LinearBufferAllocator workSpace) const { - // There's nothing to do here since the algebraic equations have already been solved in preConsistentInitialState() + // There's nothing to do here since the algebraic equations have already been solved in + // preConsistentInitialState() } - virtual bool hasSalt() const CADET_NOEXCEPT { return false; } - virtual bool supportsMultistate() const CADET_NOEXCEPT { return false; } - virtual bool supportsNonBinding() const CADET_NOEXCEPT { return true; } + virtual bool hasSalt() const CADET_NOEXCEPT + { + return false; + } + virtual bool supportsMultistate() const CADET_NOEXCEPT + { + return false; + } + virtual bool supportsNonBinding() const CADET_NOEXCEPT + { + return true; + } virtual bool hasQuasiStationaryReactions() const CADET_NOEXCEPT { - return std::any_of(_reactionQuasistationarity.begin(), _reactionQuasistationarity.end(), [](int i) -> bool { return i; }); + return std::any_of(_reactionQuasistationarity.begin(), _reactionQuasistationarity.end(), + [](int i) -> bool { return i; }); } virtual bool hasDynamicReactions() const CADET_NOEXCEPT { - return std::any_of(_reactionQuasistationarity.begin(), _reactionQuasistationarity.end(), [](int i) -> bool { return !static_cast(i); }); + return std::any_of(_reactionQuasistationarity.begin(), _reactionQuasistationarity.end(), + [](int i) -> bool { return !static_cast(i); }); } - virtual bool dependsOnTime() const CADET_NOEXCEPT { return ParamHandler_t::dependsOnTime(); } - virtual bool requiresWorkspace() const CADET_NOEXCEPT { return ParamHandler_t::requiresWorkspace(); } - virtual unsigned int requiredADdirs() const CADET_NOEXCEPT { return 0; } - virtual int const* reactionQuasiStationarity() const CADET_NOEXCEPT { return _reactionQuasistationarity.data(); } + virtual bool dependsOnTime() const CADET_NOEXCEPT + { + return ParamHandler_t::dependsOnTime(); + } + virtual bool requiresWorkspace() const CADET_NOEXCEPT + { + return ParamHandler_t::requiresWorkspace(); + } + virtual unsigned int requiredADdirs() const CADET_NOEXCEPT + { + return 0; + } + virtual int const* reactionQuasiStationarity() const CADET_NOEXCEPT + { + return _reactionQuasistationarity.data(); + } protected: - int _nComp; //!< Number of components - unsigned int const* _nBoundStates; //!< Array with number of bound states for each component - std::vector _reactionQuasistationarity; //!< Determines whether each bound state is quasi-stationary (@c true) or not (@c false) + int _nComp; //!< Number of components + unsigned int const* _nBoundStates; //!< Array with number of bound states for each component + std::vector _reactionQuasistationarity; //!< Determines whether each bound state is quasi-stationary (@c true) + //!< or not (@c false) ParamHandler_t _paramHandler; //!< Parameters std::unordered_map _parameters; //!< Map used to translate ParameterIds to actual variables - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } template - int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, - StateType const* y, StateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, StateType const* yCp, + ResidualType* res, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Implement -k_a * c_{p,i} + k_d * q_i @@ -656,9 +741,11 @@ class LinearBindingBase : public IBindingModel } template - inline void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + inline void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, int offsetCp, + RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); int bndIdx = 0; for (int i = 0; i < _nComp; ++i) @@ -667,7 +754,7 @@ class LinearBindingBase : public IBindingModel if (_nBoundStates[i] == 0) continue; - jac[0] = static_cast(p->kD[i]); // dres / dq_i + jac[0] = static_cast(p->kD[i]); // dres / dq_i jac[i - bndIdx - offsetCp] = -static_cast(p->kA[i]); // dres / dc_{p,i} // The distance from liquid phase to solid phase is reduced for each non-binding component // since a bound state is neglected. The number of neglected bound states so far is i - bndIdx. @@ -678,7 +765,6 @@ class LinearBindingBase : public IBindingModel ++jac; } } - }; typedef LinearBindingBase LinearBinding; @@ -686,13 +772,13 @@ typedef LinearBindingBase ExternalLinearBinding; namespace binding { - void registerLinearModel(std::unordered_map>& bindings) - { - bindings[LinearBinding::identifier()] = []() { return new LinearBinding(); }; - bindings[ExternalLinearBinding::identifier()] = []() { return new ExternalLinearBinding(); }; - } -} // namespace binding +void registerLinearModel(std::unordered_map>& bindings) +{ + bindings[LinearBinding::identifier()] = []() { return new LinearBinding(); }; + bindings[ExternalLinearBinding::identifier()] = []() { return new ExternalLinearBinding(); }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/MobilePhaseModulatorLangmuirBinding.cpp b/src/libcadet/model/binding/MobilePhaseModulatorLangmuirBinding.cpp index 4649a3951..34189baae 100644 --- a/src/libcadet/model/binding/MobilePhaseModulatorLangmuirBinding.cpp +++ b/src/libcadet/model/binding/MobilePhaseModulatorLangmuirBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -57,38 +57,43 @@ namespace cadet namespace model { -inline const char* MPMLangmuirParamHandler::identifier() CADET_NOEXCEPT { return "MOBILE_PHASE_MODULATOR"; } +inline const char* MPMLangmuirParamHandler::identifier() CADET_NOEXCEPT +{ + return "MOBILE_PHASE_MODULATOR"; +} inline bool MPMLangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() != _gamma.size()) - || (_kA.size() != _beta.size()) || (_kA.size() < nComp)) + if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() != _gamma.size()) || + (_kA.size() != _beta.size()) || (_kA.size() < nComp)) throw InvalidParameterException("MPM_KA, MPM_KD, MPM_QMAX, MPM_GAMMA, and MPM_BETA have to have the same size"); return true; } -inline const char* ExtMPMLangmuirParamHandler::identifier() CADET_NOEXCEPT { return "EXT_MOBILE_PHASE_MODULATOR"; } +inline const char* ExtMPMLangmuirParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_MOBILE_PHASE_MODULATOR"; +} inline bool ExtMPMLangmuirParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() != _gamma.size()) - || (_kA.size() != _beta.size()) || (_kA.size() < nComp)) + if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() != _gamma.size()) || + (_kA.size() != _beta.size()) || (_kA.size() < nComp)) throw InvalidParameterException("MPM_KA, MPM_KD, MPM_QMAX, MPM_GAMMA, and MPM_BETA have to have the same size"); return true; } - /** * @brief Defines the mobile phase modulator Langmuir binding model - * @details Implements the mobile phase modulator Langmuir adsorption model: \f[ \begin{align} + * @details Implements the mobile phase modulator Langmuir adsorption model: \f[ \begin{align} * \frac{\mathrm{d}q_0}{\mathrm{d}t} &= 0 \\ - * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} e^{\gamma_i c_{p,0}} q_{\text{max},i} \left( 1 - \sum_j \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} c_{p,0}^{\beta_i} q_i - * \end{align} \f] - * While @f$ \gamma @f$ describes hydrophobicity, @f$ \beta @f$ accounts for ion-exchange characteristics. - * Multiple bound states are not supported. Component @c 0 is assumed to be salt, which is also assumed to be inert. - * Components without bound state (i.e., non-binding components) are supported. + * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} e^{\gamma_i c_{p,0}} q_{\text{max},i} \left( 1 - + * \sum_j \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} c_{p,0}^{\beta_i} q_i \end{align} \f] While @f$ \gamma @f$ + * describes hydrophobicity, @f$ \beta @f$ accounts for ion-exchange characteristics. Multiple bound states are not + * supported. Component @c 0 is assumed to be salt, which is also assumed to be inert. Components without bound state + * (i.e., non-binding components) are supported. * * Note that the first flux is only used if salt (component @c 0) has a bound state. * It is reasonable to set the number of bound states for salt to @c 0 in order to save time and memory. @@ -100,15 +105,25 @@ template class MobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindingModelBase { public: + MobilePhaseModulatorLangmuirBindingBase() + { + } + virtual ~MobilePhaseModulatorLangmuirBindingBase() CADET_NOEXCEPT + { + } - MobilePhaseModulatorLangmuirBindingBase() { } - virtual ~MobilePhaseModulatorLangmuirBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - virtual bool hasSalt() const CADET_NOEXCEPT { return true; } + virtual bool hasSalt() const CADET_NOEXCEPT + { + return true; + } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { const bool res = BindingModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); @@ -127,18 +142,23 @@ class MobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindingModelB using ParamHandlerBindingModelBase::_nComp; using ParamHandlerBindingModelBase::_nBoundStates; - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } template int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); const double linearThreshold = static_cast(p->linearThreshold); // Salt flux: 0 - // Protein fluxes: -k_{a,i} * exp(\gamma_i * c_{p,0}) * c_{p,i} * q_{max,i} * (1 - \sum q_i / q_{max,i}) + k_{d,i} * c_{p,0}^\beta_i * q_i) + // Protein fluxes: -k_{a,i} * exp(\gamma_i * c_{p,0}) * c_{p,i} * q_{max,i} * (1 - \sum q_i / q_{max,i}) + + // k_{d,i} * c_{p,0}^\beta_i * q_i) ResidualType qSum = 1.0; unsigned int bndIdx = 0; if (_nBoundStates[0] == 1) @@ -174,18 +194,29 @@ class MobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindingModelB // Residual if (yCp[0] <= linearThreshold) { - ResidualType alpha = static_cast(p->kA[i]) * yCp[i] * static_cast(p->qMax[i]) * qSum; + ResidualType alpha = + static_cast(p->kA[i]) * yCp[i] * static_cast(p->qMax[i]) * qSum; // Linearize - ResidualType fThreshold = static_cast(p->kD[i]) * pow(linearThreshold, static_cast(p->beta[i])) * y[bndIdx] - alpha * exp(linearThreshold * static_cast(p->gamma[i])); // f(threshold) - ResidualType fdThreshold = static_cast(p->kD[i]) * pow(linearThreshold, static_cast(p->beta[i]) - 1) * static_cast(p->beta[i]) * y[bndIdx] - alpha * exp(linearThreshold * static_cast(p->gamma[i])) * static_cast(p->gamma[i]); // f'(threshold) + ResidualType fThreshold = + static_cast(p->kD[i]) * pow(linearThreshold, static_cast(p->beta[i])) * + y[bndIdx] - + alpha * exp(linearThreshold * static_cast(p->gamma[i])); // f(threshold) + ResidualType fdThreshold = static_cast(p->kD[i]) * + pow(linearThreshold, static_cast(p->beta[i]) - 1) * + static_cast(p->beta[i]) * y[bndIdx] - + alpha * exp(linearThreshold * static_cast(p->gamma[i])) * + static_cast(p->gamma[i]); // f'(threshold) res[bndIdx] = fThreshold + fdThreshold * (yCp[0] - linearThreshold); } else { // Residual - res[bndIdx] = static_cast(p->kD[i]) * pow(yCp[0], static_cast(p->beta[i])) * y[bndIdx] - static_cast(p->kA[i]) * exp(yCp[0] * static_cast(p->gamma[i])) * yCp[i] * static_cast(p->qMax[i]) * qSum; + res[bndIdx] = + static_cast(p->kD[i]) * pow(yCp[0], static_cast(p->beta[i])) * y[bndIdx] - + static_cast(p->kA[i]) * exp(yCp[0] * static_cast(p->gamma[i])) * yCp[i] * + static_cast(p->qMax[i]) * qSum; } // Next bound component ++bndIdx; @@ -195,9 +226,11 @@ class MobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindingModelB } template - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); const double linearThreshold = static_cast(p->linearThreshold); @@ -222,7 +255,8 @@ class MobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindingModelB ++bndIdx; } - // Protein fluxes: -k_{a,i} * exp(\gamma_i * c_{p,0}) * c_{p,i} * q_{max,i} * (1 - \sum q_i / q_{max,i}) + k_{d,i} * c_{p,0}^\beta_i * q_i) + // Protein fluxes: -k_{a,i} * exp(\gamma_i * c_{p,0}) * c_{p,i} * q_{max,i} * (1 - \sum q_i / q_{max,i}) + + // k_{d,i} * c_{p,0}^\beta_i * q_i) bndIdx = 0; if (_nBoundStates[0] == 1) bndIdx = 1; @@ -245,7 +279,8 @@ class MobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindingModelB kaEGammaC0 = ka * exp(gamma * linearThreshold); // dres_i / dc_{p,0} - jac[-bndIdx - offsetCp] = -kaEGammaC0 * yCp[i] * qMax * qSum * gamma + kdRaw * pow(linearThreshold, beta) * beta * y[bndIdx]; + jac[-bndIdx - offsetCp] = + -kaEGammaC0 * yCp[i] * qMax * qSum * gamma + kdRaw * pow(linearThreshold, beta) * beta * y[bndIdx]; // Getting to c_{p,0}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0}. // This means jac[bndIdx - offsetCp] corresponds to c_{p,0}. } @@ -254,7 +289,8 @@ class MobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindingModelB kaEGammaC0 = ka * exp(gamma * yCp[0]); // dres_i / dc_{p,0} - jac[-bndIdx - offsetCp] = -kaEGammaC0 * yCp[i] * qMax * qSum * gamma + kdRaw * beta * y[bndIdx] * pow(yCp[0], beta - 1.0); + jac[-bndIdx - offsetCp] = + -kaEGammaC0 * yCp[i] * qMax * qSum * gamma + kdRaw * beta * y[bndIdx] * pow(yCp[0], beta - 1.0); // Getting to c_{p,0}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0}. // This means jac[bndIdx - offsetCp] corresponds to c_{p,0}. } @@ -277,7 +313,8 @@ class MobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindingModelB // dres_i / dq_j jac[bndIdx2 - bndIdx] = kaEGammaC0 * yCp[i] * qMax / static_cast(p->qMax[j]); - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] + // corresponds to q_j. ++bndIdx2; } @@ -300,19 +337,23 @@ class MobilePhaseModulatorLangmuirBindingBase : public ParamHandlerBindingModelB } }; - typedef MobilePhaseModulatorLangmuirBindingBase MobilePhaseModulatorLangmuirBinding; typedef MobilePhaseModulatorLangmuirBindingBase ExternalMobilePhaseModulatorLangmuirBinding; namespace binding { - void registerMobilePhaseModulatorLangmuirModel(std::unordered_map>& bindings) - { - bindings[MobilePhaseModulatorLangmuirBinding::identifier()] = []() { return new MobilePhaseModulatorLangmuirBinding(); }; - bindings[ExternalMobilePhaseModulatorLangmuirBinding::identifier()] = []() { return new ExternalMobilePhaseModulatorLangmuirBinding(); }; - } -} // namespace binding +void registerMobilePhaseModulatorLangmuirModel( + std::unordered_map>& bindings) +{ + bindings[MobilePhaseModulatorLangmuirBinding::identifier()] = []() { + return new MobilePhaseModulatorLangmuirBinding(); + }; + bindings[ExternalMobilePhaseModulatorLangmuirBinding::identifier()] = []() { + return new ExternalMobilePhaseModulatorLangmuirBinding(); + }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/MultiComponentSpreadingBinding.cpp b/src/libcadet/model/binding/MultiComponentSpreadingBinding.cpp index 073870cda..2a65d5c5c 100644 --- a/src/libcadet/model/binding/MultiComponentSpreadingBinding.cpp +++ b/src/libcadet/model/binding/MultiComponentSpreadingBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -53,7 +53,10 @@ namespace cadet namespace model { -inline const char* SpreadingParamHandler::identifier() CADET_NOEXCEPT { return "MULTI_COMPONENT_SPREADING"; } +inline const char* SpreadingParamHandler::identifier() CADET_NOEXCEPT +{ + return "MULTI_COMPONENT_SPREADING"; +} inline bool SpreadingParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { @@ -65,45 +68,61 @@ inline bool SpreadingParamHandler::validateConfig(unsigned int nComp, unsigned i return true; } -inline const char* ExtSpreadingParamHandler::identifier() CADET_NOEXCEPT { return "EXT_MULTI_COMPONENT_SPREADING"; } +inline const char* ExtSpreadingParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_MULTI_COMPONENT_SPREADING"; +} inline bool ExtSpreadingParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { if ((_kA.size() != _kD.size()) || (_kA.size() != _qMax.size()) || (_kA.size() < nComp * 2)) throw InvalidParameterException("EXT_MCSPR_KA, EXT_MCSPR_KD, and EXT_MCSPR_QMAX have to have the same size"); if ((_k12.size() != _k21.size()) || (_k12.size() < nComp)) - throw InvalidParameterException("EXT_MCSPR_K12 and EXT_MCSPR_K21 have to have the same size (number of components)"); + throw InvalidParameterException( + "EXT_MCSPR_K12 and EXT_MCSPR_K21 have to have the same size (number of components)"); return true; } - /** * @brief Defines the multi component spreading binding model - * @details Implements the multi component spreading adsorption model: \f[ \begin{align} - * \frac{\mathrm{d} q_i^A}{\mathrm{d} t} &= \left( k_a^A\: c_{p,i} - k_{AB} q_i^A \right) q_{\text{max},i}^A \left( 1 - \sum_{j=1}^{N_{\text{comp}}} \frac{q_j^A}{q_{\text{max},j}^A} - \sum_{j=1}^{N_{\text{comp}}} \frac{q_j^B}{q_{\text{max},j}^B} \right) - k_d^A q_i^A + k_{BA} q_i^B \\ - * \frac{\mathrm{d} q_i^B}{\mathrm{d} t} &= \left( k_a^B\: c_{p,i} + k_{AB} q_i^A \right) q_{\text{max},i}^A \left( 1 - \sum_{j=1}^{N_{\text{comp}}} \frac{q_j^A}{q_{\text{max},j}^A} - \sum_{j=1}^{N_{\text{comp}}} \frac{q_j^B}{q_{\text{max},j}^B} \right) - \left( k_d^B + k_{BA} \right) q_i^B + * @details Implements the multi component spreading adsorption model: \f[ \begin{align} + * \frac{\mathrm{d} q_i^A}{\mathrm{d} t} &= \left( k_a^A\: c_{p,i} - k_{AB} q_i^A \right) + * q_{\text{max},i}^A \left( 1 - \sum_{j=1}^{N_{\text{comp}}} \frac{q_j^A}{q_{\text{max},j}^A} - + * \sum_{j=1}^{N_{\text{comp}}} \frac{q_j^B}{q_{\text{max},j}^B} \right) - k_d^A q_i^A + k_{BA} q_i^B \\ + * \frac{\mathrm{d} q_i^B}{\mathrm{d} t} &= \left( k_a^B\: c_{p,i} + k_{AB} q_i^A \right) + * q_{\text{max},i}^A \left( 1 - \sum_{j=1}^{N_{\text{comp}}} \frac{q_j^A}{q_{\text{max},j}^A} - + * \sum_{j=1}^{N_{\text{comp}}} \frac{q_j^B}{q_{\text{max},j}^B} \right) - \left( k_d^B + k_{BA} \right) q_i^B * \end{align} \f] - * Here, a second bound state $q_i^B$ is added to the Langmuir model and the exchange between the two bound - * states $q_i^A$ and $q_i^B$ is allowed. The second bound state may correspond to a different orientation + * Here, a second bound state $q_i^B$ is added to the Langmuir model and the exchange between the two bound + * states $q_i^A$ and $q_i^B$ is allowed. The second bound state may correspond to a different orientation * on the surface or a different folding state of the molecule. - * While components without bound state (i.e., non-binding components) are supported, all other components must have + * While components without bound state (i.e., non-binding components) are supported, all other components must + * have * @c 2 bound states. - * - * Internal state vector layout is component-major. First, all bound states of component 0 are placed, then all bound states of component 1, etc. + * + * Internal state vector layout is component-major. First, all bound states of component 0 are placed, then all + * bound states of component 1, etc. * @tparam ParamHandler_t Type that can add support for external function dependence */ template class MultiComponentSpreadingBindingBase : public ParamHandlerBindingModelBase { public: + MultiComponentSpreadingBindingBase() + { + } + virtual ~MultiComponentSpreadingBindingBase() CADET_NOEXCEPT + { + } - MultiComponentSpreadingBindingBase() { } - virtual ~MultiComponentSpreadingBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { const bool res = BindingModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); @@ -114,7 +133,8 @@ class MultiComponentSpreadingBindingBase : public ParamHandlerBindingModelBase

                                                int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); ResidualType qSum = 1.0; unsigned int bndIdx = 0; @@ -178,8 +211,11 @@ class MultiComponentSpreadingBindingBase : public ParamHandlerBindingModelBase

                                                (localKd[i]) * y[bndIdx] - static_cast(p->k21[i]) * y[bndIdx + _numBindingComp] - - (static_cast(localKa[i]) * yCp[i] - static_cast(p->k12[i]) * y[bndIdx]) * static_cast(localQmax[i]) * qSum; + res[bndIdx] = + static_cast(localKd[i]) * y[bndIdx] - + static_cast(p->k21[i]) * y[bndIdx + _numBindingComp] - + (static_cast(localKa[i]) * yCp[i] - static_cast(p->k12[i]) * y[bndIdx]) * + static_cast(localQmax[i]) * qSum; // Next bound component ++bndIdx; @@ -197,8 +233,10 @@ class MultiComponentSpreadingBindingBase : public ParamHandlerBindingModelBase

                                                (localKd[i]) + static_cast(p->k21[i])) * y[bndIdx] - - (static_cast(localKa[i]) * yCp[i] + static_cast(p->k12[i]) * y[bndIdx - _numBindingComp]) * static_cast(localQmax[i]) * qSum; + res[bndIdx] = (static_cast(localKd[i]) + static_cast(p->k21[i])) * y[bndIdx] - + (static_cast(localKa[i]) * yCp[i] + + static_cast(p->k12[i]) * y[bndIdx - _numBindingComp]) * + static_cast(localQmax[i]) * qSum; // Next bound component ++bndIdx; @@ -208,9 +246,11 @@ class MultiComponentSpreadingBindingBase : public ParamHandlerBindingModelBase

                                                - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); double qSum = 1.0; int bndIdx = 0; @@ -252,7 +292,8 @@ class MultiComponentSpreadingBindingBase : public ParamHandlerBindingModelBase

                                                (p->k12[i])) * static_cast(qMax1[i]); + const double factor = + (ka * yCp[i] - y[bndIdx] * static_cast(p->k12[i])) * static_cast(qMax1[i]); int bndIdx2 = 0; for (int j = 0; j < _nComp; ++j) { @@ -262,13 +303,15 @@ class MultiComponentSpreadingBindingBase : public ParamHandlerBindingModelBase

                                                (qMax1[j]); - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] + // corresponds to q_j. ++bndIdx2; } // Add to dres_i / dq_i^A - jac[0] += kd + static_cast(p->k12[i]) * static_cast(qMax1[i]) * qSum; // last summand by product rule + jac[0] += kd + static_cast(p->k12[i]) * static_cast(qMax1[i]) * + qSum; // last summand by product rule // Fill dres_i / dq_j^B for (int j = 0; j < _nComp; ++j) @@ -279,7 +322,8 @@ class MultiComponentSpreadingBindingBase : public ParamHandlerBindingModelBase

                                                (qMax2[j]); - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] + // corresponds to q_j. ++bndIdx2; } @@ -311,7 +355,9 @@ class MultiComponentSpreadingBindingBase : public ParamHandlerBindingModelBase

                                                (_numBindingComp)] * static_cast(p->k12[i])) * static_cast(qMax1[i]); + const double factor = + (ka * yCp[i] + y[bndIdx - static_cast(_numBindingComp)] * static_cast(p->k12[i])) * + static_cast(qMax1[i]); int bndIdx2 = 0; for (int j = 0; j < _nComp; ++j) { @@ -321,7 +367,8 @@ class MultiComponentSpreadingBindingBase : public ParamHandlerBindingModelBase

                                                (qMax1[j]); - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] + // corresponds to q_j. ++bndIdx2; } @@ -335,7 +382,8 @@ class MultiComponentSpreadingBindingBase : public ParamHandlerBindingModelBase

                                                (qMax2[j]); - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] + // corresponds to q_j. ++bndIdx2; } @@ -344,7 +392,8 @@ class MultiComponentSpreadingBindingBase : public ParamHandlerBindingModelBase

                                                (p->k21[i]); // Add to dres_i / dq_i^A - jac[-static_cast(_numBindingComp)] -= static_cast(p->k12[i]) * static_cast(qMax1[i]) * qSum; // last summand by product rule + jac[-static_cast(_numBindingComp)] -= + static_cast(p->k12[i]) * static_cast(qMax1[i]) * qSum; // last summand by product rule // Advance to next flux and Jacobian row ++bndIdx; @@ -353,19 +402,21 @@ class MultiComponentSpreadingBindingBase : public ParamHandlerBindingModelBase

                                                MultiComponentSpreadingBinding; typedef MultiComponentSpreadingBindingBase ExternalMultiComponentSpreadingBinding; namespace binding { - void registerMultiComponentSpreadingModel(std::unordered_map>& bindings) - { - bindings[MultiComponentSpreadingBinding::identifier()] = []() { return new MultiComponentSpreadingBinding(); }; - bindings[ExternalMultiComponentSpreadingBinding::identifier()] = []() { return new ExternalMultiComponentSpreadingBinding(); }; - } -} // namespace binding +void registerMultiComponentSpreadingModel( + std::unordered_map>& bindings) +{ + bindings[MultiComponentSpreadingBinding::identifier()] = []() { return new MultiComponentSpreadingBinding(); }; + bindings[ExternalMultiComponentSpreadingBinding::identifier()] = []() { + return new ExternalMultiComponentSpreadingBinding(); + }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/MultiStateStericMassActionBinding.cpp b/src/libcadet/model/binding/MultiStateStericMassActionBinding.cpp index 705c4e6ea..8d7071401 100644 --- a/src/libcadet/model/binding/MultiStateStericMassActionBinding.cpp +++ b/src/libcadet/model/binding/MultiStateStericMassActionBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -40,7 +40,8 @@ ], "constantParameters": [ - { "type": "ReferenceConcentrationParameter", "varName": ["refC0", "refQ"], "objName": "refConcentration", "confPrefix": "MSSMA_"} + { "type": "ReferenceConcentrationParameter", "varName": ["refC0", "refQ"], "objName": "refConcentration", +"confPrefix": "MSSMA_"} ] } */ @@ -62,13 +63,17 @@ namespace cadet namespace model { -inline const char* MSSMAParamHandler::identifier() CADET_NOEXCEPT { return "MULTISTATE_STERIC_MASS_ACTION"; } +inline const char* MSSMAParamHandler::identifier() CADET_NOEXCEPT +{ + return "MULTISTATE_STERIC_MASS_ACTION"; +} inline bool MSSMAParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { const unsigned int totalBoundStates = numBoundStates(nBoundStates, nComp); - if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _sigma.size()) || (_kA.size() != totalBoundStates)) + if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _sigma.size()) || + (_kA.size() != totalBoundStates)) throw InvalidParameterException("MSSMA_KA, MSSMA_KD, MSSMA_NU, and MSSMA_SIGMA have to have the same size"); util::SlicedVector& n = _nu.get(); @@ -77,8 +82,9 @@ inline bool MSSMAParamHandler::validateConfig(unsigned int nComp, unsigned int c active const* curNu = n[i]; for (unsigned int j = 1; j < nBoundStates[i]; ++j) { - if (curNu[j-1] > curNu[j]) - throw InvalidParameterException("MSSMA_NU is not monotonically increasing for component " + std::to_string(i) + " at bound state " + std::to_string(j)); + if (curNu[j - 1] > curNu[j]) + throw InvalidParameterException("MSSMA_NU is not monotonically increasing for component " + + std::to_string(i) + " at bound state " + std::to_string(j)); } } @@ -89,14 +95,19 @@ inline bool MSSMAParamHandler::validateConfig(unsigned int nComp, unsigned int c return true; } -inline const char* ExtMSSMAParamHandler::identifier() CADET_NOEXCEPT { return "EXT_MULTISTATE_STERIC_MASS_ACTION"; } +inline const char* ExtMSSMAParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_MULTISTATE_STERIC_MASS_ACTION"; +} inline bool ExtMSSMAParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { const unsigned int totalBoundStates = numBoundStates(nBoundStates, nComp); - if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _sigma.size()) || (_kA.size() != totalBoundStates)) - throw InvalidParameterException("EXT_MSSMA_KA, EXT_MSSMA_KD, EXT_MSSMA_NU, and EXT_MSSMA_SIGMA have to have the same size"); + if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _sigma.size()) || + (_kA.size() != totalBoundStates)) + throw InvalidParameterException( + "EXT_MSSMA_KA, EXT_MSSMA_KD, EXT_MSSMA_NU, and EXT_MSSMA_SIGMA have to have the same size"); // Assume monovalent salt ions by default if (_nu.base().native(0) <= 0.0) @@ -105,40 +116,51 @@ inline bool ExtMSSMAParamHandler::validateConfig(unsigned int nComp, unsigned in return true; } - /** * @brief Defines the Multi-State-SMA binding model - * @details Implements the Multi-State-SMA adsorption model, which introduces @f$ M_i - 1 @f$ additional bound states @f$ q_i^{(j)} @f$ - * for each component @f$ i @f$ with transitions between them: \f[ \begin{align} + * @details Implements the Multi-State-SMA adsorption model, which introduces @f$ M_i - 1 @f$ additional bound states + *@f$ q_i^{(j)} @f$ for each component @f$ i @f$ with transitions between them: \f[ \begin{align} * q_0 &= \Lambda - \sum_{i=1}^{n_{\text{comp}}} \sum_{j=1}^{M_i} \nu_i^{(j)} q_i^{(j)} \\ - * \frac{\mathrm{d}q_i^{(j)}}{\mathrm{d}t} &= k_{a,i}^{(j)} c_{p,i} \bar{q}_0^{\nu_i^{(j)}} - k_{d,i}^{(j)}\: q_i^{(j)}\: c_{p,0}^{\nu_i^{(j)}} + \sum_{\ell = 1}^{j-1} r^{(i)}_{j\ell} + \sum_{\ell = j+1}^{M} r^{(i)}_{j\ell}, - * \end{align} \f] - * where @f$ i = 1, \dots, N_{\text{comp}} @f$ and @f$ j = 1, \dots, M_i @f$. - * The net flux @f$ r^{(i)}_{j\ell} @f$ from @f$ q_i^{(\ell)} @f$ into @f$ q_i^{(j)} @f$ is given by \f[ \begin{align*} + * \frac{\mathrm{d}q_i^{(j)}}{\mathrm{d}t} &= k_{a,i}^{(j)} c_{p,i} \bar{q}_0^{\nu_i^{(j)}} - + *k_{d,i}^{(j)}\: q_i^{(j)}\: c_{p,0}^{\nu_i^{(j)}} + \sum_{\ell = 1}^{j-1} r^{(i)}_{j\ell} + \sum_{\ell = j+1}^{M} + *r^{(i)}_{j\ell}, \end{align} \f] where @f$ i = 1, \dots, N_{\text{comp}} @f$ and @f$ j = 1, \dots, M_i @f$. The net + *flux @f$ r^{(i)}_{j\ell} @f$ from @f$ q_i^{(\ell)} @f$ into @f$ q_i^{(j)} @f$ is given by \f[ \begin{align*} * r_{j,\ell}^{(i)} = \begin{cases} - * k_{\ell j}^{(i)} q_i^{(\ell)} \bar{q}_0^{\nu_i^{(j)} - \nu_i^{(\ell)}} - k_{j \ell}^{(i)} q_i^{(j)} c_{p,0}^{\nu_i^{(j)} - \nu_i^{(\ell)}}, & \ell < j, \\ - * k_{\ell j}^{(i)} q_i^{(\ell)} c_{p,0}^{\nu_i^{(\ell)} - \nu_i^{(j)}} - k_{j \ell}^{(i)} q_i^{(j)} \bar{q}_0^{\nu_i^{(\ell)} - \nu_i^{(j)}}, & \ell > j - * \end{cases} \end{align*} \f] - * for @f$ 1 \leq \ell, j \leq M_i @f$ and @f$ i = 1, \dots, N_{\text{comp}} @f$. - * Here, each component possesses several different binding states with increasing strength: @f[ \nu_i^{(j)} \leq \nu_i^{(j+1)} @f] - * A molecule of component @f$ i @f$ can transition from bound state @f$ \ell @f$ to state @f$ j @f$ with given rate @f$ k_{\ell j}^{(i)} @f$. - * - * Components without bound state (i.e., non-binding components) are supported and all components are allowed to have - * varying numbers of bound states (except for salt which has only one bound state @f$ q_0 @f$). Component @c 0 is assumed to be salt. + * k_{\ell j}^{(i)} q_i^{(\ell)} \bar{q}_0^{\nu_i^{(j)} - \nu_i^{(\ell)}} - k_{j \ell}^{(i)} q_i^{(j)} + *c_{p,0}^{\nu_i^{(j)} - \nu_i^{(\ell)}}, & \ell < j, \\ k_{\ell j}^{(i)} q_i^{(\ell)} c_{p,0}^{\nu_i^{(\ell)} - + *\nu_i^{(j)}} - k_{j \ell}^{(i)} q_i^{(j)} \bar{q}_0^{\nu_i^{(\ell)} - \nu_i^{(j)}}, & \ell > j \end{cases} + *\end{align*} \f] for @f$ 1 \leq \ell, j \leq M_i @f$ and @f$ i = 1, \dots, N_{\text{comp}} @f$. Here, each component + *possesses several different binding states with increasing strength: @f[ \nu_i^{(j)} \leq \nu_i^{(j+1)} @f] A molecule + *of component @f$ i @f$ can transition from bound state @f$ \ell @f$ to state @f$ j @f$ with given rate @f$ k_{\ell + *j}^{(i)} @f$. + * + * Components without bound state (i.e., non-binding components) are supported and all components are allowed + *to have varying numbers of bound states (except for salt which has only one bound state @f$ q_0 @f$). Component @c 0 + *is assumed to be salt. * @tparam ParamHandler_t Type that can add support for external function dependence */ template class MultiStateStericMassActionBindingBase : public ParamHandlerBindingModelBase { public: + MultiStateStericMassActionBindingBase() + { + } + virtual ~MultiStateStericMassActionBindingBase() CADET_NOEXCEPT + { + } - MultiStateStericMassActionBindingBase() { } - virtual ~MultiStateStericMassActionBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } - virtual const char* name() const CADET_NOEXCEPT { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } + virtual const char* name() const CADET_NOEXCEPT + { + return ParamHandler_t::identifier(); + } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { const bool res = BindingModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); @@ -156,18 +178,32 @@ class MultiStateStericMassActionBindingBase : public ParamHandlerBindingModelBas return res; } - virtual bool hasSalt() const CADET_NOEXCEPT { return true; } - virtual bool supportsMultistate() const CADET_NOEXCEPT { return true; } - virtual bool supportsNonBinding() const CADET_NOEXCEPT { return true; } - virtual bool hasQuasiStationaryReactions() const CADET_NOEXCEPT { return true; } + virtual bool hasSalt() const CADET_NOEXCEPT + { + return true; + } + virtual bool supportsMultistate() const CADET_NOEXCEPT + { + return true; + } + virtual bool supportsNonBinding() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasQuasiStationaryReactions() const CADET_NOEXCEPT + { + return true; + } - virtual bool preConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const + virtual bool preConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, + double const* yCp, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Compute salt component from given bound states q_i^j - // Salt equation: q_0 * nu_0 - Lambda + Sum[Sum[nu_i^j * q_i^j, j], i] == 0 + // Salt equation: q_0 * nu_0 - Lambda + Sum[Sum[nu_i^j * q_i^j, j], i] == 0 // <=> q_0 == (Lambda - Sum[Sum[nu_i^j * q_i^j, j], i]) / nu_0 y[0] = static_cast(p->lambda); @@ -193,7 +229,8 @@ class MultiStateStericMassActionBindingBase : public ParamHandlerBindingModelBas return true; } - virtual void postConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const + virtual void postConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, + double const* yCp, LinearBufferAllocator workSpace) const { preConsistentInitialState(t, secIdx, colPos, y, yCp, workSpace); } @@ -206,19 +243,23 @@ class MultiStateStericMassActionBindingBase : public ParamHandlerBindingModelBas using ParamHandlerBindingModelBase::_nComp; using ParamHandlerBindingModelBase::_nBoundStates; - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } template int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { using CpStateParamType = typename DoubleActivePromoter::type; using StateParamType = typename DoubleActivePromoter::type; - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); - // Salt flux: q_0 * nu_0 - Lambda + Sum[Sum[nu_i^j * q_i^j, j], i] == 0 - // <=> q_0 * nu_0 == Lambda - Sum[Sum[nu_i^j * q_i^j, j], i] + // Salt flux: q_0 * nu_0 - Lambda + Sum[Sum[nu_i^j * q_i^j, j], i] == 0 + // <=> q_0 * nu_0 == Lambda - Sum[Sum[nu_i^j * q_i^j, j], i] // Also compute \bar{q}_0 = q_0 * nu_0 - Sum[Sum[sigma_i^j * q_i^j, j], i] res[0] = static_cast(p->nu.native(0)) * y[0] - static_cast(p->lambda); StateParamType q0_bar = static_cast(p->nu.native(0)) * y[0]; @@ -262,35 +303,45 @@ class MultiStateStericMassActionBindingBase : public ParamHandlerBindingModelBas // Loop over bound states j of component i for (unsigned int j = 0; j < nBoundStatesI; ++j) { - const ParamType nu_over_nu0 = static_cast(curNu[j]) / static_cast(p->nu.native(0)); + const ParamType nu_over_nu0 = + static_cast(curNu[j]) / static_cast(p->nu.native(0)); const CpStateParamType c0_pow_nu = pow(yCp0_divRef, nu_over_nu0); const StateParamType q0_bar_pow_nu = pow(q0_bar_divRef, nu_over_nu0); // Calculate residual // Adsorption and desorption - res[bndIdx] = static_cast(curKd[j]) * y[bndIdx] * c0_pow_nu - static_cast(curKa[j]) * yCp[i] * q0_bar_pow_nu; + res[bndIdx] = static_cast(curKd[j]) * y[bndIdx] * c0_pow_nu - + static_cast(curKa[j]) * yCp[i] * q0_bar_pow_nu; // Conversion to and from weaker states for (unsigned int l = 0; l < j; ++l) { - const ParamType nuDiff_over_nu0 = (static_cast(curNu[j]) - static_cast(curNu[l])) / static_cast(p->nu.native(0)); + const ParamType nuDiff_over_nu0 = + (static_cast(curNu[j]) - static_cast(curNu[l])) / + static_cast(p->nu.native(0)); // Conversion to weaker state - res[bndIdx] += static_cast(curRates[nBoundStatesI * j + l]) * y[bndIdx] * pow(yCp0_divRef, nuDiff_over_nu0); + res[bndIdx] += static_cast(curRates[nBoundStatesI * j + l]) * y[bndIdx] * + pow(yCp0_divRef, nuDiff_over_nu0); // Conversion from weaker state - res[bndIdx] -= static_cast(curRates[nBoundStatesI * l + j]) * y[bndIdx - j + l] * pow(q0_bar_divRef, nuDiff_over_nu0); + res[bndIdx] -= static_cast(curRates[nBoundStatesI * l + j]) * y[bndIdx - j + l] * + pow(q0_bar_divRef, nuDiff_over_nu0); // Getting to q_i^l: bndIdx takes us to q_i^j, subtracting j sets the pointer back // to q_i^0, and adding l brings us to q_i^l. // This means y[bndIdx - j + l] corresponds to q_i^l } // Conversion to and from stronger states - for (unsigned int l = j+1; l < nBoundStatesI; ++l) + for (unsigned int l = j + 1; l < nBoundStatesI; ++l) { - const ParamType nuDiff_over_nu0 = (static_cast(curNu[l]) - static_cast(curNu[j])) / static_cast(p->nu.native(0)); + const ParamType nuDiff_over_nu0 = + (static_cast(curNu[l]) - static_cast(curNu[j])) / + static_cast(p->nu.native(0)); // Conversion to stronger state - res[bndIdx] += static_cast(curRates[nBoundStatesI * j + l]) * y[bndIdx] * pow(q0_bar_divRef, nuDiff_over_nu0); + res[bndIdx] += static_cast(curRates[nBoundStatesI * j + l]) * y[bndIdx] * + pow(q0_bar_divRef, nuDiff_over_nu0); // Conversion from stronger state - res[bndIdx] -= static_cast(curRates[nBoundStatesI * l + j]) * y[bndIdx - j + l] * pow(yCp0_divRef, nuDiff_over_nu0); + res[bndIdx] -= static_cast(curRates[nBoundStatesI * l + j]) * y[bndIdx - j + l] * + pow(yCp0_divRef, nuDiff_over_nu0); } // Next bound component @@ -301,9 +352,11 @@ class MultiStateStericMassActionBindingBase : public ParamHandlerBindingModelBas } template - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); double q0_bar = static_cast(p->nu.native(0)) * y[0]; @@ -350,8 +403,9 @@ class MultiStateStericMassActionBindingBase : public ParamHandlerBindingModelBas for (int j = 0; j < static_cast(nBoundStatesI); ++j) { - // Getting to c_{p,0}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0}. This means jac[-bndIdx - offsetCp] corresponds to c_{p,0}. - // Getting to c_{p,i}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0} and a +i to c_{p,i}. + // Getting to c_{p,0}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0}. This means jac[-bndIdx - + // offsetCp] corresponds to c_{p,0}. Getting to c_{p,i}: -bndIdx takes us to q_0, another -offsetCp to + // c_{p,0} and a +i to c_{p,i}. // This means jac[i - bndIdx - offsetCp] corresponds to c_{p,i}. const double ka = static_cast(curKa[j]); @@ -359,9 +413,9 @@ class MultiStateStericMassActionBindingBase : public ParamHandlerBindingModelBas const double nu = static_cast(curNu[j]); const double nu_over_nu0 = nu / static_cast(p->nu.native(0)); - const double c0_pow_nu = pow(yCp0_divRef, nu_over_nu0); + const double c0_pow_nu = pow(yCp0_divRef, nu_over_nu0); const double q0_bar_pow_nu = pow(q0_bar_divRef, nu_over_nu0); - const double c0_pow_nu_m1_divRef = pow(yCp0_divRef, nu_over_nu0 - 1.0) / refC0; + const double c0_pow_nu_m1_divRef = pow(yCp0_divRef, nu_over_nu0 - 1.0) / refC0; const double q0_bar_pow_nu_m1_divRef = nu_over_nu0 * pow(q0_bar_divRef, nu_over_nu0 - 1.0) / refQ; // dres_i / dc_{p,0} @@ -379,7 +433,8 @@ class MultiStateStericMassActionBindingBase : public ParamHandlerBindingModelBas for (unsigned int j2 = 0; j2 < _nBoundStates[i2]; ++j2) { // dres_i / dq_{i2}^{j2} - jac[bndIdx2 - bndIdx] = ka * yCp[i] * q0_bar_pow_nu_m1_divRef * static_cast(curSigma[j2]); + jac[bndIdx2 - bndIdx] = + ka * yCp[i] * q0_bar_pow_nu_m1_divRef * static_cast(curSigma[j2]); // Getting to q_{i2}^{j2}: -bndIdx takes us to q_1^0, another +bndIdx2 to q_{i2}^{j2}. // This means jac[bndIdx2 - bndIdx] corresponds to q_{i2}^{j2}. @@ -391,22 +446,26 @@ class MultiStateStericMassActionBindingBase : public ParamHandlerBindingModelBas jac[0] += kd * c0_pow_nu; // Handle flux terms - + // Conversion to and from weaker states for (int l = 0; l < j; ++l) { const double nuDiff = (nu - static_cast(curNu[l])) / static_cast(p->nu.native(0)); - const double q0_bar_pow_nudiff_deriv = static_cast(curRates[nBoundStatesI * l + j]) * y[bndIdx - j + l] * nuDiff * pow(q0_bar_divRef, nuDiff - 1.0) / refQ; + const double q0_bar_pow_nudiff_deriv = static_cast(curRates[nBoundStatesI * l + j]) * + y[bndIdx - j + l] * nuDiff * + pow(q0_bar_divRef, nuDiff - 1.0) / refQ; // dres_i / dc_{p,0} - jac[-bndIdx - offsetCp] += static_cast(curRates[nBoundStatesI * j + l]) * y[bndIdx] * nuDiff * pow(yCp0_divRef, nuDiff - 1.0) / refC0; + jac[-bndIdx - offsetCp] += static_cast(curRates[nBoundStatesI * j + l]) * y[bndIdx] * + nuDiff * pow(yCp0_divRef, nuDiff - 1.0) / refC0; // dres_i / dq_0 jac[-bndIdx] -= q0_bar_pow_nudiff_deriv * static_cast(p->nu.native(0)); // dres_i / dq_i^j (current outer loop element) jac[0] += static_cast(curRates[nBoundStatesI * j + l]) * pow(yCp0_divRef, nuDiff); // dres_i / dq_i^l (without dq0_bar / dq_i^l) jac[l - j] -= static_cast(curRates[nBoundStatesI * l + j]) * pow(q0_bar_divRef, nuDiff); - // dres_i / dq_{i2}^{j2} (accounts for all dq0_bar / dq_{i2}^{j2} including missing dq0_bar / dq_i^l from above) + // dres_i / dq_{i2}^{j2} (accounts for all dq0_bar / dq_{i2}^{j2} including missing dq0_bar / dq_i^l + // from above) bndIdx2 = 1; for (int i2 = 1; i2 < _nComp; ++i2) { @@ -417,13 +476,15 @@ class MultiStateStericMassActionBindingBase : public ParamHandlerBindingModelBas } // Conversion to and from stronger states - for (int l = j+1; l < static_cast(nBoundStatesI); ++l) + for (int l = j + 1; l < static_cast(nBoundStatesI); ++l) { const double nuDiff = (static_cast(curNu[l]) - nu) / static_cast(p->nu.native(0)); - const double q0_bar_pow_nudiff_deriv = static_cast(curRates[nBoundStatesI * j + l]) * y[bndIdx] * nuDiff * pow(q0_bar_divRef, nuDiff - 1.0) / refQ; + const double q0_bar_pow_nudiff_deriv = static_cast(curRates[nBoundStatesI * j + l]) * + y[bndIdx] * nuDiff * pow(q0_bar_divRef, nuDiff - 1.0) / refQ; // dres_i / dc_{p,0} - jac[-bndIdx - offsetCp] -= static_cast(curRates[nBoundStatesI * l + j]) * y[bndIdx - j + l] * nuDiff * pow(yCp0_divRef, nuDiff - 1.0) / refC0; + jac[-bndIdx - offsetCp] -= static_cast(curRates[nBoundStatesI * l + j]) * + y[bndIdx - j + l] * nuDiff * pow(yCp0_divRef, nuDiff - 1.0) / refC0; // dres_i / dq_0 jac[-bndIdx] += q0_bar_pow_nudiff_deriv * static_cast(p->nu.native(0)); // dres_i / dq_i^j (current outer loop element) @@ -444,23 +505,27 @@ class MultiStateStericMassActionBindingBase : public ParamHandlerBindingModelBas ++bndIdx; ++jac; } - } + } } }; - typedef MultiStateStericMassActionBindingBase MultiStateStericMassActionBinding; typedef MultiStateStericMassActionBindingBase ExternalMultiStateStericMassActionBinding; namespace binding { - void registerMultiStateStericMassActionModel(std::unordered_map>& bindings) - { - bindings[MultiStateStericMassActionBinding::identifier()] = []() { return new MultiStateStericMassActionBinding(); }; - bindings[ExternalMultiStateStericMassActionBinding::identifier()] = []() { return new ExternalMultiStateStericMassActionBinding(); }; - } -} // namespace binding +void registerMultiStateStericMassActionModel( + std::unordered_map>& bindings) +{ + bindings[MultiStateStericMassActionBinding::identifier()] = []() { + return new MultiStateStericMassActionBinding(); + }; + bindings[ExternalMultiStateStericMassActionBinding::identifier()] = []() { + return new ExternalMultiStateStericMassActionBinding(); + }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/RefConcentrationSupport.hpp b/src/libcadet/model/binding/RefConcentrationSupport.hpp index 0101bcb32..b575f78dd 100644 --- a/src/libcadet/model/binding/RefConcentrationSupport.hpp +++ b/src/libcadet/model/binding/RefConcentrationSupport.hpp @@ -28,7 +28,8 @@ namespace cadet { template -inline bool readScalarParameterOrArray(std::vector& dest, IParameterProvider& paramProvider, const std::string& dataSet, unsigned int nExpand); +inline bool readScalarParameterOrArray(std::vector& dest, IParameterProvider& paramProvider, + const std::string& dataSet, unsigned int nExpand); /** * @brief Reads multiple reference concentrations from the given parameter provider @@ -43,7 +44,9 @@ inline bool readScalarParameterOrArray(std::vector& dest, IParameterPro * @tparam ValType Type of the parameter, such as @c active or @c double */ template -inline void readReferenceConcentrations(IParameterProvider& paramProvider, unsigned int numRefConc, const std::string& prefix, std::vector& refC0, std::vector& refQ) +inline void readReferenceConcentrations(IParameterProvider& paramProvider, unsigned int numRefConc, + const std::string& prefix, std::vector& refC0, + std::vector& refQ) { if (paramProvider.exists(prefix + "REFC0")) readScalarParameterOrArray(refC0, paramProvider, prefix + "REFC0", 1); @@ -103,7 +106,8 @@ inline void readReferenceConcentrations(IParameterProvider& paramProvider, unsig * @tparam ValType Type of the parameter, such as @c active or @c double */ template -inline void readReferenceConcentrations(IParameterProvider& paramProvider, const std::string& prefix, ValType& refC0, ValType& refQ) +inline void readReferenceConcentrations(IParameterProvider& paramProvider, const std::string& prefix, ValType& refC0, + ValType& refQ) { if (paramProvider.exists(prefix + "REFC0")) refC0 = paramProvider.getDouble(prefix + "REFC0"); @@ -116,6 +120,6 @@ inline void readReferenceConcentrations(IParameterProvider& paramProvider, const refQ = 1.0; } -} // namespace cadet +} // namespace cadet -#endif // LIBCADET_BINDINGMODELREFCONCSUPPORT_HPP_ +#endif // LIBCADET_BINDINGMODELREFCONCSUPPORT_HPP_ diff --git a/src/libcadet/model/binding/SaskaBinding.cpp b/src/libcadet/model/binding/SaskaBinding.cpp index defb3504b..3e1852afc 100644 --- a/src/libcadet/model/binding/SaskaBinding.cpp +++ b/src/libcadet/model/binding/SaskaBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -48,7 +48,10 @@ namespace cadet namespace model { -inline const char* SaskaParamHandler::identifier() CADET_NOEXCEPT { return "SASKA"; } +inline const char* SaskaParamHandler::identifier() CADET_NOEXCEPT +{ + return "SASKA"; +} inline bool SaskaParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { @@ -58,7 +61,10 @@ inline bool SaskaParamHandler::validateConfig(unsigned int nComp, unsigned int c return true; } -inline const char* ExtSaskaParamHandler::identifier() CADET_NOEXCEPT { return "EXT_SASKA"; } +inline const char* ExtSaskaParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_SASKA"; +} inline bool ExtSaskaParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { @@ -68,30 +74,35 @@ inline bool ExtSaskaParamHandler::validateConfig(unsigned int nComp, unsigned in return true; } - /** * @brief Defines the Saska binding model - * @details Implements the Saska adsorption model: \f[ \begin{align} - * \frac{\mathrm{d} q_i}{\mathrm{d} t} = H_i c_{p,i} + \sum_{j=1}^{N_{\text{comp}}} k_{ij} c_{p,i} c_{p,j} - q_i, - * \end{align} \f] - * where @f$ H_i @f$ denotes the Henry coefficient and @f$ k_{ij} @f$ the quadratic factor. - * Multiple bound states are not supported. However, the quadratic factors use the bound phase parameter index for @f$ j @f$. - * Components without bound state (i.e., non-binding components) are supported. - * + * @details Implements the Saska adsorption model: \f[ \begin{align} + * \frac{\mathrm{d} q_i}{\mathrm{d} t} = H_i c_{p,i} + \sum_{j=1}^{N_{\text{comp}}} k_{ij} c_{p,i} c_{p,j} + * - q_i, \end{align} \f] where @f$ H_i @f$ denotes the Henry coefficient and @f$ k_{ij} @f$ the quadratic factor. + * Multiple bound states are not supported. However, the quadratic factors use the bound phase parameter index + * for @f$ j @f$. Components without bound state (i.e., non-binding components) are supported. + * * See @cite Saska1992. * @tparam ParamHandler_t Type that can add support for external function dependence */ -template -class SaskaBindingBase : public ParamHandlerBindingModelBase +template class SaskaBindingBase : public ParamHandlerBindingModelBase { public: + SaskaBindingBase() + { + } + virtual ~SaskaBindingBase() CADET_NOEXCEPT + { + } - SaskaBindingBase() { } - virtual ~SaskaBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yCp, double const* y, double* dResDt, LinearBufferAllocator workSpace) const + virtual void timeDerivativeQuasiStationaryFluxes(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yCp, double const* y, double* dResDt, + LinearBufferAllocator workSpace) const { if (!this->hasQuasiStationaryReactions()) return; @@ -133,13 +144,17 @@ class SaskaBindingBase : public ParamHandlerBindingModelBase using ParamHandlerBindingModelBase::_nComp; using ParamHandlerBindingModelBase::_nBoundStates; - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } template int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Protein fluxes: -H_i c_{p,i} - \sum_j k_{ij} c_{p,i} c_{p,j} + q_i unsigned int bndIdx = 0; @@ -165,9 +180,11 @@ class SaskaBindingBase : public ParamHandlerBindingModelBase } template - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Protein fluxes: -H_i c_{p,i} - \sum_j k_{ij} c_{p,i} c_{p,j} + q_i int bndIdx = 0; @@ -183,13 +200,13 @@ class SaskaBindingBase : public ParamHandlerBindingModelBase // dres_i / dc_{p,j} for (int j = 0; j < _nComp; ++j) jac[j - bndIdx - offsetCp] = -static_cast(kSlice[j]) * yCp[i]; - // Getting to c_{p,i}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0} and a +j to c_{p,j}. - // This means jac[j - bndIdx - offsetCp] corresponds to c_{p,j}. + // Getting to c_{p,i}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0} and a +j to c_{p,j}. + // This means jac[j - bndIdx - offsetCp] corresponds to c_{p,j}. // dres_i / dc_{p,i} for (int j = 0; j < _nComp; ++j) jac[i - bndIdx - offsetCp] -= static_cast(kSlice[j]) * yCp[j]; - + jac[i - bndIdx - offsetCp] -= h; // dres_i / dq_i @@ -202,19 +219,18 @@ class SaskaBindingBase : public ParamHandlerBindingModelBase } }; - typedef SaskaBindingBase SaskaBinding; typedef SaskaBindingBase ExternalSaskaBinding; namespace binding { - void registerSaskaModel(std::unordered_map>& bindings) - { - bindings[SaskaBinding::identifier()] = []() { return new SaskaBinding(); }; - bindings[ExternalSaskaBinding::identifier()] = []() { return new ExternalSaskaBinding(); }; - } -} // namespace binding +void registerSaskaModel(std::unordered_map>& bindings) +{ + bindings[SaskaBinding::identifier()] = []() { return new SaskaBinding(); }; + bindings[ExternalSaskaBinding::identifier()] = []() { return new ExternalSaskaBinding(); }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/SelfAssociationBinding.cpp b/src/libcadet/model/binding/SelfAssociationBinding.cpp index a48186e80..ec617384f 100644 --- a/src/libcadet/model/binding/SelfAssociationBinding.cpp +++ b/src/libcadet/model/binding/SelfAssociationBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -40,7 +40,8 @@ ], "constantParameters": [ - { "type": "ReferenceConcentrationParameter", "varName": ["refC0", "refQ"], "objName": "refConcentration", "confPrefix": "SAI_"} + { "type": "ReferenceConcentrationParameter", "varName": ["refC0", "refQ"], "objName": "refConcentration", +"confPrefix": "SAI_"} ] } */ @@ -62,12 +63,15 @@ namespace cadet namespace model { -inline const char* SelfAssociationParamHandler::identifier() CADET_NOEXCEPT { return "SELF_ASSOCIATION"; } +inline const char* SelfAssociationParamHandler::identifier() CADET_NOEXCEPT +{ + return "SELF_ASSOCIATION"; +} inline bool SelfAssociationParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kA2.size()) || (_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) - || (_kA.size() != _sigma.size()) || (_kA.size() < nComp)) + if ((_kA.size() != _kA2.size()) || (_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || + (_kA.size() != _sigma.size()) || (_kA.size() < nComp)) throw InvalidParameterException("SAI_KA1, SAI_KA2, SAI_KD, SAI_NU, and SAI_SIGMA have to have the same size"); // Assume monovalent salt ions by default @@ -77,13 +81,17 @@ inline bool SelfAssociationParamHandler::validateConfig(unsigned int nComp, unsi return true; } -inline const char* ExtSelfAssociationParamHandler::identifier() CADET_NOEXCEPT { return "EXT_SELF_ASSOCIATION"; } +inline const char* ExtSelfAssociationParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_SELF_ASSOCIATION"; +} inline bool ExtSelfAssociationParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kA2.size()) || (_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) - || (_kA.size() != _sigma.size()) || (_kA.size() < nComp)) - throw InvalidParameterException("EXT_SAI_KA1, EXT_SAI_KA2, EXT_SAI_KD, EXT_SAI_NU, and EXT_SAI_SIGMA have to have the same size"); + if ((_kA.size() != _kA2.size()) || (_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || + (_kA.size() != _sigma.size()) || (_kA.size() < nComp)) + throw InvalidParameterException( + "EXT_SAI_KA1, EXT_SAI_KA2, EXT_SAI_KD, EXT_SAI_NU, and EXT_SAI_SIGMA have to have the same size"); // Assume monovalent salt ions by default if (_nu.base()[0] <= 0.0) @@ -92,37 +100,42 @@ inline bool ExtSelfAssociationParamHandler::validateConfig(unsigned int nComp, u return true; } - /** * @brief Defines the self association binding model - * @details Implements the self association adsorption model: \f[ \begin{align} + * @details Implements the self association adsorption model: \f[ \begin{align} * q_0 &= \Lambda - \sum_{j} \nu_j q_j \\ - * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= \left[k_{a1,i} + k_{a2,i} c_{p,i} \right] c_{p,i} \left( \Lambda - \sum_j\left( \nu_j + \sigma_j \right) q_j \right)^{\nu_i} - k_{d,i} q_i c_{p,0}^{\nu_i} - * \end{align} \f] - * Here, the coefficient @f$ k_{a2,i} @f$ denotes the adsorption rate of dimerization. - * Component @c 0 is assumed to be salt. Multiple bound states are not supported. - * Components without bound state (i.e., non-binding components) are supported. - * + * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= \left[k_{a1,i} + k_{a2,i} c_{p,i} \right] c_{p,i} \left( \Lambda - + * \sum_j\left( \nu_j + \sigma_j \right) q_j \right)^{\nu_i} - k_{d,i} q_i c_{p,0}^{\nu_i} \end{align} \f] Here, the + * coefficient @f$ k_{a2,i} @f$ denotes the adsorption rate of dimerization. Component @c 0 is assumed to be salt. + * Multiple bound states are not supported. Components without bound state (i.e., non-binding components) are supported. + * * See @cite Mollerup2008 and @cite Westerberg2012. * @tparam ParamHandler_t Type that can add support for external function dependence */ -template -class SelfAssociationBindingBase : public ParamHandlerBindingModelBase +template class SelfAssociationBindingBase : public ParamHandlerBindingModelBase { public: + SelfAssociationBindingBase() + { + } + virtual ~SelfAssociationBindingBase() CADET_NOEXCEPT + { + } - SelfAssociationBindingBase() { } - virtual ~SelfAssociationBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { const bool res = BindingModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); // Guarantee that salt has exactly one bound state if (nBound[0] != 1) - throw InvalidParameterException("Self association binding model requires exactly one bound state for salt component"); + throw InvalidParameterException( + "Self association binding model requires exactly one bound state for salt component"); // First flux is salt, which is always quasi-stationary _reactionQuasistationarity[0] = true; @@ -130,14 +143,28 @@ class SelfAssociationBindingBase : public ParamHandlerBindingModelBase::_nComp; using ParamHandlerBindingModelBase::_nBoundStates; - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } template int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { using CpStateParamType = typename DoubleActivePromoter::type; using StateParamType = typename DoubleActivePromoter::type; - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); - // Salt flux: nu_0 * q_0 - Lambda + Sum[nu_j * q_j, j] == 0 - // <=> nu_0 * q_0 == Lambda - Sum[nu_j * q_j, j] + // Salt flux: nu_0 * q_0 - Lambda + Sum[nu_j * q_j, j] == 0 + // <=> nu_0 * q_0 == Lambda - Sum[nu_j * q_j, j] // Also compute \bar{q}_0 = nu_0 * q_0 - Sum[sigma_j * q_j, j] res[0] = static_cast(p->nu[0]) * y[0] - static_cast(p->lambda); StateParamType q0_bar = static_cast(p->nu[0]) * y[0]; @@ -210,7 +242,8 @@ class SelfAssociationBindingBase : public ParamHandlerBindingModelBase(p->refQ); const CpStateParamType yCp0_divRef = yCp[0] / static_cast(p->refC0); - // Protein fluxes: -(k_{a,i} + k_{a2,i} * c_{p,i}) * c_{p,i} * \bar{q}_0^{nu_i / nu_0} + k_{d,i} * q_i * c_{p,0}^{nu_i / nu_0} + // Protein fluxes: -(k_{a,i} + k_{a2,i} * c_{p,i}) * c_{p,i} * \bar{q}_0^{nu_i / nu_0} + k_{d,i} * q_i * + // c_{p,0}^{nu_i / nu_0} bndIdx = 1; for (int i = 1; i < _nComp; ++i) { @@ -223,7 +256,9 @@ class SelfAssociationBindingBase : public ParamHandlerBindingModelBase(p->kD[i]) * y[bndIdx] * c0_pow_nu - yCp[i] * (static_cast(p->kA[i]) + yCp[i] * static_cast(p->kA2[i])) * q0_bar_pow_nu; + res[bndIdx] = static_cast(p->kD[i]) * y[bndIdx] * c0_pow_nu - + yCp[i] * (static_cast(p->kA[i]) + yCp[i] * static_cast(p->kA2[i])) * + q0_bar_pow_nu; // Next bound component ++bndIdx; @@ -233,9 +268,11 @@ class SelfAssociationBindingBase : public ParamHandlerBindingModelBase - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); double q0_bar = static_cast(p->nu[0]) * y[0]; @@ -265,8 +302,8 @@ class SelfAssociationBindingBase : public ParamHandlerBindingModelBase(p->kA[i]); @@ -284,9 +322,9 @@ class SelfAssociationBindingBase : public ParamHandlerBindingModelBase(p->nu[i]) / static_cast(p->nu[0]); const double effKa = yCp[i] * (ka + ka2 * yCp[i]); - const double c0_pow_nu = pow(yCp0_divRef, nu); + const double c0_pow_nu = pow(yCp0_divRef, nu); const double q0_bar_pow_nu = pow(q0_bar_divRef, nu); - const double c0_pow_nu_m1_divRef = pow(yCp0_divRef, nu - 1.0) / refC0; + const double c0_pow_nu_m1_divRef = pow(yCp0_divRef, nu - 1.0) / refC0; const double q0_bar_pow_nu_m1_divRef = pow(q0_bar_divRef, nu - 1.0) / refQ; // dres_i / dc_{p,0} @@ -306,7 +344,8 @@ class SelfAssociationBindingBase : public ParamHandlerBindingModelBase(p->sigma[j])); - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] + // corresponds to q_j. ++bndIdx2; } @@ -321,19 +360,18 @@ class SelfAssociationBindingBase : public ParamHandlerBindingModelBase SelfAssociationBinding; typedef SelfAssociationBindingBase ExternalSelfAssociationBinding; namespace binding { - void registerSelfAssociationModel(std::unordered_map>& bindings) - { - bindings[SelfAssociationBinding::identifier()] = []() { return new SelfAssociationBinding(); }; - bindings[ExternalSelfAssociationBinding::identifier()] = []() { return new ExternalSelfAssociationBinding(); }; - } -} // namespace binding +void registerSelfAssociationModel(std::unordered_map>& bindings) +{ + bindings[SelfAssociationBinding::identifier()] = []() { return new SelfAssociationBinding(); }; + bindings[ExternalSelfAssociationBinding::identifier()] = []() { return new ExternalSelfAssociationBinding(); }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/SimplifiedMultiStateStericMassActionBinding.cpp b/src/libcadet/model/binding/SimplifiedMultiStateStericMassActionBinding.cpp index 8e3621e7d..94511a4ad 100644 --- a/src/libcadet/model/binding/SimplifiedMultiStateStericMassActionBinding.cpp +++ b/src/libcadet/model/binding/SimplifiedMultiStateStericMassActionBinding.cpp @@ -26,11 +26,11 @@ namespace { - inline double getExtremeValueOfOrthoParametrization(double sMin, double sMax, double sQuad, unsigned int nStates) - { - return (sMax - sMin) * 0.5 / (sQuad * static_cast(nStates - 1)) + 0.5 * static_cast(nStates - 1); - } +inline double getExtremeValueOfOrthoParametrization(double sMin, double sMax, double sQuad, unsigned int nStates) +{ + return (sMax - sMin) * 0.5 / (sQuad * static_cast(nStates - 1)) + 0.5 * static_cast(nStates - 1); } +} // namespace namespace cadet { @@ -38,15 +38,23 @@ namespace cadet namespace model { -SimplifiedMultiStateStericMassActionBinding::SimplifiedMultiStateStericMassActionBinding() { } -SimplifiedMultiStateStericMassActionBinding::~SimplifiedMultiStateStericMassActionBinding() CADET_NOEXCEPT { } +SimplifiedMultiStateStericMassActionBinding::SimplifiedMultiStateStericMassActionBinding() +{ +} +SimplifiedMultiStateStericMassActionBinding::~SimplifiedMultiStateStericMassActionBinding() CADET_NOEXCEPT +{ +} -bool SimplifiedMultiStateStericMassActionBinding::configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) +bool SimplifiedMultiStateStericMassActionBinding::configureModelDiscretization(IParameterProvider& paramProvider, + unsigned int nComp, + unsigned int const* nBound, + unsigned int const* boundOffset) { const bool res = BindingModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); if (nBound[0] != 1) - throw InvalidParameterException("Simplified Multi-State SMA binding model requires exactly one bound state for salt"); + throw InvalidParameterException( + "Simplified Multi-State SMA binding model requires exactly one bound state for salt"); const unsigned int totalBoundStates = numBoundStates(nBound, nComp); @@ -74,14 +82,17 @@ bool SimplifiedMultiStateStericMassActionBinding::configureModelDiscretization(I return res; } -bool SimplifiedMultiStateStericMassActionBinding::configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) +bool SimplifiedMultiStateStericMassActionBinding::configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx) { const unsigned int totalBoundStates = numBoundStates(_nBoundStates, _nComp); // Read parameters _lambda = paramProvider.getDouble("SMSSMA_LAMBDA"); - readBoundStateDependentParameter, active>(_kA, paramProvider, "SMSSMA_KA", _nComp, _nBoundStates); - readBoundStateDependentParameter, active>(_kD, paramProvider, "SMSSMA_KD", _nComp, _nBoundStates); + readBoundStateDependentParameter, active>(_kA, paramProvider, "SMSSMA_KA", _nComp, + _nBoundStates); + readBoundStateDependentParameter, active>(_kD, paramProvider, "SMSSMA_KD", _nComp, + _nBoundStates); readParameterMatrix(_nuMin, paramProvider, "SMSSMA_NU_MIN", _nComp, 1); readParameterMatrix(_nuMax, paramProvider, "SMSSMA_NU_MAX", _nComp, 1); readParameterMatrix(_nuQuad, paramProvider, "SMSSMA_NU_QUAD", _nComp, 1); @@ -104,14 +115,22 @@ bool SimplifiedMultiStateStericMassActionBinding::configureImpl(IParameterProvid // Check parameters if ((_kA.size() != _kD.size()) || (_kA.size() != totalBoundStates)) throw InvalidParameterException("SMSSMA_KA and SMSSMA_KD have to have the same size"); - if ((_nuMin.size() != _nuMax.size()) || (_nuMin.size() != _nuQuad.size()) || (static_cast(_nuMin.size()) != _nComp)) - throw InvalidParameterException("SMSSMA_NU_MIN, SMSSMA_NU_MAX and SMSSMA_NU_QUAD require " + std::to_string(_nComp) + " elements"); - if ((_sigmaMin.size() != _sigmaMax.size()) || (_sigmaMin.size() != _sigmaQuad.size()) || (static_cast(_sigmaMin.size()) != _nComp)) - throw InvalidParameterException("SMSSMA_SIGMA_MIN, SMSSMA_SIGMA_MAX and SMSSMA_SIGMA_QUAD require " + std::to_string(_nComp) + " elements"); - if ((_kSW.size() != _kSW_lin.size()) || (_kSW.size() != _kSW_quad.size()) || (static_cast(_kSW.size()) != _nComp)) - throw InvalidParameterException("SMSSMA_KSW, SMSSMA_KSW_LIN and SMSSMA_KSW_QUAD require " + std::to_string(_nComp) + " elements"); - if ((_kWS.size() != _kWS_lin.size()) || (_kWS.size() != _kWS_quad.size()) || (static_cast(_kWS.size()) != _nComp)) - throw InvalidParameterException("SMSSMA_KWS, SMSSMA_KWS_LIN and SMSSMA_KWS_QUAD require " + std::to_string(_nComp) + " elements"); + if ((_nuMin.size() != _nuMax.size()) || (_nuMin.size() != _nuQuad.size()) || + (static_cast(_nuMin.size()) != _nComp)) + throw InvalidParameterException("SMSSMA_NU_MIN, SMSSMA_NU_MAX and SMSSMA_NU_QUAD require " + + std::to_string(_nComp) + " elements"); + if ((_sigmaMin.size() != _sigmaMax.size()) || (_sigmaMin.size() != _sigmaQuad.size()) || + (static_cast(_sigmaMin.size()) != _nComp)) + throw InvalidParameterException("SMSSMA_SIGMA_MIN, SMSSMA_SIGMA_MAX and SMSSMA_SIGMA_QUAD require " + + std::to_string(_nComp) + " elements"); + if ((_kSW.size() != _kSW_lin.size()) || (_kSW.size() != _kSW_quad.size()) || + (static_cast(_kSW.size()) != _nComp)) + throw InvalidParameterException("SMSSMA_KSW, SMSSMA_KSW_LIN and SMSSMA_KSW_QUAD require " + + std::to_string(_nComp) + " elements"); + if ((_kWS.size() != _kWS_lin.size()) || (_kWS.size() != _kWS_quad.size()) || + (static_cast(_kWS.size()) != _nComp)) + throw InvalidParameterException("SMSSMA_KWS, SMSSMA_KWS_LIN and SMSSMA_KWS_QUAD require " + + std::to_string(_nComp) + " elements"); // Check for negative values for (int i = 0; i < _nComp; ++i) @@ -122,80 +141,105 @@ bool SimplifiedMultiStateStericMassActionBinding::configureImpl(IParameterProvid // ---- Check sigma // Endpoints have to be non-negative if ((_sigmaMin[i] < 0.0) || (_sigmaMax[i] < 0.0)) - throw InvalidParameterException("SMSSMA_SIGMA_MIN, SMSSMA_SIGMA_MAX and SMSSMA_SIGMA_QUAD of component " + std::to_string(i) + " have to be set such that all computed sigma are non-negative"); + throw InvalidParameterException("SMSSMA_SIGMA_MIN, SMSSMA_SIGMA_MAX and SMSSMA_SIGMA_QUAD of component " + + std::to_string(i) + + " have to be set such that all computed sigma are non-negative"); if (_sigmaQuad[i] != 0.0) { // Position of global extremum - const double extStateSigma = getExtremeValueOfOrthoParametrization(static_cast(_sigmaMin[i]), static_cast(_sigmaMax[i]), static_cast(_sigmaQuad[i]), _nBoundStates[i]); + const double extStateSigma = getExtremeValueOfOrthoParametrization( + static_cast(_sigmaMin[i]), static_cast(_sigmaMax[i]), + static_cast(_sigmaQuad[i]), _nBoundStates[i]); if ((extStateSigma >= 0.0) && (extStateSigma <= static_cast(_nBoundStates[i] - 1))) { // Extremum is inside interval, so we have to check all three values const double extVal = sigma(i, extStateSigma); if (extVal < 0.0) - throw InvalidParameterException("SMSSMA_SIGMA_MIN, SMSSMA_SIGMA_MAX and SMSSMA_SIGMA_QUAD of component " + std::to_string(i) + " have to be set such that all computed sigma are non-negative"); + throw InvalidParameterException( + "SMSSMA_SIGMA_MIN, SMSSMA_SIGMA_MAX and SMSSMA_SIGMA_QUAD of component " + std::to_string(i) + + " have to be set such that all computed sigma are non-negative"); } } // ---- Check nu // Endpoints have to be non-negative if ((_nuMin[i] < 0.0) || (_nuMax[i] < 0.0)) - throw InvalidParameterException("SMSSMA_NU_MIN, SMSSMA_NU_MAX and SMSSMA_NU_QUAD of component " + std::to_string(i) + " have to be set such that all computed nu are non-negative"); + throw InvalidParameterException("SMSSMA_NU_MIN, SMSSMA_NU_MAX and SMSSMA_NU_QUAD of component " + + std::to_string(i) + + " have to be set such that all computed nu are non-negative"); if (_nuQuad[i] != 0.0) { // Position of global extremum - const double extStateNu = getExtremeValueOfOrthoParametrization(static_cast(_nuMin[i]), static_cast(_nuMax[i]), static_cast(_nuQuad[i]), _nBoundStates[i]); + const double extStateNu = + getExtremeValueOfOrthoParametrization(static_cast(_nuMin[i]), static_cast(_nuMax[i]), + static_cast(_nuQuad[i]), _nBoundStates[i]); if ((extStateNu >= 0.0) && (extStateNu <= static_cast(_nBoundStates[i] - 1))) { // Extremum is inside interval, so we have to check all three values const double extVal = nu(i, extStateNu); if (extVal < 0.0) - throw InvalidParameterException("SMSSMA_NU_MIN, SMSSMA_NU_MAX and SMSSMA_NU_QUAD of component " + std::to_string(i) + " have to be set such that all computed nu are non-negative"); + throw InvalidParameterException("SMSSMA_NU_MIN, SMSSMA_NU_MAX and SMSSMA_NU_QUAD of component " + + std::to_string(i) + + " have to be set such that all computed nu are non-negative"); } } // ---- Check kSW // Endpoints have to be non-negative if ((_kSW[i] < 0.0) || (k_sw(i, _nBoundStates[i] - 2) < 0.0)) - throw InvalidParameterException("SMSSMA_KSW, SMSSMA_KSW_LIN and SMSSMA_KSW_QUAD of component " + std::to_string(i) + " have to be set such that all computed rates are non-negative"); + throw InvalidParameterException("SMSSMA_KSW, SMSSMA_KSW_LIN and SMSSMA_KSW_QUAD of component " + + std::to_string(i) + + " have to be set such that all computed rates are non-negative"); if (_kSW_quad[i] != 0.0) { // Position of global extremum - const double extStateSW = static_cast(_kSW_lin[i]) * 0.5 / static_cast(_kSW_quad[i]) + static_cast(_nBoundStates[i]) * 0.5 - 1.0; + const double extStateSW = static_cast(_kSW_lin[i]) * 0.5 / static_cast(_kSW_quad[i]) + + static_cast(_nBoundStates[i]) * 0.5 - 1.0; if ((extStateSW >= 0.0) && (extStateSW <= static_cast(_nBoundStates[i] - 2))) { // Extremum is inside interval, so we have to check all three values const double extVal = k_sw(i, extStateSW); if (extVal < 0.0) - throw InvalidParameterException("SMSSMA_KSW, SMSSMA_KSW_LIN and SMSSMA_KSW_QUAD of component " + std::to_string(i) + " have to be set such that all computed rates are non-negative"); + throw InvalidParameterException("SMSSMA_KSW, SMSSMA_KSW_LIN and SMSSMA_KSW_QUAD of component " + + std::to_string(i) + + " have to be set such that all computed rates are non-negative"); } } // ---- Check kWS // Endpoints have to be non-negative if ((_kWS[i] < 0.0) || (k_ws(i, _nBoundStates[i] - 2) < 0.0)) - throw InvalidParameterException("SMSSMA_KWS, SMSSMA_KWS_LIN and SMSSMA_KWS_QUAD of component " + std::to_string(i) + " have to be set such that all computed rates are non-negative"); + throw InvalidParameterException("SMSSMA_KWS, SMSSMA_KWS_LIN and SMSSMA_KWS_QUAD of component " + + std::to_string(i) + + " have to be set such that all computed rates are non-negative"); if (_kWS_quad[i] != 0.0) { // Position of global extremum - const double extStateWS = static_cast(_kWS_lin[i]) * 0.5 / static_cast(_kWS_quad[i]) + static_cast(_nBoundStates[i]) * 0.5 - 1.0; + const double extStateWS = static_cast(_kWS_lin[i]) * 0.5 / static_cast(_kWS_quad[i]) + + static_cast(_nBoundStates[i]) * 0.5 - 1.0; if ((extStateWS >= 0.0) && (extStateWS <= static_cast(_nBoundStates[i] - 2))) { // Extremum is inside interval, so we have to check all three values const double extVal = k_ws(i, extStateWS); if (extVal < 0.0) - throw InvalidParameterException("SMSSMA_KWS, SMSSMA_KWS_LIN and SMSSMA_KWS_QUAD of component " + std::to_string(i) + " have to be set such that all computed rates are non-negative"); + throw InvalidParameterException("SMSSMA_KWS, SMSSMA_KWS_LIN and SMSSMA_KWS_QUAD of component " + + std::to_string(i) + + " have to be set such that all computed rates are non-negative"); } } } // Register parameters - _parameters[makeParamId(hashString("SMSSMA_LAMBDA"), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, ReactionIndep, SectionIndep)] = &_lambda; - registerComponentBoundStateDependentParamCompMajor(hashString("SMSSMA_KA"), _parameters, _kA, unitOpIdx, parTypeIdx); - registerComponentBoundStateDependentParamCompMajor(hashString("SMSSMA_KD"), _parameters, _kD, unitOpIdx, parTypeIdx); + _parameters[makeParamId(hashString("SMSSMA_LAMBDA"), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_lambda; + registerComponentBoundStateDependentParamCompMajor(hashString("SMSSMA_KA"), _parameters, _kA, unitOpIdx, + parTypeIdx); + registerComponentBoundStateDependentParamCompMajor(hashString("SMSSMA_KD"), _parameters, _kD, unitOpIdx, + parTypeIdx); registerComponentDependentParam(hashString("SMSSMA_NU_MIN"), _parameters, _nuMin, unitOpIdx, parTypeIdx); registerComponentDependentParam(hashString("SMSSMA_NU_MAX"), _parameters, _nuMax, unitOpIdx, parTypeIdx); registerComponentDependentParam(hashString("SMSSMA_NU_QUAD"), _parameters, _nuQuad, unitOpIdx, parTypeIdx); @@ -208,14 +252,18 @@ bool SimplifiedMultiStateStericMassActionBinding::configureImpl(IParameterProvid registerComponentDependentParam(hashString("SMSSMA_KWS"), _parameters, _kWS, unitOpIdx, parTypeIdx); registerComponentDependentParam(hashString("SMSSMA_KWS_LIN"), _parameters, _kWS_lin, unitOpIdx, parTypeIdx); registerComponentDependentParam(hashString("SMSSMA_KWS_QUAD"), _parameters, _kWS_quad, unitOpIdx, parTypeIdx); - _parameters[makeParamId(hashString("SMSSMA_NU_SALT"), unitOpIdx, 0, parTypeIdx, BoundStateIndep, ReactionIndep, SectionIndep)] = &_nu0; - _parameters[makeParamId(hashString("SMSSMA_REFC0"), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, ReactionIndep, SectionIndep)] = &_refC0; - _parameters[makeParamId(hashString("SMSSMA_REFQ"), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, ReactionIndep, SectionIndep)] = &_refQ; + _parameters[makeParamId(hashString("SMSSMA_NU_SALT"), unitOpIdx, 0, parTypeIdx, BoundStateIndep, ReactionIndep, + SectionIndep)] = &_nu0; + _parameters[makeParamId(hashString("SMSSMA_REFC0"), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_refC0; + _parameters[makeParamId(hashString("SMSSMA_REFQ"), unitOpIdx, CompIndep, parTypeIdx, BoundStateIndep, ReactionIndep, + SectionIndep)] = &_refQ; return true; } -unsigned int SimplifiedMultiStateStericMassActionBinding::workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT +unsigned int SimplifiedMultiStateStericMassActionBinding::workspaceSize( + unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT { return 0; } @@ -224,7 +272,10 @@ template inline ParamType SimplifiedMultiStateStericMassActionBinding::sigma(int comp, double state) const { if (_nBoundStates[comp] > 1) - return static_cast(_sigmaMin[comp]) + state * (static_cast(_sigmaMax[comp]) - static_cast(_sigmaMin[comp])) / (_nBoundStates[comp] - 1) - static_cast(_sigmaQuad[comp]) * state * (1 - static_cast(_nBoundStates[comp]) + state); + return static_cast(_sigmaMin[comp]) + + state * (static_cast(_sigmaMax[comp]) - static_cast(_sigmaMin[comp])) / + (_nBoundStates[comp] - 1) - + static_cast(_sigmaQuad[comp]) * state * (1 - static_cast(_nBoundStates[comp]) + state); else return static_cast(_sigmaMin[comp]); } @@ -233,7 +284,10 @@ template inline ParamType SimplifiedMultiStateStericMassActionBinding::nu(int comp, double state) const { if (_nBoundStates[comp] > 1) - return static_cast(_nuMin[comp]) + state * (static_cast(_nuMax[comp]) - static_cast(_nuMin[comp])) / (_nBoundStates[comp] - 1) - static_cast(_nuQuad[comp]) * state * (1 - static_cast(_nBoundStates[comp]) + state); + return static_cast(_nuMin[comp]) + + state * (static_cast(_nuMax[comp]) - static_cast(_nuMin[comp])) / + (_nBoundStates[comp] - 1) - + static_cast(_nuQuad[comp]) * state * (1 - static_cast(_nBoundStates[comp]) + state); else return static_cast(_nuMin[comp]); } @@ -241,18 +295,21 @@ inline ParamType SimplifiedMultiStateStericMassActionBinding::nu(int comp, doubl template inline ParamType SimplifiedMultiStateStericMassActionBinding::k_sw(int comp, double state) const { - return static_cast(_kSW[comp]) + state * static_cast(_kSW_lin[comp]) - static_cast(_kSW_quad[comp]) * state * (2 - static_cast(_nBoundStates[comp]) + state); + return static_cast(_kSW[comp]) + state * static_cast(_kSW_lin[comp]) - + static_cast(_kSW_quad[comp]) * state * (2 - static_cast(_nBoundStates[comp]) + state); } template inline ParamType SimplifiedMultiStateStericMassActionBinding::k_ws(int comp, double state) const { - return static_cast(_kWS[comp]) + state * static_cast(_kWS_lin[comp]) - static_cast(_kWS_quad[comp]) * state * (2 - static_cast(_nBoundStates[comp]) + state); + return static_cast(_kWS[comp]) + state * static_cast(_kWS_lin[comp]) - + static_cast(_kWS_quad[comp]) * state * (2 - static_cast(_nBoundStates[comp]) + state); } template int SimplifiedMultiStateStericMassActionBinding::fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, - StateType const* y, CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + StateType const* y, CpStateType const* yCp, ResidualType* res, + LinearBufferAllocator workSpace) const { using CpStateParamType = typename DoubleActivePromoter::type; using StateParamType = typename DoubleActivePromoter::type; @@ -300,7 +357,8 @@ int SimplifiedMultiStateStericMassActionBinding::fluxImpl(double t, unsigned int // Calculate residual // Adsorption and desorption - res[bndIdx] = static_cast(curKd[j]) * y[bndIdx] * c0_pow_nu - static_cast(curKa[j]) * yCp[i] * q0_bar_pow_nu; + res[bndIdx] = static_cast(curKd[j]) * y[bndIdx] * c0_pow_nu - + static_cast(curKa[j]) * yCp[i] * q0_bar_pow_nu; // Conversion to and from weaker state j - 1 if (j > 0) @@ -336,7 +394,10 @@ int SimplifiedMultiStateStericMassActionBinding::fluxImpl(double t, unsigned int } template -void SimplifiedMultiStateStericMassActionBinding::jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const +void SimplifiedMultiStateStericMassActionBinding::jacobianImpl(double t, unsigned int secIdx, + const ColumnPosition& colPos, double const* y, + double const* yCp, int offsetCp, RowIterator jac, + LinearBufferAllocator workSpace) const { double q0_bar = static_cast(_nu0) * y[0]; @@ -375,17 +436,18 @@ void SimplifiedMultiStateStericMassActionBinding::jacobianImpl(double t, unsigne for (int j = 0; j < static_cast(_nBoundStates[i]); ++j) { - // Getting to c_{p,0}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0}. This means jac[-bndIdx - offsetCp] corresponds to c_{p,0}. - // Getting to c_{p,i}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0} and a +i to c_{p,i}. + // Getting to c_{p,0}: -bndIdx takes us to q_0, another -offsetCp to c_{p,0}. This means jac[-bndIdx - + // offsetCp] corresponds to c_{p,0}. Getting to c_{p,i}: -bndIdx takes us to q_0, another -offsetCp to + // c_{p,0} and a +i to c_{p,i}. // This means jac[i - bndIdx - offsetCp] corresponds to c_{p,i}. const double ka = static_cast(curKa[j]); const double kd = static_cast(curKd[j]); const double curNu = nu(i, j) / static_cast(_nu0); - const double c0_pow_nu = pow(yCp0_divRef, curNu); + const double c0_pow_nu = pow(yCp0_divRef, curNu); const double q0_bar_pow_nu = pow(q0_bar_divRef, curNu); - const double c0_pow_nu_m1_divRef = pow(yCp0_divRef, curNu - 1.0) / refC0; + const double c0_pow_nu_m1_divRef = pow(yCp0_divRef, curNu - 1.0) / refC0; const double q0_bar_pow_nu_m1_divRef = curNu * pow(q0_bar_divRef, curNu - 1.0) / refQ; // dres_i / dc_{p,0} @@ -421,8 +483,9 @@ void SimplifiedMultiStateStericMassActionBinding::jacobianImpl(double t, unsigne const double curSW = k_sw(i, j - 1); const double curWS = k_ws(i, j - 1); - const double nuDiff = curNu - nu(i, j-1) / static_cast(_nu0); - const double q0_bar_pow_nudiff_deriv = curWS * y[bndIdx - 1] * nuDiff * pow(q0_bar_divRef, nuDiff - 1.0) / refQ; + const double nuDiff = curNu - nu(i, j - 1) / static_cast(_nu0); + const double q0_bar_pow_nudiff_deriv = + curWS * y[bndIdx - 1] * nuDiff * pow(q0_bar_divRef, nuDiff - 1.0) / refQ; // dres_i / dc_{p,0} jac[-bndIdx - offsetCp] += curSW * y[bndIdx] * nuDiff * pow(yCp0_divRef, nuDiff - 1.0) / refC0; @@ -432,7 +495,8 @@ void SimplifiedMultiStateStericMassActionBinding::jacobianImpl(double t, unsigne jac[0] += curSW * pow(yCp0_divRef, nuDiff); // dres_i / dq_i^l (without dq0_bar / dq_i^l) jac[-1] -= curWS * pow(q0_bar_divRef, nuDiff); - // dres_i / dq_{i2}^{j2} (accounts for all dq0_bar / dq_{i2}^{j2} including missing dq0_bar / dq_i^l from above) + // dres_i / dq_{i2}^{j2} (accounts for all dq0_bar / dq_{i2}^{j2} including missing dq0_bar / dq_i^l + // from above) bndIdx2 = 1; for (int i2 = 1; i2 < _nComp; ++i2) { @@ -447,8 +511,9 @@ void SimplifiedMultiStateStericMassActionBinding::jacobianImpl(double t, unsigne const double curSW = k_sw(i, j); const double curWS = k_ws(i, j); - const double nuDiff = nu(i, j+1) / static_cast(_nu0) - curNu; - const double q0_bar_pow_nudiff_deriv = curWS * y[bndIdx] * nuDiff * pow(q0_bar_divRef, nuDiff - 1.0) / refQ; + const double nuDiff = nu(i, j + 1) / static_cast(_nu0) - curNu; + const double q0_bar_pow_nudiff_deriv = + curWS * y[bndIdx] * nuDiff * pow(q0_bar_divRef, nuDiff - 1.0) / refQ; // dres_i / dc_{p,0} jac[-bndIdx - offsetCp] -= curSW * y[bndIdx + 1] * nuDiff * pow(yCp0_divRef, nuDiff - 1.0) / refC0; @@ -474,7 +539,10 @@ void SimplifiedMultiStateStericMassActionBinding::jacobianImpl(double t, unsigne } } -bool SimplifiedMultiStateStericMassActionBinding::preConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const +bool SimplifiedMultiStateStericMassActionBinding::preConsistentInitialState(double t, unsigned int secIdx, + const ColumnPosition& colPos, double* y, + double const* yCp, + LinearBufferAllocator workSpace) const { // Compute salt component from given bound states q_i^j @@ -501,11 +569,14 @@ bool SimplifiedMultiStateStericMassActionBinding::preConsistentInitialState(doub return true; } -void SimplifiedMultiStateStericMassActionBinding::postConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const +void SimplifiedMultiStateStericMassActionBinding::postConsistentInitialState(double t, unsigned int secIdx, + const ColumnPosition& colPos, double* y, + double const* yCp, + LinearBufferAllocator workSpace) const { preConsistentInitialState(t, secIdx, colPos, y, yCp, workSpace); } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/binding/SimplifiedMultiStateStericMassActionBinding.hpp b/src/libcadet/model/binding/SimplifiedMultiStateStericMassActionBinding.hpp index 4370f3889..84859b133 100644 --- a/src/libcadet/model/binding/SimplifiedMultiStateStericMassActionBinding.hpp +++ b/src/libcadet/model/binding/SimplifiedMultiStateStericMassActionBinding.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the SimplifiedMultiStateStericMassActionBinding class. */ @@ -32,102 +32,132 @@ namespace model /** * @brief Defines the simplified Multi-State-SMA binding model - * @details Implements a simplification of the Multi-State-SMA adsorption model defined in cadet::model::MultiStateStericMassActionBinding. - * The simplification consists in additional assumptions and a different parameterization which significantly - * reduces the number of parameters. The assumptions are - * -# Molecules are only exchanged between two adjacent states, this means no transfer from state @f$ q_i^{(1)} @f$ to state @f$ q_i^{(3)} @f$. - * -# Characteristic charge @f$ \nu_i^{(j)} @f$ and shielding factor @f$ \sigma_i^{(j)} @f$ only depend on the index of the state @f$ j @f$. + * @details Implements a simplification of the Multi-State-SMA adsorption model defined in + * cadet::model::MultiStateStericMassActionBinding. The simplification consists in additional assumptions and a + * different parameterization which significantly reduces the number of parameters. The assumptions are + * -# Molecules are only exchanged between two adjacent states, this means no transfer from state @f$ + * q_i^{(1)} @f$ to state @f$ q_i^{(3)} @f$. + * -# Characteristic charge @f$ \nu_i^{(j)} @f$ and shielding factor @f$ \sigma_i^{(j)} @f$ only depend on + * the index of the state @f$ j @f$. * - * Thus, the exchange parameters @f$ k^{(i)}_{j\ell} @f$, the characteristic charge @f$ \nu_i^{(j)} @f$, and the shielding @f$ \sigma_i^{(j)} @f$ - * can be parameterized with few degrees of freedom. For all @f$ i = 1,\dots,N_{\text{comp}} @f$ and @f$ j,\ell = 1,\dots,M_i @f$ let + * Thus, the exchange parameters @f$ k^{(i)}_{j\ell} @f$, the characteristic charge @f$ \nu_i^{(j)} @f$, and + * the shielding @f$ \sigma_i^{(j)} @f$ can be parameterized with few degrees of freedom. For all @f$ i = + * 1,\dots,N_{\text{comp}} @f$ and @f$ j,\ell = 1,\dots,M_i @f$ let * @f[ \begin{align*} * k^{(i)}_{j\ell} &= \begin{cases} * 0, & \text{for } \abs{j-\ell} \neq 1 \\ - * K^{(i)}_{ws} + j K^{(i)}_{ws,\text{lin}} + K^{(i)}_{ws,\text{quad}} (j-1)(j - M+1), & \text{for } \ell = j+1 \\ - * K^{(i)}_{sw} + \ell K^{(i)}_{sw,\text{lin}} + K^{(i)}_{sw,\text{quad}} (\ell-1)(\ell - M+1), & \text{for } \ell = j-1, + * K^{(i)}_{ws} + j K^{(i)}_{ws,\text{lin}} + K^{(i)}_{ws,\text{quad}} (j-1)(j - M+1), & \text{for + * } \ell = j+1 \\ K^{(i)}_{sw} + \ell K^{(i)}_{sw,\text{lin}} + K^{(i)}_{sw,\text{quad}} (\ell-1)(\ell - M+1), & + * \text{for } \ell = j-1, * \end{cases}\\ - * \nu_i^{(j)} &= \nu_{\text{min},i} + \frac{j-1}{M-1} \left( \nu_{\text{max},i} - \nu_{\text{min},i} \right) + \nu_{\text{quad},i} (j-1) (j-M), \\ - * \sigma_i^{(j)} &= \sigma_{\text{min},i} + \frac{j-1}{M-1} \left( \sigma_{\text{max},i} - \sigma_{\text{min},i} \right) + \sigma_{\text{quad},i} (j-1) (j-M). - * \end{align*} @f] - * The parameterization is orthogonal in the sense that the endpoints of the linear interpolation defined by @f$ \nu_{\text{min},i} @f$ - * and @f$ \nu_{\text{max},i} @f$ are not changed by @f$ \nu_{\text{quad},i} @f$. Rather the quadratic effect is added on top of - * the linear interpolation. - * - * The internal state vector layout follows the more general MultiStateStericMassActinoBinding, which is component-major. - * The state vector contains all components and within each component all bound states are placed. + * \nu_i^{(j)} &= \nu_{\text{min},i} + \frac{j-1}{M-1} \left( \nu_{\text{max},i} - \nu_{\text{min},i} + * \right) + \nu_{\text{quad},i} (j-1) (j-M), \\ \sigma_i^{(j)} &= \sigma_{\text{min},i} + \frac{j-1}{M-1} \left( + * \sigma_{\text{max},i} - \sigma_{\text{min},i} \right) + \sigma_{\text{quad},i} (j-1) (j-M). \end{align*} @f] The + * parameterization is orthogonal in the sense that the endpoints of the linear interpolation defined by @f$ + * \nu_{\text{min},i} @f$ and @f$ \nu_{\text{max},i} @f$ are not changed by @f$ \nu_{\text{quad},i} @f$. Rather the + * quadratic effect is added on top of the linear interpolation. + * + * The internal state vector layout follows the more general MultiStateStericMassActinoBinding, which is + * component-major. The state vector contains all components and within each component all bound states are placed. */ class SimplifiedMultiStateStericMassActionBinding : public BindingModelBase { public: - SimplifiedMultiStateStericMassActionBinding(); virtual ~SimplifiedMultiStateStericMassActionBinding() CADET_NOEXCEPT; - static const char* identifier() { return "SIMPLIFIED_MULTISTATE_STERIC_MASS_ACTION"; } - virtual const char* name() const CADET_NOEXCEPT { return SimplifiedMultiStateStericMassActionBinding::identifier(); } - - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset); - - virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT; - - virtual bool hasSalt() const CADET_NOEXCEPT { return true; } - virtual bool supportsMultistate() const CADET_NOEXCEPT { return true; } - virtual bool supportsNonBinding() const CADET_NOEXCEPT { return true; } - virtual bool hasQuasiStationaryReactions() const CADET_NOEXCEPT { return true; } - - virtual bool dependsOnTime() const CADET_NOEXCEPT { return false; } - virtual bool requiresWorkspace() const CADET_NOEXCEPT { return true; } - - virtual bool preConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const; - virtual void postConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, double const* yCp, LinearBufferAllocator workSpace) const; + static const char* identifier() + { + return "SIMPLIFIED_MULTISTATE_STERIC_MASS_ACTION"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return SimplifiedMultiStateStericMassActionBinding::identifier(); + } + + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset); + + virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT; + + virtual bool hasSalt() const CADET_NOEXCEPT + { + return true; + } + virtual bool supportsMultistate() const CADET_NOEXCEPT + { + return true; + } + virtual bool supportsNonBinding() const CADET_NOEXCEPT + { + return true; + } + virtual bool hasQuasiStationaryReactions() const CADET_NOEXCEPT + { + return true; + } + + virtual bool dependsOnTime() const CADET_NOEXCEPT + { + return false; + } + virtual bool requiresWorkspace() const CADET_NOEXCEPT + { + return true; + } + + virtual bool preConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, + double const* yCp, LinearBufferAllocator workSpace) const; + virtual void postConsistentInitialState(double t, unsigned int secIdx, const ColumnPosition& colPos, double* y, + double const* yCp, LinearBufferAllocator workSpace) const; CADET_BINDINGMODELBASE_BOILERPLATE protected: - active _lambda; //!< Ionic capacity - active _nu0; //!< Valence of salt ions + active _lambda; //!< Ionic capacity + active _nu0; //!< Valence of salt ions util::SlicedVector _kA; //!< Adsorption rate in component-major ordering util::SlicedVector _kD; //!< Desorption rate in component-major ordering - std::vector _nuMin; //!< Characteristic charge, minimum value - std::vector _nuMax; //!< Characteristic charge, maximum value - std::vector _nuQuad; //!< Characteristic charge, quadratic modifier - std::vector _sigmaMin; //!< Steric factor, minimum value - std::vector _sigmaMax; //!< Steric factor, maximum value + std::vector _nuMin; //!< Characteristic charge, minimum value + std::vector _nuMax; //!< Characteristic charge, maximum value + std::vector _nuQuad; //!< Characteristic charge, quadratic modifier + std::vector _sigmaMin; //!< Steric factor, minimum value + std::vector _sigmaMax; //!< Steric factor, maximum value std::vector _sigmaQuad; //!< Steric factor, quadratic modifier - std::vector _kSW; //!< State transition rate from strong to weak state, offset - std::vector _kSW_lin; //!< State transition rate from strong to weak state, linear factor - std::vector _kSW_quad; //!< State transition rate from strong to weak state, quadratic factor - std::vector _kWS; //!< State transition rate from weak to strong state, offset - std::vector _kWS_lin; //!< State transition rate from weak to strong state, linear factor - std::vector _kWS_quad; //!< State transition rate from weak to strong state, quadratic factor - active _refC0; //! Liquid phase reference concentration - active _refQ; //! Solid phase reference concentration - - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + std::vector _kSW; //!< State transition rate from strong to weak state, offset + std::vector _kSW_lin; //!< State transition rate from strong to weak state, linear factor + std::vector _kSW_quad; //!< State transition rate from strong to weak state, quadratic factor + std::vector _kWS; //!< State transition rate from weak to strong state, offset + std::vector _kWS_lin; //!< State transition rate from weak to strong state, linear factor + std::vector _kWS_quad; //!< State transition rate from weak to strong state, quadratic factor + active _refC0; //! Liquid phase reference concentration + active _refQ; //! Solid phase reference concentration + + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx); - - template - inline ParamType sigma(int comp, double state) const; - template - inline ParamType nu(int comp, double state) const; + template inline ParamType sigma(int comp, double state) const; - template - inline ParamType k_sw(int comp, double state) const; + template inline ParamType nu(int comp, double state) const; - template - inline ParamType k_ws(int comp, double state) const; + template inline ParamType k_sw(int comp, double state) const; + + template inline ParamType k_ws(int comp, double state) const; template - int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, - StateType const* y, CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const; + int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const; template - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const; + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const; }; - } // namespace model } // namespace cadet diff --git a/src/libcadet/model/binding/StericMassActionBinding.cpp b/src/libcadet/model/binding/StericMassActionBinding.cpp index 603f0e3f4..b7c113ff7 100644 --- a/src/libcadet/model/binding/StericMassActionBinding.cpp +++ b/src/libcadet/model/binding/StericMassActionBinding.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -39,7 +39,8 @@ ], "constantParameters": [ - { "type": "ReferenceConcentrationParameter", "varName": ["refC0", "refQ"], "objName": "refConcentration", "confPrefix": "SMA_"} + { "type": "ReferenceConcentrationParameter", "varName": ["refC0", "refQ"], "objName": "refConcentration", +"confPrefix": "SMA_"} ] } */ @@ -60,11 +61,15 @@ namespace cadet namespace model { -inline const char* SMAParamHandler::identifier() CADET_NOEXCEPT { return "STERIC_MASS_ACTION"; } +inline const char* SMAParamHandler::identifier() CADET_NOEXCEPT +{ + return "STERIC_MASS_ACTION"; +} inline bool SMAParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _sigma.size()) || (_kA.size() < nComp)) + if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _sigma.size()) || + (_kA.size() < nComp)) throw InvalidParameterException("SMA_KA, SMA_KD, SMA_NU, and SMA_SIGMA have to have the same size"); // Assume monovalent salt ions by default @@ -74,12 +79,17 @@ inline bool SMAParamHandler::validateConfig(unsigned int nComp, unsigned int con return true; } -inline const char* ExtSMAParamHandler::identifier() CADET_NOEXCEPT { return "EXT_STERIC_MASS_ACTION"; } +inline const char* ExtSMAParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_STERIC_MASS_ACTION"; +} inline bool ExtSMAParamHandler::validateConfig(unsigned int nComp, unsigned int const* nBoundStates) { - if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _sigma.size()) || (_kA.size() < nComp)) - throw InvalidParameterException("EXT_SMA_KA, EXT_SMA_KD, EXT_SMA_NU, and EXT_SMA_SIGMA have to have the same size"); + if ((_kA.size() != _kD.size()) || (_kA.size() != _nu.size()) || (_kA.size() != _sigma.size()) || + (_kA.size() < nComp)) + throw InvalidParameterException( + "EXT_SMA_KA, EXT_SMA_KD, EXT_SMA_NU, and EXT_SMA_SIGMA have to have the same size"); // Assume monovalent salt ions by default if (_nu.base()[0] <= 0.0) @@ -88,36 +98,41 @@ inline bool ExtSMAParamHandler::validateConfig(unsigned int nComp, unsigned int return true; } - /** * @brief Defines the steric mass action binding model - * @details Implements the steric mass action adsorption model: \f[ \begin{align} + * @details Implements the steric mass action adsorption model: \f[ \begin{align} * q_0 &= \Lambda - \sum_{j} \nu_j q_j \\ - * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} \left( \Lambda - \sum_j\left( \nu_j + \sigma_j \right) q_j \right)^{\nu_i} - k_{d,i} q_i c_{p,0}^{\nu_i} - * \end{align} \f] - * Component @c 0 is assumed to be salt. Multiple bound states are not supported. - * Components without bound state (i.e., non-binding components) are supported. - * + * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} \left( \Lambda - \sum_j\left( \nu_j + \sigma_j + * \right) q_j \right)^{\nu_i} - k_{d,i} q_i c_{p,0}^{\nu_i} \end{align} \f] Component @c 0 is assumed to be salt. + * Multiple bound states are not supported. Components without bound state (i.e., non-binding components) are supported. + * * See @cite Brooks1992. * @tparam ParamHandler_t Type that can add support for external function dependence */ -template -class StericMassActionBindingBase : public ParamHandlerBindingModelBase +template class StericMassActionBindingBase : public ParamHandlerBindingModelBase { public: + StericMassActionBindingBase() + { + } + virtual ~StericMassActionBindingBase() CADET_NOEXCEPT + { + } - StericMassActionBindingBase() { } - virtual ~StericMassActionBindingBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { const bool res = BindingModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); // Guarantee that salt has exactly one bound state if (nBound[0] != 1) - throw InvalidParameterException("Steric Mass Action binding model requires exactly one bound state for salt component"); + throw InvalidParameterException( + "Steric Mass Action binding model requires exactly one bound state for salt component"); // First flux is salt, which is always quasi-stationary _reactionQuasistationarity[0] = true; @@ -125,14 +140,28 @@ class StericMassActionBindingBase : public ParamHandlerBindingModelBase::_nComp; using ParamHandlerBindingModelBase::_nBoundStates; - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } template int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const { using CpStateParamType = typename DoubleActivePromoter::type; using StateParamType = typename DoubleActivePromoter::type; - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); - // Salt flux: nu_0 * q_0 - Lambda + Sum[nu_j * q_j, j] == 0 - // <=> nu_0 * q_0 == Lambda - Sum[nu_j * q_j, j] + // Salt flux: nu_0 * q_0 - Lambda + Sum[nu_j * q_j, j] == 0 + // <=> nu_0 * q_0 == Lambda - Sum[nu_j * q_j, j] // Also compute \bar{q}_0 = nu_0 * q_0 - Sum[sigma_j * q_j, j] res[0] = static_cast(p->nu[0]) * y[0] - static_cast(p->lambda); StateParamType q0_bar = static_cast(p->nu[0]) * y[0]; @@ -221,7 +254,8 @@ class StericMassActionBindingBase : public ParamHandlerBindingModelBase(p->kD[i]) * y[bndIdx] * c0_pow_nu - static_cast(p->kA[i]) * yCp[i] * q0_bar_pow_nu; + res[bndIdx] = static_cast(p->kD[i]) * y[bndIdx] * c0_pow_nu - + static_cast(p->kA[i]) * yCp[i] * q0_bar_pow_nu; // Next bound component ++bndIdx; @@ -231,9 +265,11 @@ class StericMassActionBindingBase : public ParamHandlerBindingModelBase - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); double q0_bar = static_cast(p->nu[0]) * y[0]; @@ -272,17 +308,18 @@ class StericMassActionBindingBase : public ParamHandlerBindingModelBase(p->kA[i]); const double kd = static_cast(p->kD[i]); const double nu = static_cast(p->nu[i]) / static_cast(p->nu[0]); - const double c0_pow_nu = pow(yCp0_divRef, nu); + const double c0_pow_nu = pow(yCp0_divRef, nu); const double q0_bar_pow_nu = pow(q0_bar_divRef, nu); - const double c0_pow_nu_m1_divRef = pow(yCp0_divRef, nu - 1.0) / refC0; + const double c0_pow_nu_m1_divRef = pow(yCp0_divRef, nu - 1.0) / refC0; const double q0_bar_pow_nu_m1_divRef = nu * pow(q0_bar_divRef, nu - 1.0) / refQ; // dres_i / dc_{p,0} @@ -302,7 +339,8 @@ class StericMassActionBindingBase : public ParamHandlerBindingModelBase(p->sigma[j])); - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] + // corresponds to q_j. ++bndIdx2; } @@ -317,34 +355,42 @@ class StericMassActionBindingBase : public ParamHandlerBindingModelBase StericMassActionBinding; typedef StericMassActionBindingBase ExternalStericMassActionBinding; namespace binding { - void registerStericMassActionModel(std::unordered_map>& bindings) - { - bindings[StericMassActionBinding::identifier()] = []() { return new StericMassActionBinding(); }; - bindings[ExternalStericMassActionBinding::identifier()] = []() { return new ExternalStericMassActionBinding(); }; - } -} // namespace binding +void registerStericMassActionModel(std::unordered_map>& bindings) +{ + bindings[StericMassActionBinding::identifier()] = []() { return new StericMassActionBinding(); }; + bindings[ExternalStericMassActionBinding::identifier()] = []() { return new ExternalStericMassActionBinding(); }; +} +} // namespace binding -class NoJacobianStericMassActionBinding : public model::StericMassActionBinding { +class NoJacobianStericMassActionBinding : public model::StericMassActionBinding +{ public: - - static const char* identity() { return "NO_JACOBIAN_STERIC_MASS_ACTION"; } + static const char* identity() + { + return "NO_JACOBIAN_STERIC_MASS_ACTION"; + } CADET_BINDINGMODELBASE_BOILERPLATE protected: - - const char* name() const CADET_NOEXCEPT override { return identity(); } - bool implementsAnalyticJacobian() const CADET_NOEXCEPT override { return false; } + const char* name() const CADET_NOEXCEPT override + { + return identity(); + } + bool implementsAnalyticJacobian() const CADET_NOEXCEPT override + { + return false; + } template - void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const + void jacobianImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double const* yCp, + int offsetCp, RowIterator jac, LinearBufferAllocator workSpace) const { BindingModelBase::jacobianImpl(t, secIdx, colPos, y, yCp, offsetCp, jac, workSpace); } @@ -352,12 +398,13 @@ class NoJacobianStericMassActionBinding : public model::StericMassActionBinding namespace binding { - void registerNoJacobianStericMassActionModel(std::unordered_map>& bindings) - { - bindings[NoJacobianStericMassActionBinding::identity()] = []() { return new NoJacobianStericMassActionBinding(); }; - } -} // namespace binding +void registerNoJacobianStericMassActionModel( + std::unordered_map>& bindings) +{ + bindings[NoJacobianStericMassActionBinding::identity()] = []() { return new NoJacobianStericMassActionBinding(); }; +} +} // namespace binding -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/extfun/LinearInterpolationExternalFunction.cpp b/src/libcadet/model/extfun/LinearInterpolationExternalFunction.cpp index 3f8e8ba58..90f388f8a 100644 --- a/src/libcadet/model/extfun/LinearInterpolationExternalFunction.cpp +++ b/src/libcadet/model/extfun/LinearInterpolationExternalFunction.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides a linearly interpolated external function from moving data points without radial dependence. */ @@ -44,11 +44,21 @@ namespace model class LinearInterpolationExternalFunction : public IExternalFunction { public: - LinearInterpolationExternalFunction() { } - virtual ~LinearInterpolationExternalFunction() { } + LinearInterpolationExternalFunction() + { + } + virtual ~LinearInterpolationExternalFunction() + { + } - static const char* identifier() { return "LINEAR_INTERP_DATA"; } - virtual const char* name() const CADET_NOEXCEPT { return LinearInterpolationExternalFunction::identifier(); } + static const char* identifier() + { + return "LINEAR_INTERP_DATA"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return LinearInterpolationExternalFunction::identifier(); + } virtual bool configure(IParameterProvider* paramProvider) { @@ -65,12 +75,14 @@ class LinearInterpolationExternalFunction : public IExternalFunction return true; } - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { } + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) + { + } virtual double externalProfile(double t, double z, double rho, double r, unsigned int sec) { // z denotes relative axial position in the column, i.e., z is in [0,1]. - // + // // The coordinate system of the external profile begins at the column outlet // and points backward to the column inlet. // @@ -143,15 +155,17 @@ class LinearInterpolationExternalFunction : public IExternalFunction private: double _velocity; //!< Velocity of the movement of the external profile in [1/s] (normalized by column length) std::vector _dataY; //!< External profile data points (function values) - std::vector _time; //!< Time point of each measurement in [s] + std::vector _time; //!< Time point of each measurement in [s] }; namespace extfun { - void registerLinearInterpolation(std::unordered_map>& extFuns) - { - extFuns[LinearInterpolationExternalFunction::identifier()] = []() { return new LinearInterpolationExternalFunction(); }; - } +void registerLinearInterpolation(std::unordered_map>& extFuns) +{ + extFuns[LinearInterpolationExternalFunction::identifier()] = []() { + return new LinearInterpolationExternalFunction(); + }; +} } // namespace extfun } // namespace model diff --git a/src/libcadet/model/extfun/PiecewiseCubicPolyExternalFunction.cpp b/src/libcadet/model/extfun/PiecewiseCubicPolyExternalFunction.cpp index b07944fb3..1dc14b24c 100644 --- a/src/libcadet/model/extfun/PiecewiseCubicPolyExternalFunction.cpp +++ b/src/libcadet/model/extfun/PiecewiseCubicPolyExternalFunction.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides a piecewise cubic polynomial external function. */ @@ -34,14 +34,14 @@ namespace model * @brief A piecewise cubic polynomial external function * @details This external function takes into account time and axial position in the * column, but ignores radial position in the bead. A quantity of interest - * is measured at the column outlet and represented by a piecewise cubic + * is measured at the column outlet and represented by a piecewise cubic * polynomial (which is more general than a cubic spline due to missing * smoothness and continuity conditions). It is assumed that this quantity * is transported inside the column with a known velocity. Since the restart * of the time integrator cannot be detected as long as the section times * do not align with the time integrator's continuity sections, the profile * should at least be continuous. - * + * * Note that the @c SECTION_TIMES parameter is independent of the global * section times. The coefficients of the polynomials in the respective * sections are just appended to one array for each coefficient. Thus, @@ -51,12 +51,22 @@ namespace model class PiecewiseCubicPolyExternalFunction : public cadet::IExternalFunction { public: - PiecewiseCubicPolyExternalFunction() { } + PiecewiseCubicPolyExternalFunction() + { + } - virtual ~PiecewiseCubicPolyExternalFunction() CADET_NOEXCEPT { } + virtual ~PiecewiseCubicPolyExternalFunction() CADET_NOEXCEPT + { + } - static const char* identifier() { return "PIECEWISE_CUBIC_POLY"; } - virtual const char* name() const CADET_NOEXCEPT { return PiecewiseCubicPolyExternalFunction::identifier(); } + static const char* identifier() + { + return "PIECEWISE_CUBIC_POLY"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return PiecewiseCubicPolyExternalFunction::identifier(); + } virtual bool configure(IParameterProvider* paramProvider) { @@ -71,8 +81,8 @@ class PiecewiseCubicPolyExternalFunction : public cadet::IExternalFunction _velocity = paramProvider->getDouble("VELOCITY"); // Check sizes - return (_sectionTimes.size() >= 2) && (_const.size() == _sectionTimes.size()-1) && (_const.size() == _lin.size()) - && (_const.size() == _quad.size()) && (_const.size() == _cub.size()); + return (_sectionTimes.size() >= 2) && (_const.size() == _sectionTimes.size() - 1) && + (_const.size() == _lin.size()) && (_const.size() == _quad.size()) && (_const.size() == _cub.size()); } virtual double externalProfile(double t, double z, double rho, double r, unsigned int sec) @@ -86,7 +96,7 @@ class PiecewiseCubicPolyExternalFunction : public cadet::IExternalFunction if (transT >= _sectionTimes.back()) { const int n = _sectionTimes.size(); - const double tShift = _sectionTimes[n-1] - _sectionTimes[n-2]; + const double tShift = _sectionTimes[n - 1] - _sectionTimes[n - 2]; return _const.back() + tShift * (_lin.back() + tShift * (_quad.back() + tShift * _cub.back())); } @@ -96,9 +106,10 @@ class PiecewiseCubicPolyExternalFunction : public cadet::IExternalFunction // This function evaluates a piecewise cubic polynomial given on some intervals // called sections. On each section a polynomial of degree 3 is evaluated: - // - // p_i(t) = CUBE_COEFF[i] * (t - t_i)^3 + QUAD_COEFF[i] * (t - t_i)^2 + LIN_COEFF[i] * (t - t_i) + CONST_COEFF[i], - // + // + // p_i(t) = CUBE_COEFF[i] * (t - t_i)^3 + QUAD_COEFF[i] * (t - t_i)^2 + LIN_COEFF[i] * (t - t_i) + + // CONST_COEFF[i], + // // where p_i is the polynomial on section i given by the interval [t_i, t_{i+1}]. const double tShift = transT - _sectionTimes[idx]; @@ -124,10 +135,10 @@ class PiecewiseCubicPolyExternalFunction : public cadet::IExternalFunction // This function evaluates a piecewise cubic polynomial given on some intervals // called sections. On each section a polynomial of degree 3 is evaluated: - // - // p_i(t) = CUBE_COEFF[i] * (t - t_i)^3 + QUAD_COEFF[i] * (t - t_i)^2 + LIN_COEFF[i] * (t - t_i) + CONST_COEFF[i], - // p_i'(t) = 3 * CUBE_COEFF[i] * (t - t_i)^2 + 2 * QUAD_COEFF[i] * (t - t_i) + LIN_COEFF[i], - // + // + // p_i(t) = CUBE_COEFF[i] * (t - t_i)^3 + QUAD_COEFF[i] * (t - t_i)^2 + LIN_COEFF[i] * (t - t_i) + + // CONST_COEFF[i], p_i'(t) = 3 * CUBE_COEFF[i] * (t - t_i)^2 + 2 * QUAD_COEFF[i] * (t - t_i) + LIN_COEFF[i], + // // where p_i is the polynomial on section i given by the interval [t_i, t_{i+1}]. const double tShift = transT - _sectionTimes[idx]; @@ -136,24 +147,29 @@ class PiecewiseCubicPolyExternalFunction : public cadet::IExternalFunction return _lin[idx] + tShift * (2.0 * _quad[idx] + tShift * 3.0 * _cub[idx]); } - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) CADET_NOEXCEPT { } + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, + unsigned int nSections) CADET_NOEXCEPT + { + } private: double _velocity; //!< Velocity of the movement of the external profile in [1/s] (normalized by column length) std::vector _sectionTimes; //!< Section times std::vector _const; //!< Constant coefficient of each polynomial piece - std::vector _lin; //!< Linear coefficient of each polynomial piece - std::vector _quad; //!< Quadratic coefficient of each polynomial piece - std::vector _cub; //!< Cubic coefficient of each polynomial piece + std::vector _lin; //!< Linear coefficient of each polynomial piece + std::vector _quad; //!< Quadratic coefficient of each polynomial piece + std::vector _cub; //!< Cubic coefficient of each polynomial piece }; namespace extfun { - void registerPiecewiseCubicPoly(std::unordered_map>& extFuns) - { - extFuns[PiecewiseCubicPolyExternalFunction::identifier()] = []() { return new PiecewiseCubicPolyExternalFunction(); }; - } +void registerPiecewiseCubicPoly(std::unordered_map>& extFuns) +{ + extFuns[PiecewiseCubicPolyExternalFunction::identifier()] = []() { + return new PiecewiseCubicPolyExternalFunction(); + }; +} } // namespace extfun } // namespace model diff --git a/src/libcadet/model/inlet/PiecewiseCubicPoly.cpp b/src/libcadet/model/inlet/PiecewiseCubicPoly.cpp index 03d650e79..7870f8520 100644 --- a/src/libcadet/model/inlet/PiecewiseCubicPoly.cpp +++ b/src/libcadet/model/inlet/PiecewiseCubicPoly.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides a piecewise cubic polynomial inlet profiles. */ @@ -40,7 +40,7 @@ namespace model * @details On each section, a cubic polynomial is given as profile of each component. * Since continuity or smoothness is not enforced, this ansatz is more general * than a cubic spline. - * + * * Note that the section times of the simulator have to match the breaks of the * piecewise cubic polynomial. */ @@ -48,18 +48,29 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile { public: #if CADET_COMPILETIME_HASH - PiecewiseCubicPolyInlet() { } + PiecewiseCubicPolyInlet() + { + } #else - PiecewiseCubicPolyInlet() : _hashCons(hashString("CONST_COEFF")), _hashLin(hashString("LIN_COEFF")), - _hashQuad(hashString("QUAD_COEFF")), _hashCub(hashString("CUBE_COEFF")), _hashSectionTimes(hashString("SECTION_TIMES")) + PiecewiseCubicPolyInlet() + : _hashCons(hashString("CONST_COEFF")), _hashLin(hashString("LIN_COEFF")), _hashQuad(hashString("QUAD_COEFF")), + _hashCub(hashString("CUBE_COEFF")), _hashSectionTimes(hashString("SECTION_TIMES")) { } #endif - virtual ~PiecewiseCubicPolyInlet() CADET_NOEXCEPT { } + virtual ~PiecewiseCubicPolyInlet() CADET_NOEXCEPT + { + } - static const char* identifier() { return "PIECEWISE_CUBIC_POLY"; } - virtual const char* name() const CADET_NOEXCEPT { return PiecewiseCubicPolyInlet::identifier(); } + static const char* identifier() + { + return "PIECEWISE_CUBIC_POLY"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return PiecewiseCubicPolyInlet::identifier(); + } virtual std::vector availableParameters(unsigned int unitOpIdx) CADET_NOEXCEPT { @@ -69,12 +80,18 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile { for (unsigned int comp = 0; comp < _nComp; ++comp) { - params.push_back(cadet::makeParamId(_hashCons, unitOpIdx, comp, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, sec)); - params.push_back(cadet::makeParamId(_hashLin, unitOpIdx, comp, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, sec)); - params.push_back(cadet::makeParamId(_hashQuad, unitOpIdx, comp, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, sec)); - params.push_back(cadet::makeParamId(_hashCub, unitOpIdx, comp, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, sec)); + params.push_back(cadet::makeParamId(_hashCons, unitOpIdx, comp, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, sec)); + params.push_back(cadet::makeParamId(_hashLin, unitOpIdx, comp, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, sec)); + params.push_back(cadet::makeParamId(_hashQuad, unitOpIdx, comp, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, sec)); + params.push_back(cadet::makeParamId(_hashCub, unitOpIdx, comp, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, sec)); } - params.push_back(cadet::makeParamId(_hashSectionTimes, cadet::UnitOpIndep, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, sec)); + params.push_back(cadet::makeParamId(_hashSectionTimes, cadet::UnitOpIndep, cadet::CompIndep, + cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, + sec)); } return params; @@ -84,15 +101,16 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile { // This function evaluates a piecewise cubic polynomial given on some intervals // called sections. On each section a polynomial of degree 3 is evaluated: - // - // p_i(t) = CUBE_COEFF[i] * (t - t_i)^3 + QUAD_COEFF[i] * (t - t_i)^2 + LIN_COEFF[i] * (t - t_i) + CONST_COEFF[i], - // + // + // p_i(t) = CUBE_COEFF[i] * (t - t_i)^3 + QUAD_COEFF[i] * (t - t_i)^2 + LIN_COEFF[i] * (t - t_i) + + // CONST_COEFF[i], + // // where p_i is the polynomial on section i given by the interval [t_i, t_{i+1}]. cadet_assert(sec < _sectionTimes.size()); const double tShift = t - _sectionTimes[sec]; - const unsigned int wrapSec = sec % (_const.size()/_nComp); + const unsigned int wrapSec = sec % (_const.size() / _nComp); double const* const con = _const.data() + wrapSec * _nComp; double const* const lin = _lin.data() + wrapSec * _nComp; @@ -117,18 +135,19 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile const double tShift = t - _sectionTimes[sec]; // SECTION_TIMES is global and, thus, has no associated unitOp - if ((pId.name == _hashSectionTimes) && (pId.unitOperation == cadet::UnitOpIndep) && (pId.reaction == cadet::ReactionIndep) && - (pId.component == cadet::CompIndep) && (pId.boundState == cadet::BoundStateIndep) && (pId.particleType == cadet::ParTypeIndep)) + if ((pId.name == _hashSectionTimes) && (pId.unitOperation == cadet::UnitOpIndep) && + (pId.reaction == cadet::ReactionIndep) && (pId.component == cadet::CompIndep) && + (pId.boundState == cadet::BoundStateIndep) && (pId.particleType == cadet::ParTypeIndep)) { - const unsigned int wrapSec = sec % (_const.size()/_nComp); + const unsigned int wrapSec = sec % (_const.size() / _nComp); double const* const lin = _lin.data() + wrapSec * _nComp; double const* const quad = _quad.data() + wrapSec * _nComp; double const* const cub = _cub.data() + wrapSec * _nComp; - // p_i(t) = CUBE_COEFF[i] * (t - t_i)^3 + QUAD_COEFF[i] * (t - t_i)^2 + LIN_COEFF[i] * (t - t_i) + CONST_COEFF[i] - // Now suppose t_i = t_i(q) and t = t(q), then - // dp_i / dt_i = -3 * CUBE_COEFF[i] * (t - t_i)^2 - 2 * QUAD_COEFF[i] * (t - t_i) - LIN_COEFF[i] + // p_i(t) = CUBE_COEFF[i] * (t - t_i)^3 + QUAD_COEFF[i] * (t - t_i)^2 + LIN_COEFF[i] * (t - t_i) + + // CONST_COEFF[i] Now suppose t_i = t_i(q) and t = t(q), then dp_i / dt_i = -3 * CUBE_COEFF[i] * (t - t_i)^2 + // - 2 * QUAD_COEFF[i] * (t - t_i) - LIN_COEFF[i] for (unsigned int i = 0; i < _nComp; ++i) paramDeriv[i] = -lin[i] - tShift * (2.0 * quad[i] + tShift * 3.0 * cub[i]); @@ -139,18 +158,18 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile // Assume that t' = dt / dq = 0 for all handled parameters q switch (pId.name) { - case _hashCons: - paramDeriv[pId.component] = 1.0; - break; - case _hashLin: - paramDeriv[pId.component] = tShift; - break; - case _hashQuad: - paramDeriv[pId.component] = tShift * tShift; - break; - case _hashCub: - paramDeriv[pId.component] = tShift * tShift * tShift; - break; + case _hashCons: + paramDeriv[pId.component] = 1.0; + break; + case _hashLin: + paramDeriv[pId.component] = tShift; + break; + case _hashQuad: + paramDeriv[pId.component] = tShift * tShift; + break; + case _hashCub: + paramDeriv[pId.component] = tShift * tShift * tShift; + break; } #else // Assume that t' = dt / dq = 0 for all handled parameters q @@ -170,7 +189,7 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile cadet_assert(sec < _sectionTimes.size()); const double tShift = t - _sectionTimes[sec]; - const unsigned int wrapSec = sec % (_const.size()/_nComp); + const unsigned int wrapSec = sec % (_const.size() / _nComp); double const* const lin = _lin.data() + wrapSec * _nComp; double const* const quad = _quad.data() + wrapSec * _nComp; @@ -194,17 +213,18 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile const double tShift = t - _sectionTimes[sec]; // SECTION_TIMES is global and, thus, has no associated unitOp - if ((pId.name == _hashSectionTimes) && (pId.unitOperation == cadet::UnitOpIndep) && (pId.reaction == cadet::ReactionIndep) && - (pId.component == cadet::CompIndep) && (pId.boundState == cadet::BoundStateIndep) && (pId.particleType == cadet::ParTypeIndep)) + if ((pId.name == _hashSectionTimes) && (pId.unitOperation == cadet::UnitOpIndep) && + (pId.reaction == cadet::ReactionIndep) && (pId.component == cadet::CompIndep) && + (pId.boundState == cadet::BoundStateIndep) && (pId.particleType == cadet::ParTypeIndep)) { - const unsigned int wrapSec = sec % (_const.size()/_nComp); + const unsigned int wrapSec = sec % (_const.size() / _nComp); double const* const quad = _quad.data() + wrapSec * _nComp; double const* const cub = _cub.data() + wrapSec * _nComp; - // p_i(t) = CUBE_COEFF[i] * (t - t_i)^3 + QUAD_COEFF[i] * (t - t_i)^2 + LIN_COEFF[i] * (t - t_i) + CONST_COEFF[i] - // Now suppose t_i = t_i(q) and t = t(q), then - // dp_i / dt_i = -3 * CUBE_COEFF[i] * (t - t_i)^2 - 2 * QUAD_COEFF[i] * (t - t_i) - LIN_COEFF[i] + // p_i(t) = CUBE_COEFF[i] * (t - t_i)^3 + QUAD_COEFF[i] * (t - t_i)^2 + LIN_COEFF[i] * (t - t_i) + + // CONST_COEFF[i] Now suppose t_i = t_i(q) and t = t(q), then dp_i / dt_i = -3 * CUBE_COEFF[i] * (t - t_i)^2 + // - 2 * QUAD_COEFF[i] * (t - t_i) - LIN_COEFF[i] for (unsigned int i = 0; i < _nComp; ++i) deriv[i] = -(2.0 * quad[i] + tShift * 6.0 * cub[i]); @@ -215,18 +235,18 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile // Assume that t' = dt / dq = 0 for all handled parameters q switch (pId.name) { - case _hashCons: - deriv[pId.component] = 0.0; - break; - case _hashLin: - deriv[pId.component] = 1.0; - break; - case _hashQuad: - deriv[pId.component] = 2.0 * tShift; - break; - case _hashCub: - deriv[pId.component] = 3.0 * tShift * tShift; - break; + case _hashCons: + deriv[pId.component] = 0.0; + break; + case _hashLin: + deriv[pId.component] = 1.0; + break; + case _hashQuad: + deriv[pId.component] = 2.0 * tShift; + break; + case _hashCub: + deriv[pId.component] = 3.0 * tShift * tShift; + break; } #else // Assume that t' = dt / dq = 0 for all handled parameters q @@ -243,25 +263,25 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile virtual void setParameterValue(const cadet::ParameterId& pId, double value) { - const unsigned int wrapSec = pId.section % (_const.size()/_nComp); + const unsigned int wrapSec = pId.section % (_const.size() / _nComp); #if CADET_COMPILETIME_HASH switch (pId.name) { - case _hashCons: - _const[pId.component + wrapSec * _nComp] = value; - break; - case _hashLin: - _lin[pId.component + wrapSec * _nComp] = value; - break; - case _hashQuad: - _quad[pId.component + wrapSec * _nComp] = value; - break; - case _hashCub: - _cub[pId.component + wrapSec * _nComp] = value; - break; - case _hashSectionTimes: - _sectionTimes[pId.section] = value; - break; + case _hashCons: + _const[pId.component + wrapSec * _nComp] = value; + break; + case _hashLin: + _lin[pId.component + wrapSec * _nComp] = value; + break; + case _hashQuad: + _quad[pId.component + wrapSec * _nComp] = value; + break; + case _hashCub: + _cub[pId.component + wrapSec * _nComp] = value; + break; + case _hashSectionTimes: + _sectionTimes[pId.section] = value; + break; } #else if (pId.name == _hashCons) @@ -279,20 +299,20 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile virtual double getParameterValue(const cadet::ParameterId& pId) { - const unsigned int wrapSec = pId.section % (_const.size()/_nComp); + const unsigned int wrapSec = pId.section % (_const.size() / _nComp); #if CADET_COMPILETIME_HASH switch (pId.name) { - case _hashCons: - return _const[pId.component + wrapSec * _nComp]; - case _hashLin: - return _lin[pId.component + wrapSec * _nComp]; - case _hashQuad: - return _quad[pId.component + wrapSec * _nComp]; - case _hashCub: - return _cub[pId.component + wrapSec * _nComp]; - case _hashSectionTimes: - return _sectionTimes[pId.section]; + case _hashCons: + return _const[pId.component + wrapSec * _nComp]; + case _hashLin: + return _lin[pId.component + wrapSec * _nComp]; + case _hashQuad: + return _quad[pId.component + wrapSec * _nComp]; + case _hashCub: + return _cub[pId.component + wrapSec * _nComp]; + case _hashSectionTimes: + return _sectionTimes[pId.section]; } #else if (pId.name == _hashCons) @@ -309,24 +329,58 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile return std::numeric_limits::quiet_NaN(); } - virtual void numComponents(unsigned int nComp) CADET_NOEXCEPT { _nComp = nComp; } + virtual void numComponents(unsigned int nComp) CADET_NOEXCEPT + { + _nComp = nComp; + } - inline const std::vector& sectionTimes() const CADET_NOEXCEPT { return _sectionTimes; } - inline void sectionTimes(const std::vector& sectionTimes) CADET_NOEXCEPT { _sectionTimes = sectionTimes; } + inline const std::vector& sectionTimes() const CADET_NOEXCEPT + { + return _sectionTimes; + } + inline void sectionTimes(const std::vector& sectionTimes) CADET_NOEXCEPT + { + _sectionTimes = sectionTimes; + } - inline const std::vector& constantCoeff() const CADET_NOEXCEPT { return _const; } - inline void constantCoeff(const std::vector& cons) CADET_NOEXCEPT { _const = cons; } + inline const std::vector& constantCoeff() const CADET_NOEXCEPT + { + return _const; + } + inline void constantCoeff(const std::vector& cons) CADET_NOEXCEPT + { + _const = cons; + } - inline const std::vector& linearCoeff() const CADET_NOEXCEPT { return _lin; } - inline void linearCoeff(const std::vector& lin) CADET_NOEXCEPT { _lin = lin; } + inline const std::vector& linearCoeff() const CADET_NOEXCEPT + { + return _lin; + } + inline void linearCoeff(const std::vector& lin) CADET_NOEXCEPT + { + _lin = lin; + } - inline const std::vector& quadraticCoeff() const CADET_NOEXCEPT { return _quad; } - inline void quadraticCoeff(const std::vector& quad) CADET_NOEXCEPT { _quad = quad; } + inline const std::vector& quadraticCoeff() const CADET_NOEXCEPT + { + return _quad; + } + inline void quadraticCoeff(const std::vector& quad) CADET_NOEXCEPT + { + _quad = quad; + } - inline const std::vector& cubicCoeff() const CADET_NOEXCEPT { return _cub; } - inline void cubicCoeff(const std::vector& cub) CADET_NOEXCEPT { _cub = cub; } + inline const std::vector& cubicCoeff() const CADET_NOEXCEPT + { + return _cub; + } + inline void cubicCoeff(const std::vector& cub) CADET_NOEXCEPT + { + _cub = cub; + } - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) CADET_NOEXCEPT + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, + unsigned int nSections) CADET_NOEXCEPT { _sectionTimes = std::vector(secTimes, secTimes + nSections + 1); } @@ -338,7 +392,7 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile _const.clear(); _lin.clear(); _quad.clear(); - _cub.clear(); + _cub.clear(); if (!paramProvider) return false; @@ -363,7 +417,9 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile { const std::vector cons = paramProvider->getDoubleArray("CONST_COEFF"); if (cons.size() < nComp) - throw InvalidParameterException("Not enough elements in CONST_COEF (expected " + std::to_string(nComp) + ", got " + std::to_string(cons.size()) + " in section " + std::to_string(i) + ")"); + throw InvalidParameterException("Not enough elements in CONST_COEF (expected " + + std::to_string(nComp) + ", got " + std::to_string(cons.size()) + + " in section " + std::to_string(i) + ")"); _const.insert(_const.end(), cons.begin(), cons.begin() + nComp); } @@ -374,7 +430,9 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile { const std::vector lin = paramProvider->getDoubleArray("LIN_COEFF"); if (lin.size() < nComp) - throw InvalidParameterException("Not enough elements in LIN_COEFF (expected " + std::to_string(nComp) + ", got " + std::to_string(lin.size()) + " in section " + std::to_string(i) + ")"); + throw InvalidParameterException("Not enough elements in LIN_COEFF (expected " + + std::to_string(nComp) + ", got " + std::to_string(lin.size()) + + " in section " + std::to_string(i) + ")"); _lin.insert(_lin.end(), lin.begin(), lin.begin() + nComp); } @@ -385,7 +443,9 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile { const std::vector quad = paramProvider->getDoubleArray("QUAD_COEFF"); if (quad.size() < nComp) - throw InvalidParameterException("Not enough elements in QUAD_COEFF (expected " + std::to_string(nComp) + ", got " + std::to_string(quad.size()) + " in section " + std::to_string(i) + ")"); + throw InvalidParameterException("Not enough elements in QUAD_COEFF (expected " + + std::to_string(nComp) + ", got " + std::to_string(quad.size()) + + " in section " + std::to_string(i) + ")"); _quad.insert(_quad.end(), quad.begin(), quad.begin() + nComp); } @@ -396,7 +456,9 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile { const std::vector cub = paramProvider->getDoubleArray("CUBE_COEFF"); if (cub.size() < nComp) - throw InvalidParameterException("Not enough elements in CUBE_COEFF (expected " + std::to_string(nComp) + ", got " + std::to_string(cub.size()) + " in section " + std::to_string(i) + ")"); + throw InvalidParameterException("Not enough elements in CUBE_COEFF (expected " + + std::to_string(nComp) + ", got " + std::to_string(cub.size()) + + " in section " + std::to_string(i) + ")"); _cub.insert(_cub.end(), cub.begin(), cub.begin() + nComp); } @@ -413,7 +475,6 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile } private: - #if CADET_COMPILETIME_HASH static const cadet::StringHash _hashCons = "CONST_COEFF"_hash; static const cadet::StringHash _hashLin = "LIN_COEFF"_hash; @@ -439,10 +500,10 @@ class PiecewiseCubicPolyInlet : public cadet::IInletProfile namespace inlet { - void registerPiecewiseCubicPoly(std::unordered_map>& inlets) - { - inlets[PiecewiseCubicPolyInlet::identifier()] = []() { return new PiecewiseCubicPolyInlet(); }; - } +void registerPiecewiseCubicPoly(std::unordered_map>& inlets) +{ + inlets[PiecewiseCubicPolyInlet::identifier()] = []() { return new PiecewiseCubicPolyInlet(); }; +} } // namespace inlet } // namespace model diff --git a/src/libcadet/model/paramdep/DummyParameterDependence.cpp b/src/libcadet/model/paramdep/DummyParameterDependence.cpp index ec248d226..3a4d077ee 100644 --- a/src/libcadet/model/paramdep/DummyParameterDependence.cpp +++ b/src/libcadet/model/paramdep/DummyParameterDependence.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -32,134 +32,235 @@ namespace model class ConstantZeroParameterStateDependence : public ParameterStateDependenceBase { public: + ConstantZeroParameterStateDependence() + { + } + virtual ~ConstantZeroParameterStateDependence() CADET_NOEXCEPT + { + } - ConstantZeroParameterStateDependence() { } - virtual ~ConstantZeroParameterStateDependence() CADET_NOEXCEPT { } - - static const char* identifier() { return "CONSTANT_ZERO"; } - virtual const char* name() const CADET_NOEXCEPT { return ConstantZeroParameterStateDependence::identifier(); } + static const char* identifier() + { + return "CONSTANT_ZERO"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return ConstantZeroParameterStateDependence::identifier(); + } - virtual int jacobianElementsPerRowLiquid() const CADET_NOEXCEPT { return 0; } - virtual int jacobianElementsPerRowCombined() const CADET_NOEXCEPT { return 0; } + virtual int jacobianElementsPerRowLiquid() const CADET_NOEXCEPT + { + return 0; + } + virtual int jacobianElementsPerRowCombined() const CADET_NOEXCEPT + { + return 0; + } - virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const { } - virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const { } - virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const { } + virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, + double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const + { + } + virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp, double factor, int offset, int row, + linalg::DoubleSparseMatrix& jac) const + { + } + virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd, double factor, int offset, int row, + linalg::DoubleSparseMatrix& jac) const + { + } CADET_PARAMETERSTATEDEPENDENCE_BOILERPLATE protected: - - virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, const std::string& name) + virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + const std::string& name) { return true; } template - typename DoubleActivePromoter::type liquidParameterImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* y, int comp) const + typename DoubleActivePromoter::type liquidParameterImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* y, int comp) const { return 0.0; } template - void analyticJacobianLiquidAddImpl(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, RowIterator jac) const { } + void analyticJacobianLiquidAddImpl(const ColumnPosition& colPos, double param, double const* y, int comp, + double factor, int offset, RowIterator jac) const + { + } template - typename DoubleActivePromoter::type combinedParameterLiquidImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* yLiquid, StateType const* ySolid, int comp) const + typename DoubleActivePromoter::type combinedParameterLiquidImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* yLiquid, + StateType const* ySolid, + int comp) const { return 0.0; } template - void analyticJacobianCombinedAddLiquidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, RowIterator jac) const { } + void analyticJacobianCombinedAddLiquidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp, double factor, int offset, + RowIterator jac) const + { + } template - typename DoubleActivePromoter::type combinedParameterSolidImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* yLiquid, StateType const* ySolid, int bnd) const + typename DoubleActivePromoter::type combinedParameterSolidImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* yLiquid, + StateType const* ySolid, + int bnd) const { return 0.0; } template - void analyticJacobianCombinedAddSolidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, RowIterator jac) const { } + void analyticJacobianCombinedAddSolidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd, double factor, int offset, + RowIterator jac) const + { + } }; - /** * @brief Defines a parameter dependence that outputs constant 1.0 */ class ConstantOneParameterStateDependence : public ParameterStateDependenceBase { public: + ConstantOneParameterStateDependence() + { + } + virtual ~ConstantOneParameterStateDependence() CADET_NOEXCEPT + { + } - ConstantOneParameterStateDependence() { } - virtual ~ConstantOneParameterStateDependence() CADET_NOEXCEPT { } - - static const char* identifier() { return "CONSTANT_ONE"; } - virtual const char* name() const CADET_NOEXCEPT { return ConstantOneParameterStateDependence::identifier(); } + static const char* identifier() + { + return "CONSTANT_ONE"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return ConstantOneParameterStateDependence::identifier(); + } - virtual int jacobianElementsPerRowLiquid() const CADET_NOEXCEPT { return 0; } - virtual int jacobianElementsPerRowCombined() const CADET_NOEXCEPT { return 0; } + virtual int jacobianElementsPerRowLiquid() const CADET_NOEXCEPT + { + return 0; + } + virtual int jacobianElementsPerRowCombined() const CADET_NOEXCEPT + { + return 0; + } - virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const { } - virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const { } - virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const { } + virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, + double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const + { + } + virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp, double factor, int offset, int row, + linalg::DoubleSparseMatrix& jac) const + { + } + virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd, double factor, int offset, int row, + linalg::DoubleSparseMatrix& jac) const + { + } CADET_PARAMETERSTATEDEPENDENCE_BOILERPLATE protected: - - virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, const std::string& name) + virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + const std::string& name) { return true; } template - typename DoubleActivePromoter::type liquidParameterImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* y, int comp) const + typename DoubleActivePromoter::type liquidParameterImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* y, int comp) const { return 1.0; } template - void analyticJacobianLiquidAddImpl(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, RowIterator jac) const { } + void analyticJacobianLiquidAddImpl(const ColumnPosition& colPos, double param, double const* y, int comp, + double factor, int offset, RowIterator jac) const + { + } template - typename DoubleActivePromoter::type combinedParameterLiquidImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* yLiquid, StateType const* ySolid, int comp) const + typename DoubleActivePromoter::type combinedParameterLiquidImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* yLiquid, + StateType const* ySolid, + int comp) const { return 1.0; } template - void analyticJacobianCombinedAddLiquidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, RowIterator jac) const { } + void analyticJacobianCombinedAddLiquidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp, double factor, int offset, + RowIterator jac) const + { + } template - typename DoubleActivePromoter::type combinedParameterSolidImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* yLiquid, StateType const* ySolid, int bnd) const + typename DoubleActivePromoter::type combinedParameterSolidImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* yLiquid, + StateType const* ySolid, + int bnd) const { return 1.0; } template - void analyticJacobianCombinedAddSolidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, RowIterator jac) const { } + void analyticJacobianCombinedAddSolidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd, double factor, int offset, + RowIterator jac) const + { + } }; - /** * @brief Defines a parameter dependence that outputs constant 0.0 */ class ConstantZeroParameterParameterDependence : public ParameterParameterDependenceBase { public: + ConstantZeroParameterParameterDependence() + { + } + virtual ~ConstantZeroParameterParameterDependence() CADET_NOEXCEPT + { + } - ConstantZeroParameterParameterDependence() { } - virtual ~ConstantZeroParameterParameterDependence() CADET_NOEXCEPT { } - - static const char* identifier() { return "CONSTANT_ZERO"; } - virtual const char* name() const CADET_NOEXCEPT { return ConstantZeroParameterParameterDependence::identifier(); } + static const char* identifier() + { + return "CONSTANT_ZERO"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return ConstantZeroParameterParameterDependence::identifier(); + } CADET_PARAMETERPARAMETERDEPENDENCE_BOILERPLATE protected: - - virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, BoundStateIdx bndIdx, const std::string& name) + virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + BoundStateIdx bndIdx, const std::string& name) { return true; } @@ -171,32 +272,40 @@ class ConstantZeroParameterParameterDependence : public ParameterParameterDepend } template - ParamType getValueImpl(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, const ParamType& val) const + ParamType getValueImpl(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, + const ParamType& val) const { return 0.0; } - }; - /** * @brief Defines a parameter dependence that outputs constant 1.0 */ class ConstantOneParameterParameterDependence : public ParameterParameterDependenceBase { public: + ConstantOneParameterParameterDependence() + { + } + virtual ~ConstantOneParameterParameterDependence() CADET_NOEXCEPT + { + } - ConstantOneParameterParameterDependence() { } - virtual ~ConstantOneParameterParameterDependence() CADET_NOEXCEPT { } - - static const char* identifier() { return "CONSTANT_ONE"; } - virtual const char* name() const CADET_NOEXCEPT { return ConstantOneParameterParameterDependence::identifier(); } + static const char* identifier() + { + return "CONSTANT_ONE"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return ConstantOneParameterParameterDependence::identifier(); + } CADET_PARAMETERPARAMETERDEPENDENCE_BOILERPLATE protected: - - virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, BoundStateIdx bndIdx, const std::string& name) + virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + BoundStateIdx bndIdx, const std::string& name) { return true; } @@ -208,31 +317,40 @@ class ConstantOneParameterParameterDependence : public ParameterParameterDepende } template - ParamType getValueImpl(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, const ParamType& val) const + ParamType getValueImpl(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, + const ParamType& val) const { return 1.0; } - }; - namespace paramdep { - void registerDummyParamDependence(std::unordered_map>& paramDeps) - { - paramDeps[ConstantOneParameterStateDependence::identifier()] = []() { return new ConstantOneParameterStateDependence(); }; - paramDeps[ConstantZeroParameterStateDependence::identifier()] = []() { return new ConstantZeroParameterStateDependence(); }; - paramDeps["NONE"] = []() { return new ConstantOneParameterStateDependence(); }; - } - - void registerDummyParamDependence(std::unordered_map>& paramDeps) - { - paramDeps[ConstantOneParameterParameterDependence::identifier()] = []() { return new ConstantOneParameterParameterDependence(); }; - paramDeps[ConstantZeroParameterParameterDependence::identifier()] = []() { return new ConstantZeroParameterParameterDependence(); }; - paramDeps["NONE"] = []() { return new ConstantOneParameterParameterDependence(); }; - } -} // namespace paramdep - -} // namespace model - -} // namespace cadet +void registerDummyParamDependence( + std::unordered_map>& paramDeps) +{ + paramDeps[ConstantOneParameterStateDependence::identifier()] = []() { + return new ConstantOneParameterStateDependence(); + }; + paramDeps[ConstantZeroParameterStateDependence::identifier()] = []() { + return new ConstantZeroParameterStateDependence(); + }; + paramDeps["NONE"] = []() { return new ConstantOneParameterStateDependence(); }; +} + +void registerDummyParamDependence( + std::unordered_map>& paramDeps) +{ + paramDeps[ConstantOneParameterParameterDependence::identifier()] = []() { + return new ConstantOneParameterParameterDependence(); + }; + paramDeps[ConstantZeroParameterParameterDependence::identifier()] = []() { + return new ConstantZeroParameterParameterDependence(); + }; + paramDeps["NONE"] = []() { return new ConstantOneParameterParameterDependence(); }; +} +} // namespace paramdep + +} // namespace model + +} // namespace cadet diff --git a/src/libcadet/model/paramdep/IdentityParameterDependence.cpp b/src/libcadet/model/paramdep/IdentityParameterDependence.cpp index 0836186cb..0f3081b67 100644 --- a/src/libcadet/model/paramdep/IdentityParameterDependence.cpp +++ b/src/libcadet/model/paramdep/IdentityParameterDependence.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright ® The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -32,76 +32,131 @@ namespace model class IdentityParameterStateDependence : public ParameterStateDependenceBase { public: + IdentityParameterStateDependence() + { + } + virtual ~IdentityParameterStateDependence() CADET_NOEXCEPT + { + } - IdentityParameterStateDependence() { } - virtual ~IdentityParameterStateDependence() CADET_NOEXCEPT { } - - static const char* identifier() { return "IDENTITY"; } - virtual const char* name() const CADET_NOEXCEPT { return IdentityParameterStateDependence::identifier(); } + static const char* identifier() + { + return "IDENTITY"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return IdentityParameterStateDependence::identifier(); + } - virtual int jacobianElementsPerRowLiquid() const CADET_NOEXCEPT { return 0; } - virtual int jacobianElementsPerRowCombined() const CADET_NOEXCEPT { return 0; } + virtual int jacobianElementsPerRowLiquid() const CADET_NOEXCEPT + { + return 0; + } + virtual int jacobianElementsPerRowCombined() const CADET_NOEXCEPT + { + return 0; + } - virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const { } - virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const { } - virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const { } + virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, + double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const + { + } + virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp, double factor, int offset, int row, + linalg::DoubleSparseMatrix& jac) const + { + } + virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd, double factor, int offset, int row, + linalg::DoubleSparseMatrix& jac) const + { + } CADET_PARAMETERSTATEDEPENDENCE_BOILERPLATE protected: - - virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, const std::string& name) + virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + const std::string& name) { return true; } template - typename DoubleActivePromoter::type liquidParameterImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* y, int comp) const + typename DoubleActivePromoter::type liquidParameterImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* y, int comp) const { return param; } template - void analyticJacobianLiquidAddImpl(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, RowIterator jac) const { } + void analyticJacobianLiquidAddImpl(const ColumnPosition& colPos, double param, double const* y, int comp, + double factor, int offset, RowIterator jac) const + { + } template - typename DoubleActivePromoter::type combinedParameterLiquidImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* yLiquid, StateType const* ySolid, int comp) const + typename DoubleActivePromoter::type combinedParameterLiquidImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* yLiquid, + StateType const* ySolid, + int comp) const { return param; } template - void analyticJacobianCombinedAddLiquidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, RowIterator jac) const { } + void analyticJacobianCombinedAddLiquidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp, double factor, int offset, + RowIterator jac) const + { + } template - typename DoubleActivePromoter::type combinedParameterSolidImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* yLiquid, StateType const* ySolid, int bnd) const + typename DoubleActivePromoter::type combinedParameterSolidImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* yLiquid, + StateType const* ySolid, + int bnd) const { return param; } template - void analyticJacobianCombinedAddSolidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, RowIterator jac) const { } + void analyticJacobianCombinedAddSolidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd, double factor, int offset, + RowIterator jac) const + { + } }; - /** * @brief Defines an identity parameter dependence */ class IdentityParameterParameterDependence : public ParameterParameterDependenceBase { public: + IdentityParameterParameterDependence() + { + } + virtual ~IdentityParameterParameterDependence() CADET_NOEXCEPT + { + } - IdentityParameterParameterDependence() { } - virtual ~IdentityParameterParameterDependence() CADET_NOEXCEPT { } - - static const char* identifier() { return "IDENTITY"; } - virtual const char* name() const CADET_NOEXCEPT { return IdentityParameterParameterDependence::identifier(); } + static const char* identifier() + { + return "IDENTITY"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return IdentityParameterParameterDependence::identifier(); + } CADET_PARAMETERPARAMETERDEPENDENCE_BOILERPLATE protected: - - virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, BoundStateIdx bndIdx, const std::string& name) + virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + BoundStateIdx bndIdx, const std::string& name) { return true; } @@ -113,29 +168,32 @@ class IdentityParameterParameterDependence : public ParameterParameterDependence } template - ParamType getValueImpl(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, const ParamType& val) const + ParamType getValueImpl(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, + const ParamType& val) const { return val; } - }; - namespace paramdep { - void registerIdentityParamDependence(std::unordered_map>& paramDeps) - { - paramDeps[IdentityParameterStateDependence::identifier()] = []() { return new IdentityParameterStateDependence(); }; - paramDeps["NONE"] = []() { return new IdentityParameterStateDependence(); }; - } +void registerIdentityParamDependence( + std::unordered_map>& paramDeps) +{ + paramDeps[IdentityParameterStateDependence::identifier()] = []() { return new IdentityParameterStateDependence(); }; + paramDeps["NONE"] = []() { return new IdentityParameterStateDependence(); }; +} - void registerIdentityParamDependence(std::unordered_map>& paramDeps) - { - paramDeps[IdentityParameterParameterDependence::identifier()] = []() { return new IdentityParameterParameterDependence(); }; - paramDeps["NONE"] = []() { return new IdentityParameterParameterDependence(); }; - } -} // namespace paramdep +void registerIdentityParamDependence( + std::unordered_map>& paramDeps) +{ + paramDeps[IdentityParameterParameterDependence::identifier()] = []() { + return new IdentityParameterParameterDependence(); + }; + paramDeps["NONE"] = []() { return new IdentityParameterParameterDependence(); }; +} +} // namespace paramdep -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/paramdep/LiquidSaltSolidParameterDependence.cpp b/src/libcadet/model/paramdep/LiquidSaltSolidParameterDependence.cpp index 93cb3ad6a..9dc7b20f0 100644 --- a/src/libcadet/model/paramdep/LiquidSaltSolidParameterDependence.cpp +++ b/src/libcadet/model/paramdep/LiquidSaltSolidParameterDependence.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -28,68 +28,102 @@ namespace model namespace { - inline void readAndRegisterParameter(std::unordered_map& params, std::vector& out, const std::string& name, IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, int nElements) +inline void readAndRegisterParameter(std::unordered_map& params, std::vector& out, + const std::string& name, IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx, int nElements) +{ + const StringHash nameHash = hashStringRuntime(name); + + if (parTypeIdx == ParTypeIndep) { - const StringHash nameHash = hashStringRuntime(name); + const std::vector tmp = paramProvider.getDoubleArray(name); + if (static_cast(tmp.size()) < nElements) + throw InvalidParameterException(name + " contains too few elements (" + std::to_string(nElements) + + " required)"); + + out.reserve(nElements); + for (int i = 0; i < nElements; ++i) + out.push_back(tmp[i]); + + registerParam1DArray(params, out, [=](bool multi, unsigned int comp) { + return makeParamId(nameHash, unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep); + }); + } + else + { + std::ostringstream oss; + oss << name << "_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << parTypeIdx; - if (parTypeIdx == ParTypeIndep) + if (paramProvider.exists(oss.str())) { - const std::vector tmp = paramProvider.getDoubleArray(name); + const std::vector tmp = paramProvider.getDoubleArray(oss.str()); if (static_cast(tmp.size()) < nElements) - throw InvalidParameterException(name + " contains too few elements (" + std::to_string(nElements) + " required)"); + throw InvalidParameterException(oss.str() + " contains too few elements (" + std::to_string(nElements) + + " required)"); out.reserve(nElements); for (int i = 0; i < nElements; ++i) out.push_back(tmp[i]); - registerParam1DArray(params, out, [=](bool multi, unsigned int comp) { return makeParamId(nameHash, unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep); }); + registerParam1DArray(params, out, [=](bool multi, unsigned int comp) { + return makeParamId(nameHash, unitOpIdx, comp, parTypeIdx, BoundStateIndep, ReactionIndep, SectionIndep); + }); } else - { - std::ostringstream oss; - oss << name << "_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << parTypeIdx; - - if (paramProvider.exists(oss.str())) - { - const std::vector tmp = paramProvider.getDoubleArray(oss.str()); - if (static_cast(tmp.size()) < nElements) - throw InvalidParameterException(oss.str() + " contains too few elements (" + std::to_string(nElements) + " required)"); - - out.reserve(nElements); - for (int i = 0; i < nElements; ++i) - out.push_back(tmp[i]); - - registerParam1DArray(params, out, [=](bool multi, unsigned int comp) { return makeParamId(nameHash, unitOpIdx, comp, parTypeIdx, BoundStateIndep, ReactionIndep, SectionIndep); }); - } - else - throw InvalidParameterException("Field " + oss.str() + " not found"); - } + throw InvalidParameterException("Field " + oss.str() + " not found"); } } - +} // namespace /** - * @brief Defines an exponential parameter dependence for the solid phase of a combined cell based on the liquid phase salt concentration + * @brief Defines an exponential parameter dependence for the solid phase of a combined cell based on the liquid phase + * salt concentration */ class ExpLiquidSaltSolidParameterStateDependence : public ParameterStateDependenceBase { public: + ExpLiquidSaltSolidParameterStateDependence() : _factor(0), _multiplicator(0) + { + } + virtual ~ExpLiquidSaltSolidParameterStateDependence() CADET_NOEXCEPT + { + } - ExpLiquidSaltSolidParameterStateDependence() : _factor(0), _multiplicator(0) { } - virtual ~ExpLiquidSaltSolidParameterStateDependence() CADET_NOEXCEPT { } - - static const char* identifier() { return "LIQUID_SALT_EXPONENTIAL"; } - virtual const char* name() const CADET_NOEXCEPT { return ExpLiquidSaltSolidParameterStateDependence::identifier(); } + static const char* identifier() + { + return "LIQUID_SALT_EXPONENTIAL"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return ExpLiquidSaltSolidParameterStateDependence::identifier(); + } - virtual int jacobianElementsPerRowLiquid() const CADET_NOEXCEPT { return 0; } - virtual int jacobianElementsPerRowCombined() const CADET_NOEXCEPT { return 1; } + virtual int jacobianElementsPerRowLiquid() const CADET_NOEXCEPT + { + return 0; + } + virtual int jacobianElementsPerRowCombined() const CADET_NOEXCEPT + { + return 1; + } - virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const { } - virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const { } + virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, + double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const + { + } + virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp, double factor, int offset, int row, + linalg::DoubleSparseMatrix& jac) const + { + } - virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const + virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd, double factor, int offset, int row, + linalg::DoubleSparseMatrix& jac) const { - jac(row, offset) += factor * param * static_cast(_factor[bnd]) * exp(yLiquid[0] * static_cast(_multiplicator[bnd])) * static_cast(_multiplicator[bnd]); + jac(row, offset) += factor * param * static_cast(_factor[bnd]) * + exp(yLiquid[0] * static_cast(_multiplicator[bnd])) * + static_cast(_multiplicator[bnd]); } CADET_PARAMETERSTATEDEPENDENCE_BOILERPLATE @@ -98,68 +132,119 @@ class ExpLiquidSaltSolidParameterStateDependence : public ParameterStateDependen std::vector _factor; std::vector _multiplicator; - virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, const std::string& name) + virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + const std::string& name) { - readAndRegisterParameter(_parameters, _factor, name + "_EXPFACTOR", paramProvider, unitOpIdx, parTypeIdx, _nTotalBoundStates); - readAndRegisterParameter(_parameters, _multiplicator, name + "_EXPARGMULT", paramProvider, unitOpIdx, parTypeIdx, _nTotalBoundStates); + readAndRegisterParameter(_parameters, _factor, name + "_EXPFACTOR", paramProvider, unitOpIdx, parTypeIdx, + _nTotalBoundStates); + readAndRegisterParameter(_parameters, _multiplicator, name + "_EXPARGMULT", paramProvider, unitOpIdx, + parTypeIdx, _nTotalBoundStates); return true; } template - typename DoubleActivePromoter::type liquidParameterImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* y, int comp) const + typename DoubleActivePromoter::type liquidParameterImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* y, int comp) const { return 0.0; } template - void analyticJacobianLiquidAddImpl(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, RowIterator jac) const { } + void analyticJacobianLiquidAddImpl(const ColumnPosition& colPos, double param, double const* y, int comp, + double factor, int offset, RowIterator jac) const + { + } template - typename DoubleActivePromoter::type combinedParameterLiquidImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* yLiquid, StateType const* ySolid, int comp) const + typename DoubleActivePromoter::type combinedParameterLiquidImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* yLiquid, + StateType const* ySolid, + int comp) const { return 0.0; } template - void analyticJacobianCombinedAddLiquidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, RowIterator jac) const { } + void analyticJacobianCombinedAddLiquidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp, double factor, int offset, + RowIterator jac) const + { + } template - typename DoubleActivePromoter::type combinedParameterSolidImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* yLiquid, StateType const* ySolid, int bnd) const + typename DoubleActivePromoter::type combinedParameterSolidImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* yLiquid, + StateType const* ySolid, + int bnd) const { - return param * static_cast(_factor[bnd]) * exp(yLiquid[0] * static_cast(_multiplicator[bnd])); + return param * static_cast(_factor[bnd]) * + exp(yLiquid[0] * static_cast(_multiplicator[bnd])); } template - void analyticJacobianCombinedAddSolidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, RowIterator jac) const + void analyticJacobianCombinedAddSolidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd, double factor, int offset, + RowIterator jac) const { - jac[offset - bnd - _nComp] += factor * param * static_cast(_factor[bnd]) * exp(yLiquid[0] * static_cast(_multiplicator[bnd])) * static_cast(_multiplicator[bnd]); + jac[offset - bnd - _nComp] += factor * param * static_cast(_factor[bnd]) * + exp(yLiquid[0] * static_cast(_multiplicator[bnd])) * + static_cast(_multiplicator[bnd]); } }; - /** - * @brief Defines a power law parameter dependence for the solid phase of a combined cell based on the liquid phase salt concentration + * @brief Defines a power law parameter dependence for the solid phase of a combined cell based on the liquid phase salt + * concentration */ class PowerLiquidSaltSolidParameterStateDependence : public ParameterStateDependenceBase { public: + PowerLiquidSaltSolidParameterStateDependence() : _factor(0), _exponent(0) + { + } + virtual ~PowerLiquidSaltSolidParameterStateDependence() CADET_NOEXCEPT + { + } - PowerLiquidSaltSolidParameterStateDependence() : _factor(0), _exponent(0) { } - virtual ~PowerLiquidSaltSolidParameterStateDependence() CADET_NOEXCEPT { } - - static const char* identifier() { return "LIQUID_SALT_POWER"; } - virtual const char* name() const CADET_NOEXCEPT { return PowerLiquidSaltSolidParameterStateDependence::identifier(); } + static const char* identifier() + { + return "LIQUID_SALT_POWER"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return PowerLiquidSaltSolidParameterStateDependence::identifier(); + } - virtual int jacobianElementsPerRowLiquid() const CADET_NOEXCEPT { return 0; } - virtual int jacobianElementsPerRowCombined() const CADET_NOEXCEPT { return 1; } + virtual int jacobianElementsPerRowLiquid() const CADET_NOEXCEPT + { + return 0; + } + virtual int jacobianElementsPerRowCombined() const CADET_NOEXCEPT + { + return 1; + } - virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const { } - virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const { } + virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, + double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const + { + } + virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp, double factor, int offset, int row, + linalg::DoubleSparseMatrix& jac) const + { + } - virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const + virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd, double factor, int offset, int row, + linalg::DoubleSparseMatrix& jac) const { - jac(row, offset) += factor * param * static_cast(_factor[bnd]) * pow(yLiquid[0], static_cast(_exponent[bnd]) - 1.0) * static_cast(_exponent[bnd]); + jac(row, offset) += factor * param * static_cast(_factor[bnd]) * + pow(yLiquid[0], static_cast(_exponent[bnd]) - 1.0) * + static_cast(_exponent[bnd]); } CADET_PARAMETERSTATEDEPENDENCE_BOILERPLATE @@ -168,71 +253,128 @@ class PowerLiquidSaltSolidParameterStateDependence : public ParameterStateDepend std::vector _factor; std::vector _exponent; - virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, const std::string& name) + virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + const std::string& name) { - readAndRegisterParameter(_parameters, _factor, name + "_POWFACTOR", paramProvider, unitOpIdx, parTypeIdx, _nTotalBoundStates); - readAndRegisterParameter(_parameters, _exponent, name + "_POWEXP", paramProvider, unitOpIdx, parTypeIdx, _nTotalBoundStates); + readAndRegisterParameter(_parameters, _factor, name + "_POWFACTOR", paramProvider, unitOpIdx, parTypeIdx, + _nTotalBoundStates); + readAndRegisterParameter(_parameters, _exponent, name + "_POWEXP", paramProvider, unitOpIdx, parTypeIdx, + _nTotalBoundStates); return true; } template - typename DoubleActivePromoter::type liquidParameterImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* y, int comp) const + typename DoubleActivePromoter::type liquidParameterImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* y, int comp) const { return 0.0; } template - void analyticJacobianLiquidAddImpl(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, RowIterator jac) const { } + void analyticJacobianLiquidAddImpl(const ColumnPosition& colPos, double param, double const* y, int comp, + double factor, int offset, RowIterator jac) const + { + } template - typename DoubleActivePromoter::type combinedParameterLiquidImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* yLiquid, StateType const* ySolid, int comp) const + typename DoubleActivePromoter::type combinedParameterLiquidImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* yLiquid, + StateType const* ySolid, + int comp) const { return 0.0; } template - void analyticJacobianCombinedAddLiquidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, RowIterator jac) const { } + void analyticJacobianCombinedAddLiquidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp, double factor, int offset, + RowIterator jac) const + { + } template - typename DoubleActivePromoter::type combinedParameterSolidImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* yLiquid, StateType const* ySolid, int bnd) const + typename DoubleActivePromoter::type combinedParameterSolidImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* yLiquid, + StateType const* ySolid, + int bnd) const { return param * static_cast(_factor[bnd]) * pow(yLiquid[0], static_cast(_exponent[bnd])); } template - void analyticJacobianCombinedAddSolidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, RowIterator jac) const + void analyticJacobianCombinedAddSolidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd, double factor, int offset, + RowIterator jac) const { - jac[offset - bnd - _nComp] += factor * param * static_cast(_factor[bnd]) * pow(yLiquid[0], static_cast(_exponent[bnd]) - 1.0) * static_cast(_exponent[bnd]); + jac[offset - bnd - _nComp] += factor * param * static_cast(_factor[bnd]) * + pow(yLiquid[0], static_cast(_exponent[bnd]) - 1.0) * + static_cast(_exponent[bnd]); } }; - /** - * @brief Defines a parameter dependence for the solid phase of a combined cell based on the liquid phase salt concentration using the binding affinity of a colloidal binding model + * @brief Defines a parameter dependence for the solid phase of a combined cell based on the liquid phase salt + * concentration using the binding affinity of a colloidal binding model */ class ColloidalAffinityLiquidSaltSolidParameterStateDependence : public ParameterStateDependenceBase { public: + ColloidalAffinityLiquidSaltSolidParameterStateDependence() + : _lnKeqExp(0), _lnKeqFactor(0), _lnKeqConst(0), _powFactor(0), _powExponent(0), _expFactor(0), _expExponent(0) + { + } + virtual ~ColloidalAffinityLiquidSaltSolidParameterStateDependence() CADET_NOEXCEPT + { + } - ColloidalAffinityLiquidSaltSolidParameterStateDependence() : _lnKeqExp(0), _lnKeqFactor(0), _lnKeqConst(0), _powFactor(0), _powExponent(0), _expFactor(0), _expExponent(0) { } - virtual ~ColloidalAffinityLiquidSaltSolidParameterStateDependence() CADET_NOEXCEPT { } - - static const char* identifier() { return "LIQUID_SALT_COLLOIDAL_AFFINITY"; } - virtual const char* name() const CADET_NOEXCEPT { return ColloidalAffinityLiquidSaltSolidParameterStateDependence::identifier(); } + static const char* identifier() + { + return "LIQUID_SALT_COLLOIDAL_AFFINITY"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return ColloidalAffinityLiquidSaltSolidParameterStateDependence::identifier(); + } - virtual int jacobianElementsPerRowLiquid() const CADET_NOEXCEPT { return 0; } - virtual int jacobianElementsPerRowCombined() const CADET_NOEXCEPT { return 1; } + virtual int jacobianElementsPerRowLiquid() const CADET_NOEXCEPT + { + return 0; + } + virtual int jacobianElementsPerRowCombined() const CADET_NOEXCEPT + { + return 1; + } - virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const { } - virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const { } + virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, + double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const + { + } + virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp, double factor, int offset, int row, + linalg::DoubleSparseMatrix& jac) const + { + } - virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, int row, linalg::DoubleSparseMatrix& jac) const + virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd, double factor, int offset, int row, + linalg::DoubleSparseMatrix& jac) const { - const double logKeq = static_cast(_lnKeqFactor[bnd]) * pow(yLiquid[0], static_cast(_lnKeqExp[bnd])) + static_cast(_lnKeqConst[bnd]); - const double dLogKeq_dy = static_cast(_lnKeqFactor[bnd]) * pow(yLiquid[0], static_cast(_lnKeqExp[bnd]) - 1.0) * static_cast(_lnKeqExp[bnd]); - const double dPow_dy = static_cast(_powFactor[bnd]) * pow(logKeq, static_cast(_powExponent[bnd]) - 1.0) * static_cast(_powExponent[bnd]); - const double dExp_dy = static_cast(_expFactor[bnd]) * exp(logKeq * static_cast(_expExponent[bnd])) * static_cast(_expExponent[bnd]); + const double logKeq = + static_cast(_lnKeqFactor[bnd]) * pow(yLiquid[0], static_cast(_lnKeqExp[bnd])) + + static_cast(_lnKeqConst[bnd]); + const double dLogKeq_dy = static_cast(_lnKeqFactor[bnd]) * + pow(yLiquid[0], static_cast(_lnKeqExp[bnd]) - 1.0) * + static_cast(_lnKeqExp[bnd]); + const double dPow_dy = static_cast(_powFactor[bnd]) * + pow(logKeq, static_cast(_powExponent[bnd]) - 1.0) * + static_cast(_powExponent[bnd]); + const double dExp_dy = static_cast(_expFactor[bnd]) * + exp(logKeq * static_cast(_expExponent[bnd])) * + static_cast(_expExponent[bnd]); jac(row, offset) += factor * param * (dPow_dy + dExp_dy) * dLogKeq_dy; } @@ -247,68 +389,113 @@ class ColloidalAffinityLiquidSaltSolidParameterStateDependence : public Paramete std::vector _expFactor; std::vector _expExponent; - virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, const std::string& name) + virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + const std::string& name) { - readAndRegisterParameter(_parameters, _lnKeqExp, name + "_LOGKEQEXP", paramProvider, unitOpIdx, parTypeIdx, _nTotalBoundStates); - readAndRegisterParameter(_parameters, _lnKeqFactor, name + "_LOGKEQFACTOR", paramProvider, unitOpIdx, parTypeIdx, _nTotalBoundStates); - readAndRegisterParameter(_parameters, _lnKeqConst, name + "_LOGKEQCONST", paramProvider, unitOpIdx, parTypeIdx, _nTotalBoundStates); - - readAndRegisterParameter(_parameters, _powFactor, name + "_POWFACTOR", paramProvider, unitOpIdx, parTypeIdx, _nTotalBoundStates); - readAndRegisterParameter(_parameters, _powExponent, name + "_POWEXP", paramProvider, unitOpIdx, parTypeIdx, _nTotalBoundStates); - - readAndRegisterParameter(_parameters, _expFactor, name + "_EXPFACTOR", paramProvider, unitOpIdx, parTypeIdx, _nTotalBoundStates); - readAndRegisterParameter(_parameters, _expExponent, name + "_EXPARGMULT", paramProvider, unitOpIdx, parTypeIdx, _nTotalBoundStates); + readAndRegisterParameter(_parameters, _lnKeqExp, name + "_LOGKEQEXP", paramProvider, unitOpIdx, parTypeIdx, + _nTotalBoundStates); + readAndRegisterParameter(_parameters, _lnKeqFactor, name + "_LOGKEQFACTOR", paramProvider, unitOpIdx, + parTypeIdx, _nTotalBoundStates); + readAndRegisterParameter(_parameters, _lnKeqConst, name + "_LOGKEQCONST", paramProvider, unitOpIdx, parTypeIdx, + _nTotalBoundStates); + + readAndRegisterParameter(_parameters, _powFactor, name + "_POWFACTOR", paramProvider, unitOpIdx, parTypeIdx, + _nTotalBoundStates); + readAndRegisterParameter(_parameters, _powExponent, name + "_POWEXP", paramProvider, unitOpIdx, parTypeIdx, + _nTotalBoundStates); + + readAndRegisterParameter(_parameters, _expFactor, name + "_EXPFACTOR", paramProvider, unitOpIdx, parTypeIdx, + _nTotalBoundStates); + readAndRegisterParameter(_parameters, _expExponent, name + "_EXPARGMULT", paramProvider, unitOpIdx, parTypeIdx, + _nTotalBoundStates); return true; } template - typename DoubleActivePromoter::type liquidParameterImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* y, int comp) const + typename DoubleActivePromoter::type liquidParameterImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* y, int comp) const { return 0.0; } template - void analyticJacobianLiquidAddImpl(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, RowIterator jac) const { } + void analyticJacobianLiquidAddImpl(const ColumnPosition& colPos, double param, double const* y, int comp, + double factor, int offset, RowIterator jac) const + { + } template - typename DoubleActivePromoter::type combinedParameterLiquidImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* yLiquid, StateType const* ySolid, int comp) const + typename DoubleActivePromoter::type combinedParameterLiquidImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* yLiquid, + StateType const* ySolid, + int comp) const { return 0.0; } template - void analyticJacobianCombinedAddLiquidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, RowIterator jac) const { } + void analyticJacobianCombinedAddLiquidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int comp, double factor, int offset, + RowIterator jac) const + { + } template - typename DoubleActivePromoter::type combinedParameterSolidImpl(const ColumnPosition& colPos, const ParamType& param, StateType const* yLiquid, StateType const* ySolid, int bnd) const + typename DoubleActivePromoter::type combinedParameterSolidImpl(const ColumnPosition& colPos, + const ParamType& param, + StateType const* yLiquid, + StateType const* ySolid, + int bnd) const { - const typename DoubleActivePromoter::type logKeq = static_cast(_lnKeqFactor[bnd]) * pow(yLiquid[0], static_cast(_lnKeqExp[bnd])) + static_cast(_lnKeqConst[bnd]); - return param * (static_cast(_powFactor[bnd]) * pow(logKeq, static_cast(_powExponent[bnd])) + static_cast(_expFactor[bnd]) * exp(logKeq * static_cast(_expExponent[bnd]))); + const typename DoubleActivePromoter::type logKeq = + static_cast(_lnKeqFactor[bnd]) * pow(yLiquid[0], static_cast(_lnKeqExp[bnd])) + + static_cast(_lnKeqConst[bnd]); + return param * + (static_cast(_powFactor[bnd]) * pow(logKeq, static_cast(_powExponent[bnd])) + + static_cast(_expFactor[bnd]) * exp(logKeq * static_cast(_expExponent[bnd]))); } template - void analyticJacobianCombinedAddSolidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, RowIterator jac) const + void analyticJacobianCombinedAddSolidImpl(const ColumnPosition& colPos, double param, double const* yLiquid, + double const* ySolid, int bnd, double factor, int offset, + RowIterator jac) const { - const double logKeq = static_cast(_lnKeqFactor[bnd]) * pow(yLiquid[0], static_cast(_lnKeqExp[bnd])) + static_cast(_lnKeqConst[bnd]); - const double dLogKeq_dy = static_cast(_lnKeqFactor[bnd]) * pow(yLiquid[0], static_cast(_lnKeqExp[bnd]) - 1.0) * static_cast(_lnKeqExp[bnd]); - const double dPow_dy = static_cast(_powFactor[bnd]) * pow(logKeq, static_cast(_powExponent[bnd]) - 1.0) * static_cast(_powExponent[bnd]); - const double dExp_dy = static_cast(_expFactor[bnd]) * exp(logKeq * static_cast(_expExponent[bnd])) * static_cast(_expExponent[bnd]); + const double logKeq = + static_cast(_lnKeqFactor[bnd]) * pow(yLiquid[0], static_cast(_lnKeqExp[bnd])) + + static_cast(_lnKeqConst[bnd]); + const double dLogKeq_dy = static_cast(_lnKeqFactor[bnd]) * + pow(yLiquid[0], static_cast(_lnKeqExp[bnd]) - 1.0) * + static_cast(_lnKeqExp[bnd]); + const double dPow_dy = static_cast(_powFactor[bnd]) * + pow(logKeq, static_cast(_powExponent[bnd]) - 1.0) * + static_cast(_powExponent[bnd]); + const double dExp_dy = static_cast(_expFactor[bnd]) * + exp(logKeq * static_cast(_expExponent[bnd])) * + static_cast(_expExponent[bnd]); jac[offset - bnd - _nComp] += factor * param * (dPow_dy + dExp_dy) * dLogKeq_dy; } }; - namespace paramdep { - void registerLiquidSaltSolidParamDependence(std::unordered_map>& paramDeps) - { - paramDeps[ExpLiquidSaltSolidParameterStateDependence::identifier()] = []() { return new ExpLiquidSaltSolidParameterStateDependence(); }; - paramDeps[PowerLiquidSaltSolidParameterStateDependence::identifier()] = []() { return new PowerLiquidSaltSolidParameterStateDependence(); }; - paramDeps[ColloidalAffinityLiquidSaltSolidParameterStateDependence::identifier()] = []() { return new ColloidalAffinityLiquidSaltSolidParameterStateDependence(); }; - } -} // namespace paramdep +void registerLiquidSaltSolidParamDependence( + std::unordered_map>& paramDeps) +{ + paramDeps[ExpLiquidSaltSolidParameterStateDependence::identifier()] = []() { + return new ExpLiquidSaltSolidParameterStateDependence(); + }; + paramDeps[PowerLiquidSaltSolidParameterStateDependence::identifier()] = []() { + return new PowerLiquidSaltSolidParameterStateDependence(); + }; + paramDeps[ColloidalAffinityLiquidSaltSolidParameterStateDependence::identifier()] = []() { + return new ColloidalAffinityLiquidSaltSolidParameterStateDependence(); + }; +} +} // namespace paramdep -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/paramdep/ParameterDependenceBase.cpp b/src/libcadet/model/paramdep/ParameterDependenceBase.cpp index e41b0004a..1e420c99d 100644 --- a/src/libcadet/model/paramdep/ParameterDependenceBase.cpp +++ b/src/libcadet/model/paramdep/ParameterDependenceBase.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -26,12 +26,16 @@ namespace cadet namespace model { -ParameterStateDependenceBase::ParameterStateDependenceBase() : _nComp(0), _nBoundStates(nullptr) { } +ParameterStateDependenceBase::ParameterStateDependenceBase() : _nComp(0), _nBoundStates(nullptr) +{ +} ParameterStateDependenceBase::~ParameterStateDependenceBase() CADET_NOEXCEPT { } -bool ParameterStateDependenceBase::configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) +bool ParameterStateDependenceBase::configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, + unsigned int const* boundOffset) { _nComp = nComp; _nBoundStates = nBound; @@ -45,7 +49,8 @@ bool ParameterStateDependenceBase::configureModelDiscretization(IParameterProvid return true; } -bool ParameterStateDependenceBase::configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, const std::string& name) +bool ParameterStateDependenceBase::configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx, const std::string& name) { // Clear all parameters and reconfigure _parameters.clear(); @@ -56,7 +61,9 @@ std::unordered_map ParameterStateDependenceBase::getAllPara { std::unordered_map data; std::transform(_parameters.begin(), _parameters.end(), std::inserter(data, data.end()), - [](const std::pair& p) { return std::make_pair(p.first, static_cast(*p.second)); }); + [](const std::pair& p) { + return std::make_pair(p.first, static_cast(*p.second)); + }); return data; } @@ -98,8 +105,9 @@ active* ParameterStateDependenceBase::getParameter(const ParameterId& pId) return nullptr; } - -ParameterParameterDependenceBase::ParameterParameterDependenceBase() { } +ParameterParameterDependenceBase::ParameterParameterDependenceBase() +{ +} ParameterParameterDependenceBase::~ParameterParameterDependenceBase() CADET_NOEXCEPT { } @@ -109,7 +117,9 @@ bool ParameterParameterDependenceBase::configureModelDiscretization(IParameterPr return true; } -bool ParameterParameterDependenceBase::configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, BoundStateIdx bndIdx, const std::string& name) +bool ParameterParameterDependenceBase::configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx, BoundStateIdx bndIdx, + const std::string& name) { // Clear all parameters and reconfigure _parameters.clear(); @@ -120,7 +130,9 @@ std::unordered_map ParameterParameterDependenceBase::getAll { std::unordered_map data; std::transform(_parameters.begin(), _parameters.end(), std::inserter(data, data.end()), - [](const std::pair& p) { return std::make_pair(p.first, static_cast(*p.second)); }); + [](const std::pair& p) { + return std::make_pair(p.first, static_cast(*p.second)); + }); return data; } @@ -162,6 +174,6 @@ active* ParameterParameterDependenceBase::getParameter(const ParameterId& pId) return nullptr; } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/paramdep/ParameterDependenceBase.hpp b/src/libcadet/model/paramdep/ParameterDependenceBase.hpp index 7d4113e31..a8e6a525c 100644 --- a/src/libcadet/model/paramdep/ParameterDependenceBase.hpp +++ b/src/libcadet/model/paramdep/ParameterDependenceBase.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines an IParameterStateDependence base class. */ @@ -37,15 +37,22 @@ namespace model class ParameterStateDependenceBase : public IParameterStateDependence { public: - ParameterStateDependenceBase(); virtual ~ParameterStateDependenceBase() CADET_NOEXCEPT; - virtual bool requiresConfiguration() const CADET_NOEXCEPT { return true; } - virtual bool usesParamProviderInDiscretizationConfig() const CADET_NOEXCEPT { return true; } - virtual bool configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, const std::string& name); - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset); + virtual bool requiresConfiguration() const CADET_NOEXCEPT + { + return true; + } + virtual bool usesParamProviderInDiscretizationConfig() const CADET_NOEXCEPT + { + return true; + } + virtual bool configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + const std::string& name); + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset); virtual std::unordered_map getAllParameterValues() const; virtual bool hasParameter(const ParameterId& pId) const; @@ -57,9 +64,9 @@ class ParameterStateDependenceBase : public IParameterStateDependence virtual active* getParameter(const ParameterId& pId); protected: - int _nComp; //!< Number of components + int _nComp; //!< Number of components unsigned int const* _nBoundStates; //!< Array with number of bound states for each component - unsigned int const* _boundOffset; //!< Array with offsets to the first bound state of each component + unsigned int const* _boundOffset; //!< Array with offsets to the first bound state of each component unsigned int _nTotalBoundStates; std::unordered_map _parameters; //!< Map used to translate ParameterIds to actual variables @@ -75,10 +82,10 @@ class ParameterStateDependenceBase : public IParameterStateDependence * @param [in] name Name of the parameter * @return @c true if the configuration was successful, otherwise @c false */ - virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, const std::string& name) = 0; + virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + const std::string& name) = 0; }; - /** * @brief Inserts implementations of all parameter() and analyticJacobian() method variants * @details An IParameterStateDependence implementation has to provide liquidParameter(), combinedParameterLiquid(), @@ -88,102 +95,118 @@ class ParameterStateDependenceBase : public IParameterStateDependence * provides templatized liquidParameterImpl(), combinedParameterLiquidImpl(), * combinedParameterSolidImpl(), analyticJacobianLiquidAddImpl(), analyticJacobianCombinedAddLiquidImpl(), * and analyticJacobianCombinedAddSolidImpl() functions that realize all required variants. - * + * * The implementation is inserted inline in the class declaration. */ -#define CADET_PARAMETERSTATEDEPENDENCE_BOILERPLATE \ - virtual active liquidParameter(const ColumnPosition& colPos, const active& param, active const* y, int comp) const \ - { \ - return liquidParameterImpl(colPos, param, y, comp); \ - } \ - \ - virtual active liquidParameter(const ColumnPosition& colPos, const active& param, double const* y, int comp) const \ - { \ - return liquidParameterImpl(colPos, param, y, comp); \ - } \ - \ - virtual active liquidParameter(const ColumnPosition& colPos, double param, active const* y, int comp) const \ - { \ - return liquidParameterImpl(colPos, param, y, comp); \ - } \ - \ - virtual double liquidParameter(const ColumnPosition& colPos, double param, double const* y, int comp) const \ - { \ - return liquidParameterImpl(colPos, param, y, comp); \ - } \ - \ - virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, linalg::BandMatrix::RowIterator jac) const \ - { \ - analyticJacobianLiquidAddImpl(colPos, param, y, comp, factor, offset, jac); \ - } \ - \ - virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, double factor, int offset, linalg::DenseBandedRowIterator jac) const \ - { \ - analyticJacobianLiquidAddImpl(colPos, param, y, comp, factor, offset, jac); \ - } \ - \ - virtual active combinedParameterLiquid(const ColumnPosition& colPos, const active& param, active const* yLiquid, active const* ySolid, int comp) const \ - { \ - return combinedParameterLiquidImpl(colPos, param, yLiquid, ySolid, comp); \ - } \ - \ - virtual active combinedParameterLiquid(const ColumnPosition& colPos, const active& param, double const* yLiquid, double const* ySolid, int comp) const \ - { \ - return combinedParameterLiquidImpl(colPos, param, yLiquid, ySolid, comp); \ - } \ - \ - virtual active combinedParameterLiquid(const ColumnPosition& colPos, double param, active const* yLiquid, active const* ySolid, int comp) const \ - { \ - return combinedParameterLiquidImpl(colPos, param, yLiquid, ySolid, comp); \ - } \ - \ - virtual double combinedParameterLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp) const \ - { \ - return combinedParameterLiquidImpl(colPos, param, yLiquid, ySolid, comp); \ - } \ - \ - virtual active combinedParameterSolid(const ColumnPosition& colPos, const active& param, active const* yLiquid, active const* ySolid, int bnd) const \ - { \ - return combinedParameterSolidImpl(colPos, param, yLiquid, ySolid, bnd); \ - } \ - \ - virtual active combinedParameterSolid(const ColumnPosition& colPos, const active& param, double const* yLiquid, double const* ySolid, int bnd) const \ - { \ - return combinedParameterSolidImpl(colPos, param, yLiquid, ySolid, bnd); \ - } \ - \ - virtual active combinedParameterSolid(const ColumnPosition& colPos, double param, active const* yLiquid, active const* ySolid, int bnd) const \ - { \ - return combinedParameterSolidImpl(colPos, param, yLiquid, ySolid, bnd); \ - } \ - \ - virtual double combinedParameterSolid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd) const \ - { \ - return combinedParameterSolidImpl(colPos, param, yLiquid, ySolid, bnd); \ - } \ - \ - virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, linalg::BandMatrix::RowIterator jac) const \ - { \ - analyticJacobianCombinedAddLiquidImpl(colPos, param, yLiquid, ySolid, comp, factor, offset, jac); \ - } \ - \ - virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int comp, double factor, int offset, linalg::DenseBandedRowIterator jac) const \ - { \ - analyticJacobianCombinedAddLiquidImpl(colPos, param, yLiquid, ySolid, comp, factor, offset, jac); \ - } \ - \ - virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, linalg::BandMatrix::RowIterator jac) const \ - { \ - analyticJacobianCombinedAddSolidImpl(colPos, param, yLiquid, ySolid, bnd, factor, offset, jac); \ - } \ - \ - virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, double const* ySolid, int bnd, double factor, int offset, linalg::DenseBandedRowIterator jac) const \ - { \ - analyticJacobianCombinedAddSolidImpl(colPos, param, yLiquid, ySolid, bnd, factor, offset, jac); \ +#define CADET_PARAMETERSTATEDEPENDENCE_BOILERPLATE \ + virtual active liquidParameter(const ColumnPosition& colPos, const active& param, active const* y, int comp) const \ + { \ + return liquidParameterImpl(colPos, param, y, comp); \ + } \ + \ + virtual active liquidParameter(const ColumnPosition& colPos, const active& param, double const* y, int comp) const \ + { \ + return liquidParameterImpl(colPos, param, y, comp); \ + } \ + \ + virtual active liquidParameter(const ColumnPosition& colPos, double param, active const* y, int comp) const \ + { \ + return liquidParameterImpl(colPos, param, y, comp); \ + } \ + \ + virtual double liquidParameter(const ColumnPosition& colPos, double param, double const* y, int comp) const \ + { \ + return liquidParameterImpl(colPos, param, y, comp); \ + } \ + \ + virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, \ + double factor, int offset, linalg::BandMatrix::RowIterator jac) const \ + { \ + analyticJacobianLiquidAddImpl(colPos, param, y, comp, factor, offset, jac); \ + } \ + \ + virtual void analyticJacobianLiquidAdd(const ColumnPosition& colPos, double param, double const* y, int comp, \ + double factor, int offset, linalg::DenseBandedRowIterator jac) const \ + { \ + analyticJacobianLiquidAddImpl(colPos, param, y, comp, factor, offset, jac); \ + } \ + \ + virtual active combinedParameterLiquid(const ColumnPosition& colPos, const active& param, active const* yLiquid, \ + active const* ySolid, int comp) const \ + { \ + return combinedParameterLiquidImpl(colPos, param, yLiquid, ySolid, comp); \ + } \ + \ + virtual active combinedParameterLiquid(const ColumnPosition& colPos, const active& param, double const* yLiquid, \ + double const* ySolid, int comp) const \ + { \ + return combinedParameterLiquidImpl(colPos, param, yLiquid, ySolid, comp); \ + } \ + \ + virtual active combinedParameterLiquid(const ColumnPosition& colPos, double param, active const* yLiquid, \ + active const* ySolid, int comp) const \ + { \ + return combinedParameterLiquidImpl(colPos, param, yLiquid, ySolid, comp); \ + } \ + \ + virtual double combinedParameterLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, \ + double const* ySolid, int comp) const \ + { \ + return combinedParameterLiquidImpl(colPos, param, yLiquid, ySolid, comp); \ + } \ + \ + virtual active combinedParameterSolid(const ColumnPosition& colPos, const active& param, active const* yLiquid, \ + active const* ySolid, int bnd) const \ + { \ + return combinedParameterSolidImpl(colPos, param, yLiquid, ySolid, bnd); \ + } \ + \ + virtual active combinedParameterSolid(const ColumnPosition& colPos, const active& param, double const* yLiquid, \ + double const* ySolid, int bnd) const \ + { \ + return combinedParameterSolidImpl(colPos, param, yLiquid, ySolid, bnd); \ + } \ + \ + virtual active combinedParameterSolid(const ColumnPosition& colPos, double param, active const* yLiquid, \ + active const* ySolid, int bnd) const \ + { \ + return combinedParameterSolidImpl(colPos, param, yLiquid, ySolid, bnd); \ + } \ + \ + virtual double combinedParameterSolid(const ColumnPosition& colPos, double param, double const* yLiquid, \ + double const* ySolid, int bnd) const \ + { \ + return combinedParameterSolidImpl(colPos, param, yLiquid, ySolid, bnd); \ + } \ + \ + virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, \ + double const* ySolid, int comp, double factor, int offset, \ + linalg::BandMatrix::RowIterator jac) const \ + { \ + analyticJacobianCombinedAddLiquidImpl(colPos, param, yLiquid, ySolid, comp, factor, offset, jac); \ + } \ + \ + virtual void analyticJacobianCombinedAddLiquid(const ColumnPosition& colPos, double param, double const* yLiquid, \ + double const* ySolid, int comp, double factor, int offset, \ + linalg::DenseBandedRowIterator jac) const \ + { \ + analyticJacobianCombinedAddLiquidImpl(colPos, param, yLiquid, ySolid, comp, factor, offset, jac); \ + } \ + \ + virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, \ + double const* ySolid, int bnd, double factor, int offset, \ + linalg::BandMatrix::RowIterator jac) const \ + { \ + analyticJacobianCombinedAddSolidImpl(colPos, param, yLiquid, ySolid, bnd, factor, offset, jac); \ + } \ + \ + virtual void analyticJacobianCombinedAddSolid(const ColumnPosition& colPos, double param, double const* yLiquid, \ + double const* ySolid, int bnd, double factor, int offset, \ + linalg::DenseBandedRowIterator jac) const \ + { \ + analyticJacobianCombinedAddSolidImpl(colPos, param, yLiquid, ySolid, bnd, factor, offset, jac); \ } - - /** * @brief Defines a ParameterParameterDependence base class that can be used to implement other parameter dependences * @details This base class can be used as a starting point for new parameter dependences. @@ -192,14 +215,20 @@ class ParameterStateDependenceBase : public IParameterStateDependence class ParameterParameterDependenceBase : public IParameterParameterDependence { public: - ParameterParameterDependenceBase(); virtual ~ParameterParameterDependenceBase() CADET_NOEXCEPT; - virtual bool requiresConfiguration() const CADET_NOEXCEPT { return true; } - virtual bool usesParamProviderInDiscretizationConfig() const CADET_NOEXCEPT { return true; } - virtual bool configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, BoundStateIdx bndIdx, const std::string& name); + virtual bool requiresConfiguration() const CADET_NOEXCEPT + { + return true; + } + virtual bool usesParamProviderInDiscretizationConfig() const CADET_NOEXCEPT + { + return true; + } + virtual bool configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + BoundStateIdx bndIdx, const std::string& name); virtual bool configureModelDiscretization(IParameterProvider& paramProvider); virtual std::unordered_map getAllParameterValues() const; @@ -212,7 +241,6 @@ class ParameterParameterDependenceBase : public IParameterParameterDependence virtual active* getParameter(const ParameterId& pId); protected: - std::unordered_map _parameters; //!< Map used to translate ParameterIds to actual variables /** @@ -227,43 +255,44 @@ class ParameterParameterDependenceBase : public IParameterParameterDependence * @param [in] name Name of the parameter * @return @c true if the configuration was successful, otherwise @c false */ - virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, BoundStateIdx bndIdx, const std::string& name) = 0; + virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + BoundStateIdx bndIdx, const std::string& name) = 0; }; - - /** * @brief Inserts implementations of all getValue() method variants * @details An IParameterStateDependence implementation has to provide getValue(), and getValueActive() * methods for different variants of state and parameter type. * This macro saves some time by providing those implementations. It assumes that the implementation * provides templatized getValue() functions that realize all required variants. - * + * * The implementation is inserted inline in the class declaration. */ -#define CADET_PARAMETERPARAMETERDEPENDENCE_BOILERPLATE \ - virtual double getValue(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, double val) const \ - { \ - return getValueImpl(model, colPos, comp, parType, bnd, val); \ - } \ - \ - virtual active getValue(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, const active& val) const \ - { \ - return getValueImpl(model, colPos, comp, parType, bnd, val); \ - } \ - \ - virtual double getValue(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd) const \ - { \ - return getValueImpl(model, colPos, comp, parType, bnd); \ - } \ - \ - virtual active getValueActive(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd) const \ - { \ - return getValueImpl(model, colPos, comp, parType, bnd); \ +#define CADET_PARAMETERPARAMETERDEPENDENCE_BOILERPLATE \ + virtual double getValue(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, \ + double val) const \ + { \ + return getValueImpl(model, colPos, comp, parType, bnd, val); \ + } \ + \ + virtual active getValue(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, \ + const active& val) const \ + { \ + return getValueImpl(model, colPos, comp, parType, bnd, val); \ + } \ + \ + virtual double getValue(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd) const \ + { \ + return getValueImpl(model, colPos, comp, parType, bnd); \ + } \ + \ + virtual active getValueActive(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd) \ + const \ + { \ + return getValueImpl(model, colPos, comp, parType, bnd); \ } - } // namespace model } // namespace cadet -#endif // LIBCADET_PARAMETERDEPENDENCEBASE_HPP_ +#endif // LIBCADET_PARAMETERDEPENDENCEBASE_HPP_ diff --git a/src/libcadet/model/paramdep/PowerLawParameterDependence.cpp b/src/libcadet/model/paramdep/PowerLawParameterDependence.cpp index e25dcd9a3..18176bc72 100644 --- a/src/libcadet/model/paramdep/PowerLawParameterDependence.cpp +++ b/src/libcadet/model/paramdep/PowerLawParameterDependence.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -32,12 +32,21 @@ namespace model class PowerLawParameterParameterDependence : public ParameterParameterDependenceBase { public: + PowerLawParameterParameterDependence() + { + } + virtual ~PowerLawParameterParameterDependence() CADET_NOEXCEPT + { + } - PowerLawParameterParameterDependence() { } - virtual ~PowerLawParameterParameterDependence() CADET_NOEXCEPT { } - - static const char* identifier() { return "POWER_LAW"; } - virtual const char* name() const CADET_NOEXCEPT { return PowerLawParameterParameterDependence::identifier(); } + static const char* identifier() + { + return "POWER_LAW"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return PowerLawParameterParameterDependence::identifier(); + } CADET_PARAMETERPARAMETERDEPENDENCE_BOILERPLATE @@ -46,7 +55,8 @@ class PowerLawParameterParameterDependence : public ParameterParameterDependence active _exponent; bool _useAbs; - virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, BoundStateIdx bndIdx, const std::string& name) + virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, + BoundStateIdx bndIdx, const std::string& name) { const std::string baseName = name + "_BASE"; const std::string expName = name + "_EXPONENT"; @@ -64,8 +74,10 @@ class PowerLawParameterParameterDependence : public ParameterParameterDependence else _useAbs = true; - _parameters[makeParamId(hashStringRuntime(baseName), unitOpIdx, CompIndep, parTypeIdx, bndIdx, ReactionIndep, SectionIndep)] = &_base; - _parameters[makeParamId(hashStringRuntime(expName), unitOpIdx, CompIndep, parTypeIdx, bndIdx, ReactionIndep, SectionIndep)] = &_exponent; + _parameters[makeParamId(hashStringRuntime(baseName), unitOpIdx, CompIndep, parTypeIdx, bndIdx, ReactionIndep, + SectionIndep)] = &_base; + _parameters[makeParamId(hashStringRuntime(expName), unitOpIdx, CompIndep, parTypeIdx, bndIdx, ReactionIndep, + SectionIndep)] = &_exponent; return true; } @@ -77,28 +89,30 @@ class PowerLawParameterParameterDependence : public ParameterParameterDependence } template - ParamType getValueImpl(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, ParamType val) const + ParamType getValueImpl(const IModel& model, const ColumnPosition& colPos, int comp, int parType, int bnd, + ParamType val) const { - using std::pow; using std::abs; + using std::pow; if (_useAbs) return static_cast(_base) * pow(abs(val), static_cast(_exponent)); else return static_cast(_base) * pow(val, static_cast(_exponent)); } - }; - namespace paramdep { - void registerPowerLawParamDependence(std::unordered_map>& paramDeps) - { - paramDeps[PowerLawParameterParameterDependence::identifier()] = []() { return new PowerLawParameterParameterDependence(); }; - } -} // namespace paramdep +void registerPowerLawParamDependence( + std::unordered_map>& paramDeps) +{ + paramDeps[PowerLawParameterParameterDependence::identifier()] = []() { + return new PowerLawParameterParameterDependence(); + }; +} +} // namespace paramdep -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/parts/AxialConvectionDispersionKernel.cpp b/src/libcadet/model/parts/AxialConvectionDispersionKernel.cpp index 63382c8d9..34dd22890 100644 --- a/src/libcadet/model/parts/AxialConvectionDispersionKernel.cpp +++ b/src/libcadet/model/parts/AxialConvectionDispersionKernel.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Implements the kernel of the axial convection dispersion transport operator. */ @@ -34,16 +34,22 @@ namespace convdisp namespace impl { - class DummyStencil +class DummyStencil +{ +public: + DummyStencil() + { + } + inline double operator[](const int idx) const { - public: - DummyStencil() { } - inline double operator[](const int idx) const { return 0.0; } - }; + return 0.0; + } +}; } // namespace impl -void sparsityPatternAxial(linalg::SparsityPatternRowIterator itBegin, unsigned int nComp, unsigned int nCol, int strideCell, double u, Weno& weno) +void sparsityPatternAxial(linalg::SparsityPatternRowIterator itBegin, unsigned int nComp, unsigned int nCol, + int strideCell, double u, Weno& weno) { impl::DummyStencil stencil; @@ -87,7 +93,8 @@ void sparsityPatternAxial(linalg::SparsityPatternRowIterator itBegin, unsigned i { for (int i = 0; i < 2 * wenoOrder - 1; ++i) // Note that we have an offset of -1 here (compared to the right cell face below), since - // the reconstructed value depends on the previous stencil (which has now been moved by one cell) + // the reconstructed value depends on the previous stencil (which has now been moved by one + // cell) jac.centered((i - wenoOrder) * strideCell); } @@ -144,7 +151,8 @@ void sparsityPatternAxial(linalg::SparsityPatternRowIterator itBegin, unsigned i { for (int i = 0; i < 2 * wenoOrder - 1; ++i) // Note that we have an offset of +1 here (compared to the left cell face below), since - // the reconstructed value depends on the previous stencil (which has now been moved by one cell) + // the reconstructed value depends on the previous stencil (which has now been moved by one + // cell) jac.centered((wenoOrder - i) * strideCell); } diff --git a/src/libcadet/model/parts/AxialConvectionDispersionKernel.hpp b/src/libcadet/model/parts/AxialConvectionDispersionKernel.hpp index 8ce442e91..c3a3f8181 100644 --- a/src/libcadet/model/parts/AxialConvectionDispersionKernel.hpp +++ b/src/libcadet/model/parts/AxialConvectionDispersionKernel.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Implements the kernel of the axial convection dispersion transport operator. */ @@ -39,346 +39,371 @@ namespace parts namespace convdisp { -template -struct AxialFlowParameters +template struct AxialFlowParameters { T u; active const* d_ax; T h; - double* wenoDerivatives; //!< Holds derivatives of the WENO scheme - Weno* weno; //!< The WENO scheme implementation + double* wenoDerivatives; //!< Holds derivatives of the WENO scheme + Weno* weno; //!< The WENO scheme implementation ArrayPool* stencilMemory; //!< Provides memory for the stencil - double wenoEpsilon; //!< The @f$ \varepsilon @f$ of the WENO scheme (prevents division by zero) + double wenoEpsilon; //!< The @f$ \varepsilon @f$ of the WENO scheme (prevents division by zero) int strideCell; unsigned int nComp; unsigned int nCol; unsigned int offsetToInlet; //!< Offset to the first component of the inlet DOFs in the local state vector - unsigned int offsetToBulk; //!< Offset to the first component of the first bulk cell in the local state vector + unsigned int offsetToBulk; //!< Offset to the first component of the first bulk cell in the local state vector IParameterParameterDependence* parDep; const IModel& model; }; - namespace impl { - template - int residualForwardsAxialFlow(const SimulationTime& simTime, StateType const* y, double const* yDot, ResidualType* res, RowIteratorType jacBegin, const AxialFlowParameters& p) - { - const ParamType h2 = p.h * p.h; - - // The stencil caches parts of the state vector for better spatial coherence - typedef CachingStencil StencilType; - StencilType stencil(std::max(p.weno->stencilSize(), 3u), *p.stencilMemory, std::max(p.weno->order() - 1, 1)); +template +int residualForwardsAxialFlow(const SimulationTime& simTime, StateType const* y, double const* yDot, ResidualType* res, + RowIteratorType jacBegin, const AxialFlowParameters& p) +{ + const ParamType h2 = p.h * p.h; - // The RowIterator is always centered on the main diagonal. - // This means that jac[0] is the main diagonal, jac[-1] is the first lower diagonal, - // and jac[1] is the first upper diagonal. We can also access the rows from left to - // right beginning with the last lower diagonal moving towards the main diagonal and - // continuing to the last upper diagonal by using the native() method. - RowIteratorType jac; + // The stencil caches parts of the state vector for better spatial coherence + typedef CachingStencil StencilType; + StencilType stencil(std::max(p.weno->stencilSize(), 3u), *p.stencilMemory, std::max(p.weno->order() - 1, 1)); - ResidualType* const resBulk = wantRes ? res + p.offsetToBulk : nullptr; - StateType const* const yBulk = y + p.offsetToBulk; + // The RowIterator is always centered on the main diagonal. + // This means that jac[0] is the main diagonal, jac[-1] is the first lower diagonal, + // and jac[1] is the first upper diagonal. We can also access the rows from left to + // right beginning with the last lower diagonal moving towards the main diagonal and + // continuing to the last upper diagonal by using the native() method. + RowIteratorType jac; - for (unsigned int comp = 0; comp < p.nComp; ++comp) - { - if (wantJac) - jac = jacBegin + comp; + ResidualType* const resBulk = wantRes ? res + p.offsetToBulk : nullptr; + StateType const* const yBulk = y + p.offsetToBulk; - ResidualType* const resBulkComp = wantRes ? resBulk + comp : nullptr; - StateType const* const yBulkComp = yBulk + comp; + for (unsigned int comp = 0; comp < p.nComp; ++comp) + { + if (wantJac) + jac = jacBegin + comp; - // Add time derivative to each cell - if (yDot && wantRes) - { - double const* const yDotBulkComp = yDot + p.offsetToBulk + comp; - for (unsigned int col = 0; col < p.nCol; ++col) - resBulkComp[col * p.strideCell] = yDotBulkComp[col * p.strideCell]; - } - else if (wantRes) - { - for (unsigned int col = 0; col < p.nCol; ++col) - resBulkComp[col * p.strideCell] = 0.0; - } + ResidualType* const resBulkComp = wantRes ? resBulk + comp : nullptr; + StateType const* const yBulkComp = yBulk + comp; - // Fill stencil (left side with zeros, right side with states) - for (int i = -std::max(p.weno->order(), 2) + 1; i < 0; ++i) - stencil[i] = 0.0; - for (int i = 0; i < std::max(p.weno->order(), 2); ++i) - stencil[i] = yBulkComp[i * p.strideCell]; + // Add time derivative to each cell + if (yDot && wantRes) + { + double const* const yDotBulkComp = yDot + p.offsetToBulk + comp; + for (unsigned int col = 0; col < p.nCol; ++col) + resBulkComp[col * p.strideCell] = yDotBulkComp[col * p.strideCell]; + } + else if (wantRes) + { + for (unsigned int col = 0; col < p.nCol; ++col) + resBulkComp[col * p.strideCell] = 0.0; + } - // Reset WENO output - StateType vm(0.0); // reconstructed value - if (wantJac) - std::fill(p.wenoDerivatives, p.wenoDerivatives + p.weno->stencilSize(), 0.0); + // Fill stencil (left side with zeros, right side with states) + for (int i = -std::max(p.weno->order(), 2) + 1; i < 0; ++i) + stencil[i] = 0.0; + for (int i = 0; i < std::max(p.weno->order(), 2); ++i) + stencil[i] = yBulkComp[i * p.strideCell]; - int wenoOrder = 0; - const ParamType d_ax = static_cast(p.d_ax[comp]); + // Reset WENO output + StateType vm(0.0); // reconstructed value + if (wantJac) + std::fill(p.wenoDerivatives, p.wenoDerivatives + p.weno->stencilSize(), 0.0); - // Iterate over all cells - for (unsigned int col = 0; col < p.nCol; ++col) - { - // ------------------- Dispersion ------------------- + int wenoOrder = 0; + const ParamType d_ax = static_cast(p.d_ax[comp]); - // Right side, leave out if we're in the last cell (boundary condition) - if (cadet_likely(col < p.nCol - 1)) - { - const double relCoord = static_cast(col + 1) / p.nCol; - const ParamType d_ax_right = d_ax * p.parDep->getValue(p.model, ColumnPosition{ relCoord, 0.0, 0.0 }, comp, ParTypeIndep, BoundStateIndep, static_cast(p.u)); - if (wantRes) - resBulkComp[col * p.strideCell] -= d_ax_right / h2 * (stencil[1] - stencil[0]); - // Jacobian entries - if (wantJac) - { - jac[0] += static_cast(d_ax_right) / static_cast(h2); - jac[p.strideCell] -= static_cast(d_ax_right) / static_cast(h2); - } - } + // Iterate over all cells + for (unsigned int col = 0; col < p.nCol; ++col) + { + // ------------------- Dispersion ------------------- - // Left side, leave out if we're in the first cell (boundary condition) - if (cadet_likely(col > 0)) + // Right side, leave out if we're in the last cell (boundary condition) + if (cadet_likely(col < p.nCol - 1)) + { + const double relCoord = static_cast(col + 1) / p.nCol; + const ParamType d_ax_right = + d_ax * p.parDep->getValue(p.model, ColumnPosition{relCoord, 0.0, 0.0}, comp, ParTypeIndep, + BoundStateIndep, static_cast(p.u)); + if (wantRes) + resBulkComp[col * p.strideCell] -= d_ax_right / h2 * (stencil[1] - stencil[0]); + // Jacobian entries + if (wantJac) { - const double relCoord = static_cast(col) / p.nCol; - const ParamType d_ax_left = d_ax * p.parDep->getValue(p.model, ColumnPosition{ relCoord, 0.0, 0.0 }, comp, ParTypeIndep, BoundStateIndep, static_cast(p.u)); - if (wantRes) - resBulkComp[col * p.strideCell] -= d_ax_left / h2 * (stencil[-1] - stencil[0]); - // Jacobian entries - if (wantJac) - { - jac[0] += static_cast(d_ax_left) / static_cast(h2); - jac[-p.strideCell] -= static_cast(d_ax_left) / static_cast(h2); - } + jac[0] += static_cast(d_ax_right) / static_cast(h2); + jac[p.strideCell] -= static_cast(d_ax_right) / static_cast(h2); } + } - // ------------------- Convection ------------------- - - // Add convection through this cell's left face - if (cadet_likely(col > 0)) - { - // Remember that vm still contains the reconstructed value of the previous - // cell's *right* face, which is identical to this cell's *left* face! - if (wantRes) - resBulkComp[col * p.strideCell] -= p.u / p.h * vm; - - // Jacobian entries - if (wantJac) - { - for (int i = 0; i < 2 * wenoOrder - 1; ++i) - // Note that we have an offset of -1 here (compared to the right cell face below), since - // the reconstructed value depends on the previous stencil (which has now been moved by one cell) - jac[(i - wenoOrder) * p.strideCell] -= static_cast(p.u) / static_cast(p.h) * p.wenoDerivatives[i]; - } - } - else if (wantRes) + // Left side, leave out if we're in the first cell (boundary condition) + if (cadet_likely(col > 0)) + { + const double relCoord = static_cast(col) / p.nCol; + const ParamType d_ax_left = + d_ax * p.parDep->getValue(p.model, ColumnPosition{relCoord, 0.0, 0.0}, comp, ParTypeIndep, + BoundStateIndep, static_cast(p.u)); + if (wantRes) + resBulkComp[col * p.strideCell] -= d_ax_left / h2 * (stencil[-1] - stencil[0]); + // Jacobian entries + if (wantJac) { - // In the first cell we need to apply the boundary condition: inflow concentration - resBulkComp[col * p.strideCell] -= p.u / p.h * y[p.offsetToInlet + comp]; + jac[0] += static_cast(d_ax_left) / static_cast(h2); + jac[-p.strideCell] -= static_cast(d_ax_left) / static_cast(h2); } + } - // Reconstruct concentration on this cell's right face - if (wantJac) - wenoOrder = p.weno->template reconstruct(p.wenoEpsilon, col, p.nCol, stencil, vm, p.wenoDerivatives); - else - wenoOrder = p.weno->template reconstruct(p.wenoEpsilon, col, p.nCol, stencil, vm); + // ------------------- Convection ------------------- - // Right side + // Add convection through this cell's left face + if (cadet_likely(col > 0)) + { + // Remember that vm still contains the reconstructed value of the previous + // cell's *right* face, which is identical to this cell's *left* face! if (wantRes) - resBulkComp[col * p.strideCell] += p.u / p.h * vm; + resBulkComp[col * p.strideCell] -= p.u / p.h * vm; + // Jacobian entries if (wantJac) { for (int i = 0; i < 2 * wenoOrder - 1; ++i) - jac[(i - wenoOrder + 1) * p.strideCell] += static_cast(p.u) / static_cast(p.h) * p.wenoDerivatives[i]; + // Note that we have an offset of -1 here (compared to the right cell face below), since + // the reconstructed value depends on the previous stencil (which has now been moved by one + // cell) + jac[(i - wenoOrder) * p.strideCell] -= + static_cast(p.u) / static_cast(p.h) * p.wenoDerivatives[i]; } + } + else if (wantRes) + { + // In the first cell we need to apply the boundary condition: inflow concentration + resBulkComp[col * p.strideCell] -= p.u / p.h * y[p.offsetToInlet + comp]; + } - // Update stencil - const unsigned int shift = std::max(p.weno->order(), 2); - if (cadet_likely(col + shift < p.nCol)) - stencil.advance(yBulkComp[(col + shift) * p.strideCell]); - else - stencil.advance(0.0); - - if (wantJac) - { - if (cadet_likely(col < p.nCol - 1)) - jac += p.strideCell; - } + // Reconstruct concentration on this cell's right face + if (wantJac) + wenoOrder = p.weno->template reconstruct(p.wenoEpsilon, col, p.nCol, stencil, + vm, p.wenoDerivatives); + else + wenoOrder = + p.weno->template reconstruct(p.wenoEpsilon, col, p.nCol, stencil, vm); + + // Right side + if (wantRes) + resBulkComp[col * p.strideCell] += p.u / p.h * vm; + // Jacobian entries + if (wantJac) + { + for (int i = 0; i < 2 * wenoOrder - 1; ++i) + jac[(i - wenoOrder + 1) * p.strideCell] += + static_cast(p.u) / static_cast(p.h) * p.wenoDerivatives[i]; } - } - // Film diffusion with flux into beads is added in residualFlux() function + // Update stencil + const unsigned int shift = std::max(p.weno->order(), 2); + if (cadet_likely(col + shift < p.nCol)) + stencil.advance(yBulkComp[(col + shift) * p.strideCell]); + else + stencil.advance(0.0); - return 0; + if (wantJac) + { + if (cadet_likely(col < p.nCol - 1)) + jac += p.strideCell; + } + } } - template - int residualBackwardsAxialFlow(const SimulationTime& simTime, StateType const* y, double const* yDot, ResidualType* res, RowIteratorType jacBegin, const AxialFlowParameters& p) - { - const ParamType h2 = p.h * p.h; + // Film diffusion with flux into beads is added in residualFlux() function - // The stencil caches parts of the state vector for better spatial coherence - typedef CachingStencil StencilType; - StencilType stencil(std::max(p.weno->stencilSize(), 3u), *p.stencilMemory, std::max(p.weno->order() - 1, 1)); - - // The RowIterator is always centered on the main diagonal. - // This means that jac[0] is the main diagonal, jac[-1] is the first lower diagonal, - // and jac[1] is the first upper diagonal. We can also access the rows from left to - // right beginning with the last lower diagonal moving towards the main diagonal and - // continuing to the last upper diagonal by using the native() method. - RowIteratorType jac; + return 0; +} - ResidualType* const resBulk = wantRes ? res + p.offsetToBulk : nullptr; - StateType const* const yBulk = y + p.offsetToBulk; +template +int residualBackwardsAxialFlow(const SimulationTime& simTime, StateType const* y, double const* yDot, ResidualType* res, + RowIteratorType jacBegin, const AxialFlowParameters& p) +{ + const ParamType h2 = p.h * p.h; - for (unsigned int comp = 0; comp < p.nComp; ++comp) - { - if (wantJac) - jac = jacBegin + p.strideCell * (p.nCol - 1) + comp; + // The stencil caches parts of the state vector for better spatial coherence + typedef CachingStencil StencilType; + StencilType stencil(std::max(p.weno->stencilSize(), 3u), *p.stencilMemory, std::max(p.weno->order() - 1, 1)); - ResidualType* const resBulkComp = wantRes ? resBulk + comp : nullptr; - StateType const* const yBulkComp = yBulk + comp; + // The RowIterator is always centered on the main diagonal. + // This means that jac[0] is the main diagonal, jac[-1] is the first lower diagonal, + // and jac[1] is the first upper diagonal. We can also access the rows from left to + // right beginning with the last lower diagonal moving towards the main diagonal and + // continuing to the last upper diagonal by using the native() method. + RowIteratorType jac; - // Add time derivative to each cell - if (yDot && wantRes) - { - double const* const yDotBulkComp = yDot + p.offsetToBulk + comp; - for (unsigned int col = 0; col < p.nCol; ++col) - resBulkComp[col * p.strideCell] = yDotBulkComp[col * p.strideCell]; - } - else if (wantRes) - { - for (unsigned int col = 0; col < p.nCol; ++col) - resBulkComp[col * p.strideCell] = 0.0; - } + ResidualType* const resBulk = wantRes ? res + p.offsetToBulk : nullptr; + StateType const* const yBulk = y + p.offsetToBulk; - // Fill stencil (left side with zeros, right side with states) - for (int i = -std::max(p.weno->order(), 2) + 1; i < 0; ++i) - stencil[i] = 0.0; - for (int i = 0; i < std::max(p.weno->order(), 2); ++i) - stencil[i] = yBulkComp[(p.nCol - static_cast(i) - 1) * p.strideCell]; + for (unsigned int comp = 0; comp < p.nComp; ++comp) + { + if (wantJac) + jac = jacBegin + p.strideCell * (p.nCol - 1) + comp; - // Reset WENO output - StateType vm(0.0); // reconstructed value - if (wantJac) - std::fill(p.wenoDerivatives, p.wenoDerivatives + p.weno->stencilSize(), 0.0); + ResidualType* const resBulkComp = wantRes ? resBulk + comp : nullptr; + StateType const* const yBulkComp = yBulk + comp; - int wenoOrder = 0; - const ParamType d_ax = static_cast(p.d_ax[comp]); + // Add time derivative to each cell + if (yDot && wantRes) + { + double const* const yDotBulkComp = yDot + p.offsetToBulk + comp; + for (unsigned int col = 0; col < p.nCol; ++col) + resBulkComp[col * p.strideCell] = yDotBulkComp[col * p.strideCell]; + } + else if (wantRes) + { + for (unsigned int col = 0; col < p.nCol; ++col) + resBulkComp[col * p.strideCell] = 0.0; + } - // Iterate over all cells (backwards) - // Note that col wraps around to unsigned int's maximum value after 0 - for (unsigned int col = p.nCol - 1; col < p.nCol; --col) - { - // ------------------- Dispersion ------------------- + // Fill stencil (left side with zeros, right side with states) + for (int i = -std::max(p.weno->order(), 2) + 1; i < 0; ++i) + stencil[i] = 0.0; + for (int i = 0; i < std::max(p.weno->order(), 2); ++i) + stencil[i] = yBulkComp[(p.nCol - static_cast(i) - 1) * p.strideCell]; - // Right side, leave out if we're in the first cell (boundary condition) - if (cadet_likely(col < p.nCol - 1)) - { - const double relCoord = static_cast(col + 1) / p.nCol; - const ParamType d_ax_right = d_ax * p.parDep->getValue(p.model, ColumnPosition{ relCoord, 0.0, 0.0 }, comp, ParTypeIndep, BoundStateIndep, static_cast(p.u)); - if (wantRes) - resBulkComp[col * p.strideCell] -= d_ax_right / h2 * (stencil[-1] - stencil[0]); - // Jacobian entries - if (wantJac) - { - jac[0] += static_cast(d_ax_right) / static_cast(h2); - jac[p.strideCell] -= static_cast(d_ax_right) / static_cast(h2); - } - } + // Reset WENO output + StateType vm(0.0); // reconstructed value + if (wantJac) + std::fill(p.wenoDerivatives, p.wenoDerivatives + p.weno->stencilSize(), 0.0); - // Left side, leave out if we're in the last cell (boundary condition) - if (cadet_likely(col > 0)) - { - const double relCoord = static_cast(col) / p.nCol; - const ParamType d_ax_left = d_ax * p.parDep->getValue(p.model, ColumnPosition{ relCoord, 0.0, 0.0 }, comp, ParTypeIndep, BoundStateIndep, static_cast(p.u)); - if (wantRes) - resBulkComp[col * p.strideCell] -= d_ax_left / h2 * (stencil[1] - stencil[0]); - // Jacobian entries - if (wantJac) - { - jac[0] += static_cast(d_ax_left) / static_cast(h2); - jac[-p.strideCell] -= static_cast(d_ax_left) / static_cast(h2); - } - } + int wenoOrder = 0; + const ParamType d_ax = static_cast(p.d_ax[comp]); - // ------------------- Convection ------------------- + // Iterate over all cells (backwards) + // Note that col wraps around to unsigned int's maximum value after 0 + for (unsigned int col = p.nCol - 1; col < p.nCol; --col) + { + // ------------------- Dispersion ------------------- - // Add convection through this cell's right face - if (cadet_likely(col < p.nCol - 1)) + // Right side, leave out if we're in the first cell (boundary condition) + if (cadet_likely(col < p.nCol - 1)) + { + const double relCoord = static_cast(col + 1) / p.nCol; + const ParamType d_ax_right = + d_ax * p.parDep->getValue(p.model, ColumnPosition{relCoord, 0.0, 0.0}, comp, ParTypeIndep, + BoundStateIndep, static_cast(p.u)); + if (wantRes) + resBulkComp[col * p.strideCell] -= d_ax_right / h2 * (stencil[-1] - stencil[0]); + // Jacobian entries + if (wantJac) { - // Remember that vm still contains the reconstructed value of the previous - // cell's *left* face, which is identical to this cell's *right* face! - if (wantRes) - resBulkComp[col * p.strideCell] += p.u / p.h * vm; - - // Jacobian entries - if (wantJac) - { - for (int i = 0; i < 2 * wenoOrder - 1; ++i) - // Note that we have an offset of +1 here (compared to the left cell face below), since - // the reconstructed value depends on the previous stencil (which has now been moved by one cell) - jac[(wenoOrder - i) * p.strideCell] += static_cast(p.u) / static_cast(p.h) * p.wenoDerivatives[i]; - } + jac[0] += static_cast(d_ax_right) / static_cast(h2); + jac[p.strideCell] -= static_cast(d_ax_right) / static_cast(h2); } - else if (wantRes) + } + + // Left side, leave out if we're in the last cell (boundary condition) + if (cadet_likely(col > 0)) + { + const double relCoord = static_cast(col) / p.nCol; + const ParamType d_ax_left = + d_ax * p.parDep->getValue(p.model, ColumnPosition{relCoord, 0.0, 0.0}, comp, ParTypeIndep, + BoundStateIndep, static_cast(p.u)); + if (wantRes) + resBulkComp[col * p.strideCell] -= d_ax_left / h2 * (stencil[1] - stencil[0]); + // Jacobian entries + if (wantJac) { - // In the last cell (z = L) we need to apply the boundary condition: inflow concentration - resBulkComp[col * p.strideCell] += p.u / p.h * y[p.offsetToInlet + comp]; + jac[0] += static_cast(d_ax_left) / static_cast(h2); + jac[-p.strideCell] -= static_cast(d_ax_left) / static_cast(h2); } + } - // Reconstruct concentration on this cell's left face - if (wantJac) - wenoOrder = p.weno->template reconstruct(p.wenoEpsilon, col, p.nCol, stencil, vm, p.wenoDerivatives); - else - wenoOrder = p.weno->template reconstruct(p.wenoEpsilon, col, p.nCol, stencil, vm); + // ------------------- Convection ------------------- - // Left face + // Add convection through this cell's right face + if (cadet_likely(col < p.nCol - 1)) + { + // Remember that vm still contains the reconstructed value of the previous + // cell's *left* face, which is identical to this cell's *right* face! if (wantRes) - resBulkComp[col * p.strideCell] -= p.u / p.h * vm; + resBulkComp[col * p.strideCell] += p.u / p.h * vm; + // Jacobian entries if (wantJac) { for (int i = 0; i < 2 * wenoOrder - 1; ++i) - jac[(wenoOrder - i - 1) * p.strideCell] -= static_cast(p.u) / static_cast(p.h) * p.wenoDerivatives[i]; + // Note that we have an offset of +1 here (compared to the left cell face below), since + // the reconstructed value depends on the previous stencil (which has now been moved by one + // cell) + jac[(wenoOrder - i) * p.strideCell] += + static_cast(p.u) / static_cast(p.h) * p.wenoDerivatives[i]; } + } + else if (wantRes) + { + // In the last cell (z = L) we need to apply the boundary condition: inflow concentration + resBulkComp[col * p.strideCell] += p.u / p.h * y[p.offsetToInlet + comp]; + } - // Update stencil (be careful because of wrap-around, might cause reading memory very far away [although never used]) - const unsigned int shift = std::max(p.weno->order(), 2); - if (cadet_likely(col - shift < p.nCol)) - stencil.advance(yBulkComp[(col - shift) * p.strideCell]); - else - stencil.advance(0.0); + // Reconstruct concentration on this cell's left face + if (wantJac) + wenoOrder = p.weno->template reconstruct(p.wenoEpsilon, col, p.nCol, stencil, + vm, p.wenoDerivatives); + else + wenoOrder = + p.weno->template reconstruct(p.wenoEpsilon, col, p.nCol, stencil, vm); + + // Left face + if (wantRes) + resBulkComp[col * p.strideCell] -= p.u / p.h * vm; + // Jacobian entries + if (wantJac) + { + for (int i = 0; i < 2 * wenoOrder - 1; ++i) + jac[(wenoOrder - i - 1) * p.strideCell] -= + static_cast(p.u) / static_cast(p.h) * p.wenoDerivatives[i]; + } - if (wantJac) - { - if (cadet_likely(col > 0)) - jac -= p.strideCell; - } + // Update stencil (be careful because of wrap-around, might cause reading memory very far away [although + // never used]) + const unsigned int shift = std::max(p.weno->order(), 2); + if (cadet_likely(col - shift < p.nCol)) + stencil.advance(yBulkComp[(col - shift) * p.strideCell]); + else + stencil.advance(0.0); + + if (wantJac) + { + if (cadet_likely(col > 0)) + jac -= p.strideCell; } } + } - // Film diffusion with flux into beads is added in residualFlux() function + // Film diffusion with flux into beads is added in residualFlux() function - return 0; - } + return 0; +} } // namespace impl - -template -int residualKernelAxial(const SimulationTime& simTime, StateType const* y, double const* yDot, ResidualType* res, RowIteratorType jacBegin, const AxialFlowParameters& p) +template +int residualKernelAxial(const SimulationTime& simTime, StateType const* y, double const* yDot, ResidualType* res, + RowIteratorType jacBegin, const AxialFlowParameters& p) { if (p.u >= 0.0) - return impl::residualForwardsAxialFlow(simTime, y, yDot, res, jacBegin, p); + return impl::residualForwardsAxialFlow( + simTime, y, yDot, res, jacBegin, p); else - return impl::residualBackwardsAxialFlow(simTime, y, yDot, res, jacBegin, p); + return impl::residualBackwardsAxialFlow( + simTime, y, yDot, res, jacBegin, p); } -void sparsityPatternAxial(linalg::SparsityPatternRowIterator itBegin, unsigned int nComp, unsigned int nCol, int strideCell, double u, Weno& weno); +void sparsityPatternAxial(linalg::SparsityPatternRowIterator itBegin, unsigned int nComp, unsigned int nCol, + int strideCell, double u, Weno& weno); } // namespace convdisp } // namespace parts } // namespace model } // namespace cadet -#endif // LIBCADET_AXIALCONVECTIONDISPERSIONKERNEL_HPP_ +#endif // LIBCADET_AXIALCONVECTIONDISPERSIONKERNEL_HPP_ diff --git a/src/libcadet/model/parts/BindingCellKernel.hpp b/src/libcadet/model/parts/BindingCellKernel.hpp index 51a5f619a..aa25681de 100644 --- a/src/libcadet/model/parts/BindingCellKernel.hpp +++ b/src/libcadet/model/parts/BindingCellKernel.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Implements the kernel of a particle cell with mobile and solid phase. */ @@ -40,30 +40,34 @@ namespace cell namespace { - template - void bindingFlux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, active* res, const KernelParamsType& params, LinearBufferAllocator buffer, WithParamSensitivity) - { - params.binding->flux(t, secIdx, colPos, y, y - params.nComp, res, buffer, WithParamSensitivity()); - } +template +void bindingFlux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, active* res, + const KernelParamsType& params, LinearBufferAllocator buffer, WithParamSensitivity) +{ + params.binding->flux(t, secIdx, colPos, y, y - params.nComp, res, buffer, WithParamSensitivity()); +} - template - void bindingFlux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, active* res, const KernelParamsType& params, LinearBufferAllocator buffer, WithoutParamSensitivity) - { - params.binding->flux(t, secIdx, colPos, y, y - params.nComp, res, buffer, WithoutParamSensitivity()); - } +template +void bindingFlux(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, active* res, + const KernelParamsType& params, LinearBufferAllocator buffer, WithoutParamSensitivity) +{ + params.binding->flux(t, secIdx, colPos, y, y - params.nComp, res, buffer, WithoutParamSensitivity()); +} - template - void bindingFlux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, active* res, const KernelParamsType& params, LinearBufferAllocator buffer, WithParamSensitivity) - { - params.binding->flux(t, secIdx, colPos, y, y - params.nComp, res, buffer); - } +template +void bindingFlux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, active* res, + const KernelParamsType& params, LinearBufferAllocator buffer, WithParamSensitivity) +{ + params.binding->flux(t, secIdx, colPos, y, y - params.nComp, res, buffer); +} - template - void bindingFlux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double* res, const KernelParamsType& params, LinearBufferAllocator buffer, WithoutParamSensitivity) - { - params.binding->flux(t, secIdx, colPos, y, y - params.nComp, res, buffer); - } +template +void bindingFlux(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double* res, + const KernelParamsType& params, LinearBufferAllocator buffer, WithoutParamSensitivity) +{ + params.binding->flux(t, secIdx, colPos, y, y - params.nComp, res, buffer); } +} // namespace struct CellParameters { @@ -78,9 +82,11 @@ struct CellParameters IDynamicReactionModel* dynReaction; }; -template -void residualKernel(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - double const* yDot, ResidualType* res, RowIteratorType jacBase, const KernelParamsType& params, LinearBufferAllocator buffer) +template +void residualKernel(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, double const* yDot, + ResidualType* res, RowIteratorType jacBase, const KernelParamsType& params, + LinearBufferAllocator buffer) { // Mobile phase if (handleMobilePhaseDerivative && wantRes) @@ -91,7 +97,11 @@ void residualKernel(double t, unsigned int secIdx, const ColumnPosition& colPos, { for (unsigned int comp = 0; comp < params.nComp; ++comp, ++res, ++y) { - const ParamType invBetaP = (1.0 - static_cast(params.porosity)) / (params.poreAccessFactor ? static_cast(params.poreAccessFactor[comp]) * static_cast(params.porosity) : static_cast(params.porosity)); + const ParamType invBetaP = + (1.0 - static_cast(params.porosity)) / + (params.poreAccessFactor + ? static_cast(params.poreAccessFactor[comp]) * static_cast(params.porosity) + : static_cast(params.porosity)); // Ultimately, we need dc_{p,comp} / dt + 1 / beta_p * [ sum_i dq_comp^i / dt ] // where the bound states in the brackets are the quasi-stationary states only. @@ -132,7 +142,8 @@ void residualKernel(double t, unsigned int secIdx, const ColumnPosition& colPos, if (wantJac) { // static_cast should be sufficient here, but this statement is also analyzed when wantJac = false - params.binding->analyticJacobian(t, secIdx, colPos, reinterpret_cast(y), params.nComp, jacBase + params.nComp, buffer); + params.binding->analyticJacobian(t, secIdx, colPos, reinterpret_cast(y), params.nComp, + jacBase + params.nComp, buffer); } if (params.binding->hasDynamicReactions() && yDot && wantRes) @@ -161,17 +172,24 @@ void residualKernel(double t, unsigned int secIdx, const ColumnPosition& colPos, BufferedArray fluxSolid = buffer.template array(params.nTotalBound); std::fill_n(static_cast(fluxSolid), params.nTotalBound, 0.0); - params.dynReaction->residualCombinedAdd(t, secIdx, colPos, y - params.nComp, y, res - params.nComp, static_cast(fluxSolid), -1.0, buffer); + params.dynReaction->residualCombinedAdd(t, secIdx, colPos, y - params.nComp, y, res - params.nComp, + static_cast(fluxSolid), -1.0, buffer); unsigned int idx = 0; for (unsigned int comp = 0; comp < params.nComp; ++comp) { - const ParamType invBetaP = (1.0 - static_cast(params.porosity)) / (params.poreAccessFactor ? static_cast(params.poreAccessFactor[comp]) * static_cast(params.porosity) : static_cast(params.porosity)); + const ParamType invBetaP = + (1.0 - static_cast(params.porosity)) / + (params.poreAccessFactor ? static_cast(params.poreAccessFactor[comp]) * + static_cast(params.porosity) + : static_cast(params.porosity)); for (unsigned int bnd = 0; bnd < params.nBound[comp]; ++bnd, ++idx) { // Add reaction term to mobile phase - res[-static_cast(params.nComp) + static_cast(comp)] += static_cast::type>(invBetaP)* fluxSolid[idx]; + res[-static_cast(params.nComp) + static_cast(comp)] += + static_cast::type>(invBetaP) * + fluxSolid[idx]; if (!params.qsReaction[idx]) { @@ -185,17 +203,25 @@ void residualKernel(double t, unsigned int secIdx, const ColumnPosition& colPos, { if (params.nTotalBound > 0) { - BufferedArray fluxSolidJacobian = buffer.template array(params.nTotalBound * (params.nTotalBound + params.nComp)); - linalg::DenseMatrixView dmv(static_cast(fluxSolidJacobian), nullptr, params.nTotalBound, params.nTotalBound + params.nComp); + BufferedArray fluxSolidJacobian = + buffer.template array(params.nTotalBound * (params.nTotalBound + params.nComp)); + linalg::DenseMatrixView dmv(static_cast(fluxSolidJacobian), nullptr, params.nTotalBound, + params.nTotalBound + params.nComp); dmv.setAll(0.0); // static_cast should be sufficient here, but this statement is also analyzed when wantJac = false - params.dynReaction->analyticJacobianCombinedAdd(t, secIdx, colPos, reinterpret_cast(y - params.nComp), reinterpret_cast(y), -1.0, jacBase, dmv.row(0, params.nComp), buffer); + params.dynReaction->analyticJacobianCombinedAdd( + t, secIdx, colPos, reinterpret_cast(y - params.nComp), + reinterpret_cast(y), -1.0, jacBase, dmv.row(0, params.nComp), buffer); unsigned int idx = 0; for (unsigned int comp = 0; comp < params.nComp; ++comp) { - const double invBetaP = (1.0 - static_cast(params.porosity)) / (params.poreAccessFactor ? static_cast(params.poreAccessFactor[comp]) * static_cast(params.porosity) : static_cast(params.porosity)); + const double invBetaP = + (1.0 - static_cast(params.porosity)) / + (params.poreAccessFactor + ? static_cast(params.poreAccessFactor[comp]) * static_cast(params.porosity) + : static_cast(params.porosity)); for (unsigned int bnd = 0; bnd < params.nBound[comp]; ++bnd, ++idx) { @@ -205,7 +231,8 @@ void residualKernel(double t, unsigned int secIdx, const ColumnPosition& colPos, if (!params.qsReaction[idx]) { // Add Jacobian row to solid phase - (jacBase + params.nComp + idx).addArray(dmv.rowPtr(idx), -static_cast(params.nComp + idx), dmv.columns(), 1.0); + (jacBase + params.nComp + idx) + .addArray(dmv.rowPtr(idx), -static_cast(params.nComp + idx), dmv.columns(), 1.0); } } } @@ -217,13 +244,14 @@ void residualKernel(double t, unsigned int secIdx, const ColumnPosition& colPos, // reaction model does not interact with it. // static_cast should be sufficient here, but this statement is also analyzed when wantJac = false - params.dynReaction->analyticJacobianCombinedAdd(t, secIdx, colPos, reinterpret_cast(y - params.nComp), reinterpret_cast(y), -1.0, jacBase, linalg::DenseBandedRowIterator(), buffer); + params.dynReaction->analyticJacobianCombinedAdd( + t, secIdx, colPos, reinterpret_cast(y - params.nComp), + reinterpret_cast(y), -1.0, jacBase, linalg::DenseBandedRowIterator(), buffer); } } } } - /** * @brief Executes multiplication of particle shell Jacobian wrt. to state variable * @details [long description] @@ -235,12 +263,14 @@ void residualKernel(double t, unsigned int secIdx, const ColumnPosition& colPos, * @param [in] nTotalBound Total number of bound states (of all components) * @param [in] qsReaction Array that indicates whether a reaction is quasi-stationary * @param [in] factor Factor @f$ \alpha @f$ - * @param [in] qsFactor Factor of the @f$ \mathrm{d}q / \mathrm{d}t @f$ terms added to the mobile phase (only for quasi-stationary bound states) + * @param [in] qsFactor Factor of the @f$ \mathrm{d}q / \mathrm{d}t @f$ terms added to the mobile phase (only for + * quasi-stationary bound states) */ template -inline void multiplyWithDerivativeJacobianKernel(double const* const mobileSdot, double* const mobileRes, unsigned int nComp, - unsigned int const* const nBoundPerComp, unsigned int const* const boundOffset, const unsigned int nTotalBound, - int const* const qsReaction, double factor, double qsFactor) +inline void multiplyWithDerivativeJacobianKernel(double const* const mobileSdot, double* const mobileRes, + unsigned int nComp, unsigned int const* const nBoundPerComp, + unsigned int const* const boundOffset, const unsigned int nTotalBound, + int const* const qsReaction, double factor, double qsFactor) { // Mobile phase for (unsigned int comp = 0; comp < nComp; ++comp) @@ -290,8 +320,10 @@ inline void multiplyWithDerivativeJacobianKernel(double const* const mobileSdot, * @param [in] qsReaction Array that indicates whether a reaction is quasi-stationary */ template -void addTimeDerivativeToJacobianParticleShell(rowIter_t& jac, double alpha, double porosity, int nComp, unsigned int const* const nBoundPerComp, - active const* const poreAccessFactor, const unsigned int nTotalBound, unsigned int const* const offsetBoundComp, int const* const qsReaction) +void addTimeDerivativeToJacobianParticleShell(rowIter_t& jac, double alpha, double porosity, int nComp, + unsigned int const* const nBoundPerComp, + active const* const poreAccessFactor, const unsigned int nTotalBound, + unsigned int const* const offsetBoundComp, int const* const qsReaction) { // Mobile phase for (int comp = 0; comp < nComp; ++comp, ++jac) @@ -303,7 +335,7 @@ void addTimeDerivativeToJacobianParticleShell(rowIter_t& jac, double alpha, doub const double invBetaP = (1.0 - porosity) / (static_cast(poreAccessFactor[comp]) * porosity); // Add derivative with respect to dq / dt to Jacobian -// const int nBound = static_cast(nBoundPerComp[comp]); + // const int nBound = static_cast(nBoundPerComp[comp]); for (int i = 0; i < static_cast(nBoundPerComp[comp]); ++i) { const int idxBoundState = offsetBoundComp[comp] + i; @@ -333,4 +365,4 @@ void addTimeDerivativeToJacobianParticleShell(rowIter_t& jac, double alpha, doub } // namespace model } // namespace cadet -#endif // LIBCADET_BINDINGCELLKERNEL_HPP_ +#endif // LIBCADET_BINDINGCELLKERNEL_HPP_ diff --git a/src/libcadet/model/parts/ConvectionDispersionOperator.cpp b/src/libcadet/model/parts/ConvectionDispersionOperator.cpp index e5c1c3921..7792569bf 100644 --- a/src/libcadet/model/parts/ConvectionDispersionOperator.cpp +++ b/src/libcadet/model/parts/ConvectionDispersionOperator.cpp @@ -41,8 +41,9 @@ namespace parts /** * @brief Creates an AxialConvectionDispersionOperatorBase */ -AxialConvectionDispersionOperatorBase::AxialConvectionDispersionOperatorBase() : _stencilMemory(sizeof(active) * Weno::maxStencilSize()), - _wenoDerivatives(new double[Weno::maxStencilSize()]), _weno(), _dispersionDep(nullptr) +AxialConvectionDispersionOperatorBase::AxialConvectionDispersionOperatorBase() + : _stencilMemory(sizeof(active) * Weno::maxStencilSize()), _wenoDerivatives(new double[Weno::maxStencilSize()]), + _weno(), _dispersionDep(nullptr) { } @@ -62,7 +63,10 @@ AxialConvectionDispersionOperatorBase::~AxialConvectionDispersionOperatorBase() * @param [in] nCol Number of axial cells * @return @c true if configuration went fine, @c false otherwise */ -bool AxialConvectionDispersionOperatorBase::configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, unsigned int nCol, unsigned int strideCell) +bool AxialConvectionDispersionOperatorBase::configureModelDiscretization(IParameterProvider& paramProvider, + const IConfigHelper& helper, + unsigned int nComp, unsigned int nCol, + unsigned int strideCell) { _nComp = nComp; _nCol = nCol; @@ -102,7 +106,8 @@ bool AxialConvectionDispersionOperatorBase::configureModelDiscretization(IParame * @param [out] parameters Map in which local parameters are inserted * @return @c true if configuration went fine, @c false otherwise */ -bool AxialConvectionDispersionOperatorBase::configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, std::unordered_map& parameters) +bool AxialConvectionDispersionOperatorBase::configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, + std::unordered_map& parameters) { // Read geometry parameters _colLength = paramProvider.getDouble("COL_LENGTH"); @@ -142,11 +147,16 @@ bool AxialConvectionDispersionOperatorBase::configure(UnitOpIdx unitOpIdx, IPara _dispersionCompIndep = false; if (!_dispersionCompIndep && (_colDispersion.size() % _nComp != 0)) - throw InvalidParameterException("Number of elements in field COL_DISPERSION is not a positive multiple of NCOMP (" + std::to_string(_nComp) + ")"); + throw InvalidParameterException( + "Number of elements in field COL_DISPERSION is not a positive multiple of NCOMP (" + + std::to_string(_nComp) + ")"); if ((mode == 0) && (_colDispersion.size() != 1)) - throw InvalidParameterException("Number of elements in field COL_DISPERSION inconsistent with COL_DISPERSION_MULTIPLEX (should be 1)"); + throw InvalidParameterException( + "Number of elements in field COL_DISPERSION inconsistent with COL_DISPERSION_MULTIPLEX (should be 1)"); if ((mode == 1) && (_colDispersion.size() != _nComp)) - throw InvalidParameterException("Number of elements in field COL_DISPERSION inconsistent with COL_DISPERSION_MULTIPLEX (should be " + std::to_string(_nComp) + ")"); + throw InvalidParameterException( + "Number of elements in field COL_DISPERSION inconsistent with COL_DISPERSION_MULTIPLEX (should be " + + std::to_string(_nComp) + ")"); } else { @@ -183,20 +193,30 @@ bool AxialConvectionDispersionOperatorBase::configure(UnitOpIdx unitOpIdx, IPara { // Register only the first item in each section for (std::size_t i = 0; i < _colDispersion.size() / _nComp; ++i) - parameters[makeParamId(hashString("COL_DISPERSION"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, i)] = &_colDispersion[i * _nComp]; + parameters[makeParamId(hashString("COL_DISPERSION"), unitOpIdx, CompIndep, ParTypeIndep, + BoundStateIndep, ReactionIndep, i)] = &_colDispersion[i * _nComp]; } else { // We have only one parameter - parameters[makeParamId(hashString("COL_DISPERSION"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_colDispersion[0]; + parameters[makeParamId(hashString("COL_DISPERSION"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_colDispersion[0]; } } else - registerParam2DArray(parameters, _colDispersion, [=](bool multi, unsigned int sec, unsigned int comp) { return makeParamId(hashString("COL_DISPERSION"), unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, multi ? sec : SectionIndep); }, _nComp); + registerParam2DArray( + parameters, _colDispersion, + [=](bool multi, unsigned int sec, unsigned int comp) { + return makeParamId(hashString("COL_DISPERSION"), unitOpIdx, comp, ParTypeIndep, BoundStateIndep, + ReactionIndep, multi ? sec : SectionIndep); + }, + _nComp); registerScalarSectionDependentParam(hashString("VELOCITY"), parameters, _velocity, unitOpIdx, ParTypeIndep); - parameters[makeParamId(hashString("COL_LENGTH"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_colLength; - parameters[makeParamId(hashString("CROSS_SECTION_AREA"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_crossSection; + parameters[makeParamId(hashString("COL_LENGTH"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep)] = &_colLength; + parameters[makeParamId(hashString("CROSS_SECTION_AREA"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_crossSection; return true; } @@ -246,7 +266,8 @@ bool AxialConvectionDispersionOperatorBase::notifyDiscontinuousSectionTransition * @param [in] out Total volumetric outlet flow rate * @param [in] colPorosity Porosity used for computing interstitial velocity from volumetric flow rate */ -void AxialConvectionDispersionOperatorBase::setFlowRates(const active& in, const active& out, const active& colPorosity) CADET_NOEXCEPT +void AxialConvectionDispersionOperatorBase::setFlowRates(const active& in, const active& out, + const active& colPorosity) CADET_NOEXCEPT { // If we have cross section area, interstitial velocity is given by network flow rates if (_crossSection > 0.0) @@ -264,98 +285,108 @@ void AxialConvectionDispersionOperatorBase::setFlowRates(const active& in, const * @param [in] jac Matrix that holds the Jacobian * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ -int AxialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, linalg::BandMatrix& jac) +int AxialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, double const* y, + double const* yDot, double* res, linalg::BandMatrix& jac) { // Reset Jacobian jac.setAll(0.0); - return residualImpl(model, t, secIdx, y, yDot, res, jac.row(0)); + return residualImpl(model, t, secIdx, y, yDot, res, + jac.row(0)); } -int AxialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, WithoutParamSensitivity) +int AxialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, double const* y, + double const* yDot, double* res, WithoutParamSensitivity) { - return residualImpl(model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); + return residualImpl( + model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); } -int AxialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, WithoutParamSensitivity) +int AxialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, active const* y, + double const* yDot, active* res, WithoutParamSensitivity) { - return residualImpl(model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); + return residualImpl( + model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); } -int AxialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, linalg::BandMatrix& jac) +int AxialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, double const* y, + double const* yDot, active* res, linalg::BandMatrix& jac) { // Reset Jacobian jac.setAll(0.0); - return residualImpl(model, t, secIdx, y, yDot, res, jac.row(0)); + return residualImpl(model, t, secIdx, y, yDot, res, + jac.row(0)); } -int AxialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, WithParamSensitivity) +int AxialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, double const* y, + double const* yDot, active* res, WithParamSensitivity) { - return residualImpl(model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); + return residualImpl( + model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); } -int AxialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, WithParamSensitivity) +int AxialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, active const* y, + double const* yDot, active* res, WithParamSensitivity) { - return residualImpl(model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); + return residualImpl( + model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); } -int AxialConvectionDispersionOperatorBase::jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, linalg::BandMatrix& jac) +int AxialConvectionDispersionOperatorBase::jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, + double const* yDot, double* res, linalg::BandMatrix& jac) { // Reset Jacobian jac.setAll(0.0); - return residualImpl(model, t, secIdx, y, nullptr, nullptr, jac.row(0)); + return residualImpl( + model, t, secIdx, y, nullptr, nullptr, jac.row(0)); } -int AxialConvectionDispersionOperatorBase::jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, linalg::BandMatrix& jac) +int AxialConvectionDispersionOperatorBase::jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, + double const* yDot, active* res, linalg::BandMatrix& jac) { // Reset Jacobian jac.setAll(0.0); - return residualImpl(model, t, secIdx, y, nullptr, nullptr, jac.row(0)); + return residualImpl( + model, t, secIdx, y, nullptr, nullptr, jac.row(0)); } -template -int AxialConvectionDispersionOperatorBase::residualImpl(const IModel& model, double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, RowIteratorType jacBegin) +template +int AxialConvectionDispersionOperatorBase::residualImpl(const IModel& model, double t, unsigned int secIdx, + StateType const* y, double const* yDot, ResidualType* res, + RowIteratorType jacBegin) { const ParamType u = static_cast(_curVelocity); active const* const d_c = getSectionDependentSlice(_colDispersion, _nComp, secIdx); const ParamType h = static_cast(_colLength) / static_cast(_nCol); -// const int strideCell = strideColCell(); + // const int strideCell = strideColCell(); convdisp::AxialFlowParameters fp{ - u, - d_c, - h, - _wenoDerivatives, - &_weno, - &_stencilMemory, - _wenoEpsilon, - strideColCell(), - _nComp, - _nCol, - 0u, - _nComp, - _dispersionDep, - model - }; - - return convdisp::residualKernelAxial(SimulationTime{t, secIdx}, y, yDot, res, jacBegin, fp); + u, d_c, h, _wenoDerivatives, &_weno, &_stencilMemory, _wenoEpsilon, strideColCell(), _nComp, + _nCol, 0u, _nComp, _dispersionDep, model}; + + return convdisp::residualKernelAxial( + SimulationTime{t, secIdx}, y, yDot, res, jacBegin, fp); } /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ + * with a given vector * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). - * + * * Note that this function only performs multiplication with the Jacobian of the (axial) transport equations. - * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit operation's arrays. + * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit + * operation's arrays. * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] sDot Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ * @param [out] ret Vector @f$ z @f$ which stores the result of the operation */ -void AxialConvectionDispersionOperatorBase::multiplyWithDerivativeJacobian(const SimulationTime& simTime, double const* sDot, double* ret) const +void AxialConvectionDispersionOperatorBase::multiplyWithDerivativeJacobian(const SimulationTime& simTime, + double const* sDot, double* ret) const { double* localRet = ret + offsetC(); double const* localSdot = sDot + offsetC(); @@ -372,12 +403,14 @@ void AxialConvectionDispersionOperatorBase::multiplyWithDerivativeJacobian(const /** * @brief Adds the derivatives with respect to @f$ \dot{y} @f$ of @f$ F(t, y, \dot{y}) @f$ to the Jacobian - * @details This functions computes - * @f[ \begin{align*} \text{_jacCdisc} = \text{_jacCdisc} + \alpha \frac{\partial F}{\partial \dot{y}}. \end{align*} @f] - * The factor @f$ \alpha @f$ is useful when constructing the linear system in the time integration process. + * @details This functions computes + * @f[ \begin{align*} \text{_jacCdisc} = \text{_jacCdisc} + \alpha \frac{\partial F}{\partial \dot{y}}. + * \end{align*} @f] The factor @f$ \alpha @f$ is useful when constructing the linear system in the time integration + * process. * @param [in] alpha Factor in front of @f$ \frac{\partial F}{\partial \dot{y}} @f$ */ -void AxialConvectionDispersionOperatorBase::addTimeDerivativeToJacobian(double alpha, linalg::FactorizableBandMatrix& jacDisc) +void AxialConvectionDispersionOperatorBase::addTimeDerivativeToJacobian(double alpha, + linalg::FactorizableBandMatrix& jacDisc) { const int gapCell = strideColCell() - static_cast(_nComp) * strideColComp(); linalg::FactorizableBandMatrix::RowIterator jac = jacDisc.row(0); @@ -436,7 +469,8 @@ bool AxialConvectionDispersionOperatorBase::setParameter(const ParameterId& pId, if (!_dispersionCompIndep) return false; - if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) + if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || + (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) return false; if (_colDispersion.size() > _nComp) @@ -461,7 +495,8 @@ bool AxialConvectionDispersionOperatorBase::setParameter(const ParameterId& pId, return true; } -bool AxialConvectionDispersionOperatorBase::setSensitiveParameterValue(const std::unordered_set& sensParams, const ParameterId& pId, double value) +bool AxialConvectionDispersionOperatorBase::setSensitiveParameterValue(const std::unordered_set& sensParams, + const ParameterId& pId, double value) { // Check if parameter is in parameter dependence of column dispersion coefficient if (_dispersionDep) @@ -478,7 +513,8 @@ bool AxialConvectionDispersionOperatorBase::setSensitiveParameterValue(const std if (!_dispersionCompIndep) return false; - if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) + if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || + (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) return false; if (_colDispersion.size() > _nComp) @@ -509,7 +545,9 @@ bool AxialConvectionDispersionOperatorBase::setSensitiveParameterValue(const std return true; } -bool AxialConvectionDispersionOperatorBase::setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, unsigned int adDirection, double adValue) +bool AxialConvectionDispersionOperatorBase::setSensitiveParameter(std::unordered_set& sensParams, + const ParameterId& pId, unsigned int adDirection, + double adValue) { // Check if parameter is in parameter dependence of column dispersion coefficient if (_dispersionDep) @@ -526,7 +564,8 @@ bool AxialConvectionDispersionOperatorBase::setSensitiveParameter(std::unordered if (!_dispersionCompIndep) return false; - if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) + if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || + (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) return false; if (_colDispersion.size() > _nComp) @@ -553,13 +592,11 @@ bool AxialConvectionDispersionOperatorBase::setSensitiveParameter(std::unordered return true; } - - - /** * @brief Creates a RadialConvectionDispersionOperatorBase */ -RadialConvectionDispersionOperatorBase::RadialConvectionDispersionOperatorBase() : _stencilMemory(sizeof(active) * Weno::maxStencilSize()), _dispersionDep(nullptr) +RadialConvectionDispersionOperatorBase::RadialConvectionDispersionOperatorBase() + : _stencilMemory(sizeof(active) * Weno::maxStencilSize()), _dispersionDep(nullptr) { } @@ -577,7 +614,10 @@ RadialConvectionDispersionOperatorBase::~RadialConvectionDispersionOperatorBase( * @param [in] nCol Number of axial cells * @return @c true if configuration went fine, @c false otherwise */ -bool RadialConvectionDispersionOperatorBase::configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, unsigned int nCol, unsigned int strideCell) +bool RadialConvectionDispersionOperatorBase::configureModelDiscretization(IParameterProvider& paramProvider, + const IConfigHelper& helper, + unsigned int nComp, unsigned int nCol, + unsigned int strideCell) { _nComp = nComp; _nCol = nCol; @@ -598,13 +638,13 @@ bool RadialConvectionDispersionOperatorBase::configureModelDiscretization(IParam paramProvider.pushScope("discretization"); // Read WENO settings and apply them -/* - paramProvider.pushScope("weno"); - _weno.order(paramProvider.getInt("WENO_ORDER")); - _weno.boundaryTreatment(paramProvider.getInt("BOUNDARY_MODEL")); - _wenoEpsilon = paramProvider.getDouble("WENO_EPS"); - paramProvider.popScope(); -*/ + /* + paramProvider.pushScope("weno"); + _weno.order(paramProvider.getInt("WENO_ORDER")); + _weno.boundaryTreatment(paramProvider.getInt("BOUNDARY_MODEL")); + _wenoEpsilon = paramProvider.getDouble("WENO_EPS"); + paramProvider.popScope(); + */ paramProvider.popScope(); @@ -619,7 +659,8 @@ bool RadialConvectionDispersionOperatorBase::configureModelDiscretization(IParam * @param [out] parameters Map in which local parameters are inserted * @return @c true if configuration went fine, @c false otherwise */ -bool RadialConvectionDispersionOperatorBase::configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, std::unordered_map& parameters) +bool RadialConvectionDispersionOperatorBase::configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, + std::unordered_map& parameters) { // Read geometry parameters _innerRadius = paramProvider.getDouble("COL_RADIUS_INNER"); @@ -660,11 +701,16 @@ bool RadialConvectionDispersionOperatorBase::configure(UnitOpIdx unitOpIdx, IPar _dispersionCompIndep = false; if (!_dispersionCompIndep && (_colDispersion.size() % _nComp != 0)) - throw InvalidParameterException("Number of elements in field COL_DISPERSION is not a positive multiple of NCOMP (" + std::to_string(_nComp) + ")"); + throw InvalidParameterException( + "Number of elements in field COL_DISPERSION is not a positive multiple of NCOMP (" + + std::to_string(_nComp) + ")"); if ((mode == 0) && (_colDispersion.size() != 1)) - throw InvalidParameterException("Number of elements in field COL_DISPERSION inconsistent with COL_DISPERSION_MULTIPLEX (should be 1)"); + throw InvalidParameterException( + "Number of elements in field COL_DISPERSION inconsistent with COL_DISPERSION_MULTIPLEX (should be 1)"); if ((mode == 1) && (_colDispersion.size() != _nComp)) - throw InvalidParameterException("Number of elements in field COL_DISPERSION inconsistent with COL_DISPERSION_MULTIPLEX (should be " + std::to_string(_nComp) + ")"); + throw InvalidParameterException( + "Number of elements in field COL_DISPERSION inconsistent with COL_DISPERSION_MULTIPLEX (should be " + + std::to_string(_nComp) + ")"); } else { @@ -701,21 +747,32 @@ bool RadialConvectionDispersionOperatorBase::configure(UnitOpIdx unitOpIdx, IPar { // Register only the first item in each section for (std::size_t i = 0; i < _colDispersion.size() / _nComp; ++i) - parameters[makeParamId(hashString("COL_DISPERSION"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, i)] = &_colDispersion[i * _nComp]; + parameters[makeParamId(hashString("COL_DISPERSION"), unitOpIdx, CompIndep, ParTypeIndep, + BoundStateIndep, ReactionIndep, i)] = &_colDispersion[i * _nComp]; } else { // We have only one parameter - parameters[makeParamId(hashString("COL_DISPERSION"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_colDispersion[0]; + parameters[makeParamId(hashString("COL_DISPERSION"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_colDispersion[0]; } } else - registerParam2DArray(parameters, _colDispersion, [=](bool multi, unsigned int sec, unsigned int comp) { return makeParamId(hashString("COL_DISPERSION"), unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, multi ? sec : SectionIndep); }, _nComp); + registerParam2DArray( + parameters, _colDispersion, + [=](bool multi, unsigned int sec, unsigned int comp) { + return makeParamId(hashString("COL_DISPERSION"), unitOpIdx, comp, ParTypeIndep, BoundStateIndep, + ReactionIndep, multi ? sec : SectionIndep); + }, + _nComp); registerScalarSectionDependentParam(hashString("VELOCITY_COEFF"), parameters, _velocity, unitOpIdx, ParTypeIndep); - parameters[makeParamId(hashString("COL_LENGTH"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_colLength; - parameters[makeParamId(hashString("COL_RADIUS_INNER"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_innerRadius; - parameters[makeParamId(hashString("COL_RADIUS_OUTER"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_outerRadius; + parameters[makeParamId(hashString("COL_LENGTH"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep)] = &_colLength; + parameters[makeParamId(hashString("COL_RADIUS_INNER"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_innerRadius; + parameters[makeParamId(hashString("COL_RADIUS_OUTER"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_outerRadius; equidistantCells(); @@ -764,7 +821,8 @@ bool RadialConvectionDispersionOperatorBase::notifyDiscontinuousSectionTransitio * @param [in] out Total volumetric outlet flow rate * @param [in] colPorosity Porosity used for computing interstitial velocity from volumetric flow rate */ -void RadialConvectionDispersionOperatorBase::setFlowRates(const active& in, const active& out, const active& colPorosity) CADET_NOEXCEPT +void RadialConvectionDispersionOperatorBase::setFlowRates(const active& in, const active& out, + const active& colPorosity) CADET_NOEXCEPT { const double pi = 3.1415926535897932384626434; @@ -790,94 +848,123 @@ active RadialConvectionDispersionOperatorBase::currentVelocity(double pos) const * @param [in] jac Matrix that holds the Jacobian * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ -int RadialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, linalg::BandMatrix& jac) +int RadialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, double* res, + linalg::BandMatrix& jac) { // Reset Jacobian jac.setAll(0.0); - return residualImpl(model, t, secIdx, y, yDot, res, jac.row(0)); + return residualImpl(model, t, secIdx, y, yDot, res, + jac.row(0)); } -int RadialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, WithoutParamSensitivity) +int RadialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, double* res, + WithoutParamSensitivity) { - return residualImpl(model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); + return residualImpl( + model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); } -int RadialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, WithoutParamSensitivity) +int RadialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, + active const* y, double const* yDot, active* res, + WithoutParamSensitivity) { - return residualImpl(model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); + return residualImpl( + model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); } -int RadialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, linalg::BandMatrix& jac) +int RadialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, active* res, + linalg::BandMatrix& jac) { // Reset Jacobian jac.setAll(0.0); - return residualImpl(model, t, secIdx, y, yDot, res, jac.row(0)); + return residualImpl(model, t, secIdx, y, yDot, res, + jac.row(0)); } -int RadialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, WithParamSensitivity) +int RadialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, active* res, + WithParamSensitivity) { - return residualImpl(model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); + return residualImpl( + model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); } -int RadialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, WithParamSensitivity) +int RadialConvectionDispersionOperatorBase::residual(const IModel& model, double t, unsigned int secIdx, + active const* y, double const* yDot, active* res, + WithParamSensitivity) { - return residualImpl(model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); + return residualImpl( + model, t, secIdx, y, yDot, res, linalg::BandMatrix::RowIterator()); } -int RadialConvectionDispersionOperatorBase::jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, linalg::BandMatrix& jac) +int RadialConvectionDispersionOperatorBase::jacobian(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, double* res, + linalg::BandMatrix& jac) { // Reset Jacobian jac.setAll(0.0); - return residualImpl(model, t, secIdx, y, nullptr, nullptr, jac.row(0)); + return residualImpl( + model, t, secIdx, y, nullptr, nullptr, jac.row(0)); } -int RadialConvectionDispersionOperatorBase::jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, linalg::BandMatrix& jac) +int RadialConvectionDispersionOperatorBase::jacobian(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, active* res, + linalg::BandMatrix& jac) { // Reset Jacobian jac.setAll(0.0); - return residualImpl(model, t, secIdx, y, nullptr, nullptr, jac.row(0)); + return residualImpl( + model, t, secIdx, y, nullptr, nullptr, jac.row(0)); } -template -int RadialConvectionDispersionOperatorBase::residualImpl(const IModel& model, double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, RowIteratorType jacBegin) +template +int RadialConvectionDispersionOperatorBase::residualImpl(const IModel& model, double t, unsigned int secIdx, + StateType const* y, double const* yDot, ResidualType* res, + RowIteratorType jacBegin) { const ParamType u = static_cast(_curVelocity); active const* const d_rad = getSectionDependentSlice(_colDispersion, _nComp, secIdx); - convdisp::RadialFlowParameters fp{ - u, - d_rad, - _cellCenters.data(), - _cellSizes.data(), - _cellBounds.data(), - &_stencilMemory, - strideColCell(), - _nComp, - _nCol, - 0u, - _nComp, - _dispersionDep, - model - }; - - return convdisp::residualKernelRadial(SimulationTime{t, secIdx}, y, yDot, res, jacBegin, fp); + convdisp::RadialFlowParameters fp{u, + d_rad, + _cellCenters.data(), + _cellSizes.data(), + _cellBounds.data(), + &_stencilMemory, + strideColCell(), + _nComp, + _nCol, + 0u, + _nComp, + _dispersionDep, + model}; + + return convdisp::residualKernelRadial( + SimulationTime{t, secIdx}, y, yDot, res, jacBegin, fp); } /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ + * with a given vector * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). - * + * * Note that this function only performs multiplication with the Jacobian of the (axial) transport equations. - * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit operation's arrays. + * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit + * operation's arrays. * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] sDot Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ * @param [out] ret Vector @f$ z @f$ which stores the result of the operation */ -void RadialConvectionDispersionOperatorBase::multiplyWithDerivativeJacobian(const SimulationTime& simTime, double const* sDot, double* ret) const +void RadialConvectionDispersionOperatorBase::multiplyWithDerivativeJacobian(const SimulationTime& simTime, + double const* sDot, double* ret) const { double* localRet = ret + offsetC(); double const* localSdot = sDot + offsetC(); @@ -894,12 +981,14 @@ void RadialConvectionDispersionOperatorBase::multiplyWithDerivativeJacobian(cons /** * @brief Adds the derivatives with respect to @f$ \dot{y} @f$ of @f$ F(t, y, \dot{y}) @f$ to the Jacobian - * @details This functions computes - * @f[ \begin{align*} \text{_jacCdisc} = \text{_jacCdisc} + \alpha \frac{\partial F}{\partial \dot{y}}. \end{align*} @f] - * The factor @f$ \alpha @f$ is useful when constructing the linear system in the time integration process. + * @details This functions computes + * @f[ \begin{align*} \text{_jacCdisc} = \text{_jacCdisc} + \alpha \frac{\partial F}{\partial \dot{y}}. + * \end{align*} @f] The factor @f$ \alpha @f$ is useful when constructing the linear system in the time integration + * process. * @param [in] alpha Factor in front of @f$ \frac{\partial F}{\partial \dot{y}} @f$ */ -void RadialConvectionDispersionOperatorBase::addTimeDerivativeToJacobian(double alpha, linalg::FactorizableBandMatrix& jacDisc) +void RadialConvectionDispersionOperatorBase::addTimeDerivativeToJacobian(double alpha, + linalg::FactorizableBandMatrix& jacDisc) { const int gapCell = strideColCell() - static_cast(_nComp) * strideColComp(); linalg::FactorizableBandMatrix::RowIterator jac = jacDisc.row(0); @@ -919,14 +1008,14 @@ unsigned int RadialConvectionDispersionOperatorBase::jacobianLowerBandwidth() co // right cell face (lower + 1 + upper) and to the left cell face (shift the stencil by -1 because influx of cell i // is outflux of cell i-1) // We also have to make sure that there's at least one sub and super diagonal for the dispersion term -// return std::max(_weno.lowerBandwidth() + 1u, 1u) * strideColCell(); + // return std::max(_weno.lowerBandwidth() + 1u, 1u) * strideColCell(); return strideColCell(); } unsigned int RadialConvectionDispersionOperatorBase::jacobianUpperBandwidth() const CADET_NOEXCEPT { // We have to make sure that there's at least one sub and super diagonal for the dispersion term -// return std::max(_weno.upperBandwidth(), 1u) * strideColCell(); + // return std::max(_weno.upperBandwidth(), 1u) * strideColCell(); return strideColCell(); } @@ -975,7 +1064,8 @@ bool RadialConvectionDispersionOperatorBase::setParameter(const ParameterId& pId if (!_dispersionCompIndep) return false; - if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) + if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || + (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) return false; if (_colDispersion.size() > _nComp) @@ -1000,7 +1090,8 @@ bool RadialConvectionDispersionOperatorBase::setParameter(const ParameterId& pId return true; } -bool RadialConvectionDispersionOperatorBase::setSensitiveParameterValue(const std::unordered_set& sensParams, const ParameterId& pId, double value) +bool RadialConvectionDispersionOperatorBase::setSensitiveParameterValue(const std::unordered_set& sensParams, + const ParameterId& pId, double value) { // Check if parameter is in parameter dependence of column dispersion coefficient if (_dispersionDep) @@ -1032,7 +1123,8 @@ bool RadialConvectionDispersionOperatorBase::setSensitiveParameterValue(const st if (!_dispersionCompIndep) return false; - if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) + if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || + (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) return false; if (_colDispersion.size() > _nComp) @@ -1063,7 +1155,9 @@ bool RadialConvectionDispersionOperatorBase::setSensitiveParameterValue(const st return true; } -bool RadialConvectionDispersionOperatorBase::setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, unsigned int adDirection, double adValue) +bool RadialConvectionDispersionOperatorBase::setSensitiveParameter(std::unordered_set& sensParams, + const ParameterId& pId, unsigned int adDirection, + double adValue) { // Check if parameter is in parameter dependence of column dispersion coefficient if (_dispersionDep) @@ -1095,7 +1189,8 @@ bool RadialConvectionDispersionOperatorBase::setSensitiveParameter(std::unordere if (!_dispersionCompIndep) return false; - if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) + if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || + (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) return false; if (_colDispersion.size() > _nComp) @@ -1140,17 +1235,14 @@ void RadialConvectionDispersionOperatorBase::equidistantCells() _cellBounds = std::move(bounds); } - /** * @brief Creates an ConvectionDispersionOperator */ -template -ConvectionDispersionOperator::ConvectionDispersionOperator() +template ConvectionDispersionOperator::ConvectionDispersionOperator() { } -template -ConvectionDispersionOperator::~ConvectionDispersionOperator() CADET_NOEXCEPT +template ConvectionDispersionOperator::~ConvectionDispersionOperator() CADET_NOEXCEPT { } @@ -1159,8 +1251,7 @@ ConvectionDispersionOperator::~ConvectionDispersionOperator() CADET_NO * @details Band compression is used to minimize the amount of AD directions. * @return Number of required AD directions */ -template -int ConvectionDispersionOperator::requiredADdirs() const CADET_NOEXCEPT +template int ConvectionDispersionOperator::requiredADdirs() const CADET_NOEXCEPT { return _jacC.stride(); } @@ -1176,7 +1267,9 @@ int ConvectionDispersionOperator::requiredADdirs() const CADET_NOEXCEP * @return @c true if configuration went fine, @c false otherwise */ template -bool ConvectionDispersionOperator::configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, unsigned int nCol) +bool ConvectionDispersionOperator::configureModelDiscretization(IParameterProvider& paramProvider, + const IConfigHelper& helper, + unsigned int nComp, unsigned int nCol) { const bool retVal = _baseOp.configureModelDiscretization(paramProvider, helper, nComp, nCol, nComp); @@ -1202,7 +1295,8 @@ bool ConvectionDispersionOperator::configureModelDiscretization(IParam * @return @c true if configuration went fine, @c false otherwise */ template -bool ConvectionDispersionOperator::configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, std::unordered_map& parameters) +bool ConvectionDispersionOperator::configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, + std::unordered_map& parameters) { return _baseOp.configure(unitOpIdx, paramProvider, parameters); } @@ -1217,7 +1311,8 @@ bool ConvectionDispersionOperator::configure(UnitOpIdx unitOpIdx, IPar * @return @c true if flow direction has changed, otherwise @c false */ template -bool ConvectionDispersionOperator::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const AdJacobianParams& adJac) +bool ConvectionDispersionOperator::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const AdJacobianParams& adJac) { const bool hasChanged = _baseOp.notifyDiscontinuousSectionTransition(t, secIdx); @@ -1267,7 +1362,8 @@ void ConvectionDispersionOperator::prepareADvectors(const AdJacobianPa const unsigned int upperColBandwidth = _jacC.upperBandwidth(); // Column block - ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + offsetC(), adJac.adDirOffset, _baseOp.nComp() * _baseOp.nCol(), lowerColBandwidth, upperColBandwidth, lowerColBandwidth); + ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + offsetC(), adJac.adDirOffset, _baseOp.nComp() * _baseOp.nCol(), + lowerColBandwidth, upperColBandwidth, lowerColBandwidth); } /** @@ -1278,7 +1374,8 @@ void ConvectionDispersionOperator::prepareADvectors(const AdJacobianPa * @param [in] colPorosity Porosity used for computing interstitial velocity from volumetric flow rate */ template -void ConvectionDispersionOperator::setFlowRates(const active& in, const active& out, const active& colPorosity) CADET_NOEXCEPT +void ConvectionDispersionOperator::setFlowRates(const active& in, const active& out, + const active& colPorosity) CADET_NOEXCEPT { _baseOp.setFlowRates(in, out, colPorosity); } @@ -1295,7 +1392,9 @@ void ConvectionDispersionOperator::setFlowRates(const active& in, cons * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ template -int ConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, bool wantJac, WithoutParamSensitivity) +int ConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, double* res, bool wantJac, + WithoutParamSensitivity) { if (wantJac) return _baseOp.residual(model, t, secIdx, y, yDot, res, _jacC); @@ -1304,19 +1403,25 @@ int ConvectionDispersionOperator::residual(const IModel& model, double } template -int ConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithoutParamSensitivity) +int ConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, + active const* y, double const* yDot, active* res, bool wantJac, + WithoutParamSensitivity) { return _baseOp.residual(model, t, secIdx, y, yDot, res, WithoutParamSensitivity()); } template -int ConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity) +int ConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, + active const* y, double const* yDot, active* res, bool wantJac, + WithParamSensitivity) { return _baseOp.residual(model, t, secIdx, y, yDot, res, WithParamSensitivity()); } template -int ConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity) +int ConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, active* res, bool wantJac, + WithParamSensitivity) { if (wantJac) return _baseOp.residual(model, t, secIdx, y, yDot, res, _jacC); @@ -1333,12 +1438,14 @@ int ConvectionDispersionOperator::residual(const IModel& model, double * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ template -int ConvectionDispersionOperator::jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res) +int ConvectionDispersionOperator::jacobian(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, double* res) { return _baseOp.jacobian(model, t, secIdx, y, yDot, res, _jacC); } template -int ConvectionDispersionOperator::jacobian(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res) +int ConvectionDispersionOperator::jacobian(const IModel& model, double t, unsigned int secIdx, + active const* y, double const* yDot, active* res) { // This should not be reached cadet_assert(false); @@ -1346,25 +1453,29 @@ int ConvectionDispersionOperator::jacobian(const IModel& model, double } /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ + * with a given vector * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). - * + * * Note that this function only performs multiplication with the Jacobian of the (axial) transport equations. - * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit operation's arrays. + * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit + * operation's arrays. * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] sDot Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ * @param [out] ret Vector @f$ z @f$ which stores the result of the operation */ template -void ConvectionDispersionOperator::multiplyWithDerivativeJacobian(const SimulationTime& simTime, double const* sDot, double* ret) const +void ConvectionDispersionOperator::multiplyWithDerivativeJacobian(const SimulationTime& simTime, + double const* sDot, double* ret) const { _baseOp.multiplyWithDerivativeJacobian(simTime, sDot, ret); } /** * @brief Extracts the system Jacobian from band compressed AD seed vectors - * @details The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit operation's arrays. + * @details The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit + * operation's arrays. * @param [in] adRes Residual vector of AD datatypes with band compressed seed vectors * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) */ @@ -1379,16 +1490,19 @@ void ConvectionDispersionOperator::extractJacobianFromAD(active const* /** * @brief Compares the analytical Jacobian with a Jacobian derived by AD * @details The analytical Jacobian is assumed to be stored in the corresponding band matrices. - * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit operation's arrays. + * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit + * operation's arrays. * @param [in] adRes Residual vector of AD datatypes with band compressed seed vectors * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) * @return Maximum elementwise absolute difference between analytic and AD Jacobian */ template -double ConvectionDispersionOperator::checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const +double ConvectionDispersionOperator::checkAnalyticJacobianAgainstAd(active const* const adRes, + unsigned int adDirOffset) const { // Column - const double maxDiffCol = ad::compareBandedJacobianWithAd(adRes + offsetC(), adDirOffset, _jacC.lowerBandwidth(), _jacC); + const double maxDiffCol = + ad::compareBandedJacobianWithAd(adRes + offsetC(), adDirOffset, _jacC.lowerBandwidth(), _jacC); LOG(Debug) << "-> Col block diff: " << maxDiffCol; return maxDiffCol; @@ -1398,21 +1512,17 @@ double ConvectionDispersionOperator::checkAnalyticJacobianAgainstAd(ac /** * @brief Assembles the axial transport Jacobian @f$ J_0 @f$ of the time-discretized equations - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The system Jacobian of the original equations, - * \f[ \frac{\partial F}{\partial y}, \f] - * is already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible - * for adding - * \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] - * to the system Jacobian, which yields the Jacobian of the time-discretized equations - * \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] - * when a BDF method is used. The time integrator needs to solve this equation for @f$ y_0 @f$, which requires - * the solution of the linear system mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + * b \f] has to be solved. The system Jacobian of the original equations, \f[ \frac{\partial F}{\partial y}, \f] is + * already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible for + * adding \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] to the system Jacobian, which yields the Jacobian of the + * time-discretized equations \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] when a BDF method is used. + * The time integrator needs to solve this equation for @f$ y_0 @f$, which requires the solution of the linear system + * mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). * * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) */ -template -void ConvectionDispersionOperator::assembleDiscretizedJacobian(double alpha) +template void ConvectionDispersionOperator::assembleDiscretizedJacobian(double alpha) { // Copy normal matrix over to factorizable matrix _jacCdisc.copyOver(_jacC); @@ -1421,16 +1531,15 @@ void ConvectionDispersionOperator::assembleDiscretizedJacobian(double addTimeDerivativeToJacobian(alpha); } - /** * @brief Adds the derivatives with respect to @f$ \dot{y} @f$ of @f$ F(t, y, \dot{y}) @f$ to the Jacobian - * @details This functions computes - * @f[ \begin{align*} \text{_jacCdisc} = \text{_jacCdisc} + \alpha \frac{\partial F}{\partial \dot{y}}. \end{align*} @f] - * The factor @f$ \alpha @f$ is useful when constructing the linear system in the time integration process. + * @details This functions computes + * @f[ \begin{align*} \text{_jacCdisc} = \text{_jacCdisc} + \alpha \frac{\partial F}{\partial \dot{y}}. + * \end{align*} @f] The factor @f$ \alpha @f$ is useful when constructing the linear system in the time integration + * process. * @param [in] alpha Factor in front of @f$ \frac{\partial F}{\partial \dot{y}} @f$ */ -template -void ConvectionDispersionOperator::addTimeDerivativeToJacobian(double alpha) +template void ConvectionDispersionOperator::addTimeDerivativeToJacobian(double alpha) { _baseOp.addTimeDerivativeToJacobian(alpha, _jacCdisc); } @@ -1453,12 +1562,11 @@ bool ConvectionDispersionOperator::assembleAndFactorizeDiscretizedJaco * @details The (time discretized) Jacobian matrix has to be factorized before calling this function. * Note that the given right hand side vector @p rhs is not shifted by the inlet DOFs. That * is, it is assumed to point directly to the first axial DOF. - * + * * @param [in,out] rhs On entry, right hand side of the equation system. On exit, solution of the system. * @return @c true if the system was solved correctly, otherwise @c false */ -template -bool ConvectionDispersionOperator::solveDiscretizedJacobian(double* rhs) const +template bool ConvectionDispersionOperator::solveDiscretizedJacobian(double* rhs) const { return _jacCdisc.solve(rhs); } @@ -1501,8 +1609,8 @@ bool ConvectionDispersionOperator::solveTimeDerivativeSystem(const Sim template class ConvectionDispersionOperator; template class ConvectionDispersionOperator; -} // namespace parts +} // namespace parts -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/parts/ConvectionDispersionOperator.hpp b/src/libcadet/model/parts/ConvectionDispersionOperator.hpp index f59baf8a0..fe7d67af8 100644 --- a/src/libcadet/model/parts/ConvectionDispersionOperator.hpp +++ b/src/libcadet/model/parts/ConvectionDispersionOperator.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the convection dispersion transport operator. */ @@ -50,17 +50,18 @@ namespace parts /** * @brief Convection dispersion transport operator * @details Implements the equation - * + * * @f[\begin{align} - \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 c_i}{\partial z^2} \\ -\end{align} @f] + \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 +c_i}{\partial z^2} \\ \end{align} @f] * with Danckwerts boundary conditions (see @cite Danckwerts1953) @f[ \begin{align} u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partial z}(t,0) \\ \frac{\partial c_i}{\partial z}(t,L) &= 0 \end{align} @f] - * Methods are described in @cite VonLieres2010a (WENO, linear solver), and @cite Puttmann2013, @cite Puttmann2016 (forward sensitivities, AD, band compression) - * + * Methods are described in @cite VonLieres2010a (WENO, linear solver), and @cite Puttmann2013, @cite Puttmann2016 +(forward sensitivities, AD, band compression) + * * This class does not store the Jacobian. It only fills existing matrices given to its residual() functions. * It assumes that there is no offset to the inlet in the local state vector and that the firsts cell is placed * directly after the inlet DOFs. @@ -68,43 +69,88 @@ u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partia class AxialConvectionDispersionOperatorBase { public: - AxialConvectionDispersionOperatorBase(); ~AxialConvectionDispersionOperatorBase() CADET_NOEXCEPT; void setFlowRates(const active& in, const active& out, const active& colPorosity) CADET_NOEXCEPT; - bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, unsigned int nCol, unsigned int strideCell); - bool configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, std::unordered_map& parameters); + bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, + unsigned int nComp, unsigned int nCol, unsigned int strideCell); + bool configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, + std::unordered_map& parameters); bool notifyDiscontinuousSectionTransition(double t, unsigned int secIdx); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, linalg::BandMatrix& jac); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, linalg::BandMatrix& jac); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, WithoutParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, WithParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, WithParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, WithoutParamSensitivity); - - int jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, linalg::BandMatrix& jac); - int jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, linalg::BandMatrix& jac); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, + linalg::BandMatrix& jac); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, + linalg::BandMatrix& jac); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, + WithoutParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, + WithParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, + WithParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, + WithoutParamSensitivity); + + int jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, + linalg::BandMatrix& jac); + int jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, + linalg::BandMatrix& jac); void multiplyWithDerivativeJacobian(const SimulationTime& simTime, double const* sDot, double* ret) const; void addTimeDerivativeToJacobian(double alpha, linalg::FactorizableBandMatrix& jacDisc); - inline const active& columnLength() const CADET_NOEXCEPT { return _colLength; } - inline const active& crossSectionArea() const CADET_NOEXCEPT { return _crossSection; } - inline const active& currentVelocity(double pos) const CADET_NOEXCEPT { return _curVelocity; } - inline bool forwardFlow() const CADET_NOEXCEPT { return _curVelocity >= 0.0; } + inline const active& columnLength() const CADET_NOEXCEPT + { + return _colLength; + } + inline const active& crossSectionArea() const CADET_NOEXCEPT + { + return _crossSection; + } + inline const active& currentVelocity(double pos) const CADET_NOEXCEPT + { + return _curVelocity; + } + inline bool forwardFlow() const CADET_NOEXCEPT + { + return _curVelocity >= 0.0; + } - inline double cellCenter(unsigned int idx) const CADET_NOEXCEPT { return static_cast(_colLength) / _nCol * (idx + 0.5); } - inline double relativeCoordinate(unsigned int idx) const CADET_NOEXCEPT { return (0.5 + idx) / _nCol; } - inline const active& currentVelocity() const CADET_NOEXCEPT { return _curVelocity; } - inline const active* currentDispersion(const int secIdx) const CADET_NOEXCEPT { return getSectionDependentSlice(_colDispersion, _nComp, secIdx); } // todo delete once DG has its own operator - inline const bool dispersionCompIndep() const CADET_NOEXCEPT { return _dispersionCompIndep; } // todo delete once DG has its own operator + inline double cellCenter(unsigned int idx) const CADET_NOEXCEPT + { + return static_cast(_colLength) / _nCol * (idx + 0.5); + } + inline double relativeCoordinate(unsigned int idx) const CADET_NOEXCEPT + { + return (0.5 + idx) / _nCol; + } + inline const active& currentVelocity() const CADET_NOEXCEPT + { + return _curVelocity; + } + inline const active* currentDispersion(const int secIdx) const CADET_NOEXCEPT + { + return getSectionDependentSlice(_colDispersion, _nComp, secIdx); + } // todo delete once DG has its own operator + inline const bool dispersionCompIndep() const CADET_NOEXCEPT + { + return _dispersionCompIndep; + } // todo delete once DG has its own operator - inline unsigned int nComp() const CADET_NOEXCEPT { return _nComp; } - inline unsigned int nCol() const CADET_NOEXCEPT { return _nCol; } - inline const Weno& weno() const CADET_NOEXCEPT { return _weno; } + inline unsigned int nComp() const CADET_NOEXCEPT + { + return _nComp; + } + inline unsigned int nCol() const CADET_NOEXCEPT + { + return _nCol; + } + inline const Weno& weno() const CADET_NOEXCEPT + { + return _weno; + } unsigned int jacobianLowerBandwidth() const CADET_NOEXCEPT; unsigned int jacobianUpperBandwidth() const CADET_NOEXCEPT; @@ -112,31 +158,33 @@ class AxialConvectionDispersionOperatorBase double inletJacobianFactor() const CADET_NOEXCEPT; bool setParameter(const ParameterId& pId, double value); - bool setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, unsigned int adDirection, double adValue); + bool setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, + unsigned int adDirection, double adValue); bool setSensitiveParameterValue(const std::unordered_set& sensParams, const ParameterId& id, double value); protected: + template + int residualImpl(const IModel& model, double t, unsigned int secIdx, StateType const* y, double const* yDot, + ResidualType* res, RowIteratorType jacBegin); - template - int residualImpl(const IModel& model, double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, RowIteratorType jacBegin); - - unsigned int _nComp; //!< Number of components - unsigned int _nCol; //!< Number of axial cells + unsigned int _nComp; //!< Number of components + unsigned int _nCol; //!< Number of axial cells unsigned int _strideCell; //!< Number of elements between the same item in two adjacent cells - active _colLength; //!< Column length \f$ L \f$ - active _crossSection; //!< Cross section area + active _colLength; //!< Column length \f$ L \f$ + active _crossSection; //!< Cross section area // Section dependent parameters std::vector _colDispersion; //!< Column dispersion (may be section dependent) \f$ D_{\text{ax}} \f$ - std::vector _velocity; //!< Interstitial velocity (may be section dependent) \f$ u \f$ - active _curVelocity; //!< Current interstitial velocity \f$ u \f$ in this time section - int _dir; //!< Current flow direction in this time section + std::vector _velocity; //!< Interstitial velocity (may be section dependent) \f$ u \f$ + active _curVelocity; //!< Current interstitial velocity \f$ u \f$ in this time section + int _dir; //!< Current flow direction in this time section ArrayPool _stencilMemory; //!< Provides memory for the stencil double* _wenoDerivatives; //!< Holds derivatives of the WENO scheme - Weno _weno; //!< The WENO scheme implementation - double _wenoEpsilon; //!< The @f$ \varepsilon @f$ of the WENO scheme (prevents division by zero) + Weno _weno; //!< The WENO scheme implementation + double _wenoEpsilon; //!< The @f$ \varepsilon @f$ of the WENO scheme (prevents division by zero) bool _dispersionCompIndep; //!< Determines whether dispersion is component independent @@ -145,28 +193,37 @@ class AxialConvectionDispersionOperatorBase // Indexer functionality // Strides - inline int strideColCell() const CADET_NOEXCEPT { return static_cast(_strideCell); } - inline int strideColComp() const CADET_NOEXCEPT { return 1; } + inline int strideColCell() const CADET_NOEXCEPT + { + return static_cast(_strideCell); + } + inline int strideColComp() const CADET_NOEXCEPT + { + return 1; + } // Offsets - inline int offsetC() const CADET_NOEXCEPT { return _nComp; } + inline int offsetC() const CADET_NOEXCEPT + { + return _nComp; + } }; - /** * @brief Convection dispersion transport operator * @details Implements the equation - * + * * @f[\begin{align} - \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 c_i}{\partial z^2} \\ -\end{align} @f] + \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 +c_i}{\partial z^2} \\ \end{align} @f] * with Danckwerts boundary conditions (see @cite Danckwerts1953) @f[ \begin{align} u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partial z}(t,0) \\ \frac{\partial c_i}{\partial z}(t,L) &= 0 \end{align} @f] - * Methods are described in @cite VonLieres2010a (WENO, linear solver), and @cite Puttmann2013, @cite Puttmann2016 (forward sensitivities, AD, band compression) - * + * Methods are described in @cite VonLieres2010a (WENO, linear solver), and @cite Puttmann2013, @cite Puttmann2016 +(forward sensitivities, AD, band compression) + * * This class does not store the Jacobian. It only fills existing matrices given to its residual() functions. * It assumes that there is no offset to the inlet in the local state vector and that the firsts cell is placed * directly after the inlet DOFs. @@ -174,41 +231,74 @@ u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partia class RadialConvectionDispersionOperatorBase { public: - RadialConvectionDispersionOperatorBase(); ~RadialConvectionDispersionOperatorBase() CADET_NOEXCEPT; void setFlowRates(const active& in, const active& out, const active& colPorosity) CADET_NOEXCEPT; - bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, unsigned int nCol, unsigned int strideCell); - bool configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, std::unordered_map& parameters); + bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, + unsigned int nComp, unsigned int nCol, unsigned int strideCell); + bool configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, + std::unordered_map& parameters); bool notifyDiscontinuousSectionTransition(double t, unsigned int secIdx); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, linalg::BandMatrix& jac); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, linalg::BandMatrix& jac); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, WithoutParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, WithParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, WithParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, WithoutParamSensitivity); - - int jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, linalg::BandMatrix& jac); - int jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, linalg::BandMatrix& jac); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, + linalg::BandMatrix& jac); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, + linalg::BandMatrix& jac); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, + WithoutParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, + WithParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, + WithParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, + WithoutParamSensitivity); + + int jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, + linalg::BandMatrix& jac); + int jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, + linalg::BandMatrix& jac); void multiplyWithDerivativeJacobian(const SimulationTime& simTime, double const* sDot, double* ret) const; void addTimeDerivativeToJacobian(double alpha, linalg::FactorizableBandMatrix& jacDisc); - inline const active& columnLength() const CADET_NOEXCEPT { return _colLength; } - inline const active& innerRadius() const CADET_NOEXCEPT { return _innerRadius; } - inline const active& outerRadius() const CADET_NOEXCEPT { return _outerRadius; } + inline const active& columnLength() const CADET_NOEXCEPT + { + return _colLength; + } + inline const active& innerRadius() const CADET_NOEXCEPT + { + return _innerRadius; + } + inline const active& outerRadius() const CADET_NOEXCEPT + { + return _outerRadius; + } active currentVelocity(double pos) const CADET_NOEXCEPT; - inline bool forwardFlow() const CADET_NOEXCEPT { return _curVelocity >= 0.0; } + inline bool forwardFlow() const CADET_NOEXCEPT + { + return _curVelocity >= 0.0; + } - inline double cellCenter(unsigned int idx) const CADET_NOEXCEPT { return static_cast(_cellCenters[idx]); } - inline double relativeCoordinate(unsigned int idx) const CADET_NOEXCEPT { return (0.5 + idx) / _nCol; } + inline double cellCenter(unsigned int idx) const CADET_NOEXCEPT + { + return static_cast(_cellCenters[idx]); + } + inline double relativeCoordinate(unsigned int idx) const CADET_NOEXCEPT + { + return (0.5 + idx) / _nCol; + } - inline unsigned int nComp() const CADET_NOEXCEPT { return _nComp; } - inline unsigned int nCol() const CADET_NOEXCEPT { return _nCol; } -// inline const Weno& weno() const CADET_NOEXCEPT { return _weno; } + inline unsigned int nComp() const CADET_NOEXCEPT + { + return _nComp; + } + inline unsigned int nCol() const CADET_NOEXCEPT + { + return _nCol; + } + // inline const Weno& weno() const CADET_NOEXCEPT { return _weno; } unsigned int jacobianLowerBandwidth() const CADET_NOEXCEPT; unsigned int jacobianUpperBandwidth() const CADET_NOEXCEPT; @@ -216,34 +306,36 @@ class RadialConvectionDispersionOperatorBase double inletJacobianFactor() const CADET_NOEXCEPT; bool setParameter(const ParameterId& pId, double value); - bool setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, unsigned int adDirection, double adValue); + bool setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, + unsigned int adDirection, double adValue); bool setSensitiveParameterValue(const std::unordered_set& sensParams, const ParameterId& id, double value); protected: - - template - int residualImpl(const IModel& model, double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, RowIteratorType jacBegin); + template + int residualImpl(const IModel& model, double t, unsigned int secIdx, StateType const* y, double const* yDot, + ResidualType* res, RowIteratorType jacBegin); void equidistantCells(); - unsigned int _nComp; //!< Number of components - unsigned int _nCol; //!< Number of axial cells + unsigned int _nComp; //!< Number of components + unsigned int _nCol; //!< Number of axial cells unsigned int _strideCell; //!< Number of elements between the same item in two adjacent cells - active _colLength; //!< Column length \f$ L \f$ + active _colLength; //!< Column length \f$ L \f$ active _innerRadius; //!< Inner radius active _outerRadius; //!< Outer radius // Section dependent parameters std::vector _colDispersion; //!< Column dispersion (may be section dependent) \f$ D_{\text{rad}} \f$ - std::vector _velocity; //!< Radial velocity (may be section dependent) \f$ v \f$ - active _curVelocity; //!< Current interstitial velocity \f$ u \f$ in this time section - int _dir; //!< Current flow direction in this time section + std::vector _velocity; //!< Radial velocity (may be section dependent) \f$ v \f$ + active _curVelocity; //!< Current interstitial velocity \f$ u \f$ in this time section + int _dir; //!< Current flow direction in this time section ArrayPool _stencilMemory; //!< Provides memory for the stencil -// double* _wenoDerivatives; //!< Holds derivatives of the WENO scheme -// Weno _weno; //!< The WENO scheme implementation -// double _wenoEpsilon; //!< The @f$ \varepsilon @f$ of the WENO scheme (prevents division by zero) + // double* _wenoDerivatives; //!< Holds derivatives of the WENO scheme + // Weno _weno; //!< The WENO scheme implementation + // double _wenoEpsilon; //!< The @f$ \varepsilon @f$ of the WENO scheme (prevents division by zero) bool _dispersionCompIndep; //!< Determines whether dispersion is component independent @@ -257,27 +349,33 @@ class RadialConvectionDispersionOperatorBase // Indexer functionality // Strides - inline int strideColCell() const CADET_NOEXCEPT { return static_cast(_strideCell); } - inline int strideColComp() const CADET_NOEXCEPT { return 1; } + inline int strideColCell() const CADET_NOEXCEPT + { + return static_cast(_strideCell); + } + inline int strideColComp() const CADET_NOEXCEPT + { + return 1; + } // Offsets - inline int offsetC() const CADET_NOEXCEPT { return _nComp; } + inline int offsetC() const CADET_NOEXCEPT + { + return _nComp; + } }; - /** * @brief Convection dispersion transport operator * @details This class wraps AxialConvectionDispersionOperatorBase or RadialConvectionDispersionOperatorBase * and provides all the functionality it does. In addition, the Jacobian is stored and corresponding functions * are provided (assembly, factorization, solution, retrieval). - * + * * This class assumes that the first cell is offset by the number of components (inlet DOFs) in the global state vector. */ -template -class ConvectionDispersionOperator +template class ConvectionDispersionOperator { public: - ConvectionDispersionOperator(); ~ConvectionDispersionOperator() CADET_NOEXCEPT; @@ -285,14 +383,20 @@ class ConvectionDispersionOperator void setFlowRates(const active& in, const active& out, const active& colPorosity) CADET_NOEXCEPT; - bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, unsigned int nCol); - bool configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, std::unordered_map& parameters); + bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, + unsigned int nComp, unsigned int nCol); + bool configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, + std::unordered_map& parameters); bool notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const AdJacobianParams& adJac); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, bool wantJac, WithoutParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithoutParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, + bool wantJac, WithoutParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, + bool wantJac, WithParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, + bool wantJac, WithoutParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, + bool wantJac, WithParamSensitivity); int jacobian(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res); int jacobian(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res); @@ -310,47 +414,81 @@ class ConvectionDispersionOperator double checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const; #endif - inline const active& columnLength() const CADET_NOEXCEPT { return _baseOp.columnLength(); } - inline active currentVelocity(double pos) const CADET_NOEXCEPT { return _baseOp.currentVelocity(pos); } - inline bool forwardFlow() const CADET_NOEXCEPT { return _baseOp.forwardFlow(); } - inline double inletJacobianFactor() const CADET_NOEXCEPT { return _baseOp.inletJacobianFactor(); } + inline const active& columnLength() const CADET_NOEXCEPT + { + return _baseOp.columnLength(); + } + inline active currentVelocity(double pos) const CADET_NOEXCEPT + { + return _baseOp.currentVelocity(pos); + } + inline bool forwardFlow() const CADET_NOEXCEPT + { + return _baseOp.forwardFlow(); + } + inline double inletJacobianFactor() const CADET_NOEXCEPT + { + return _baseOp.inletJacobianFactor(); + } - inline double cellCenter(unsigned int idx) const CADET_NOEXCEPT { return _baseOp.cellCenter(idx); } - inline double relativeCoordinate(unsigned int idx) const CADET_NOEXCEPT { return _baseOp.relativeCoordinate(idx); } + inline double cellCenter(unsigned int idx) const CADET_NOEXCEPT + { + return _baseOp.cellCenter(idx); + } + inline double relativeCoordinate(unsigned int idx) const CADET_NOEXCEPT + { + return _baseOp.relativeCoordinate(idx); + } - inline linalg::BandMatrix& jacobian() CADET_NOEXCEPT { return _jacC; } - inline const linalg::BandMatrix& jacobian() const CADET_NOEXCEPT { return _jacC; } + inline linalg::BandMatrix& jacobian() CADET_NOEXCEPT + { + return _jacC; + } + inline const linalg::BandMatrix& jacobian() const CADET_NOEXCEPT + { + return _jacC; + } - inline linalg::FactorizableBandMatrix& jacobianDisc() CADET_NOEXCEPT { return _jacCdisc; } - inline const linalg::FactorizableBandMatrix& jacobianDisc() const CADET_NOEXCEPT { return _jacCdisc; } + inline linalg::FactorizableBandMatrix& jacobianDisc() CADET_NOEXCEPT + { + return _jacCdisc; + } + inline const linalg::FactorizableBandMatrix& jacobianDisc() const CADET_NOEXCEPT + { + return _jacCdisc; + } inline bool setParameter(const ParameterId& pId, double value) { return _baseOp.setParameter(pId, value); } - inline bool setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, unsigned int adDirection, double adValue) + inline bool setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, + unsigned int adDirection, double adValue) { return _baseOp.setSensitiveParameter(sensParams, pId, adDirection, adValue); } - inline bool setSensitiveParameterValue(const std::unordered_set& sensParams, const ParameterId& id, double value) + inline bool setSensitiveParameterValue(const std::unordered_set& sensParams, const ParameterId& id, + double value) { return _baseOp.setSensitiveParameterValue(sensParams, id, value); } protected: - void addTimeDerivativeToJacobian(double alpha); void assembleDiscretizedJacobian(double alpha); BaseOperator _baseOp; - linalg::BandMatrix _jacC; //!< Jacobian + linalg::BandMatrix _jacC; //!< Jacobian linalg::FactorizableBandMatrix _jacCdisc; //!< Jacobian with time derivatives from BDF method // Indexer functionality // Offsets - inline int offsetC() const CADET_NOEXCEPT { return _baseOp.nComp(); } + inline int offsetC() const CADET_NOEXCEPT + { + return _baseOp.nComp(); + } }; extern template class ConvectionDispersionOperator; @@ -363,4 +501,4 @@ typedef ConvectionDispersionOperator Rad } // namespace model } // namespace cadet -#endif // LIBCADET_CONVECTIONDISPERSIONOPERATOR_HPP_ +#endif // LIBCADET_CONVECTIONDISPERSIONOPERATOR_HPP_ diff --git a/src/libcadet/model/parts/ConvectionDispersionOperatorDG.cpp b/src/libcadet/model/parts/ConvectionDispersionOperatorDG.cpp index d07ba650d..00f567b02 100644 --- a/src/libcadet/model/parts/ConvectionDispersionOperatorDG.cpp +++ b/src/libcadet/model/parts/ConvectionDispersionOperatorDG.cpp @@ -17,8 +17,8 @@ #include "ParamReaderHelper.hpp" #include "AdUtils.hpp" #include "SimulationTypes.hpp" -//#include "model/parts/AxialConvectionDispersionKernelDG.hpp" // todo radial flow DG and outsource residual implementation to kernel -//#include "model/parts/RadialConvectionDispersionKernelDG.hpp" +// #include "model/parts/AxialConvectionDispersionKernelDG.hpp" // todo radial flow DG and outsource residual +// implementation to kernel #include "model/parts/RadialConvectionDispersionKernelDG.hpp" #include "model/ParameterDependence.hpp" #include "SensParamUtil.hpp" #include "ConfigurationHelper.hpp" @@ -43,8 +43,8 @@ namespace parts /** * @brief Creates an AxialConvectionDispersionOperatorBaseDG */ -AxialConvectionDispersionOperatorBaseDG::AxialConvectionDispersionOperatorBaseDG() : - _dispersionDep(nullptr), _DGjacAxDispBlocks(nullptr) +AxialConvectionDispersionOperatorBaseDG::AxialConvectionDispersionOperatorBaseDG() + : _dispersionDep(nullptr), _DGjacAxDispBlocks(nullptr) { } @@ -70,11 +70,14 @@ AxialConvectionDispersionOperatorBaseDG::~AxialConvectionDispersionOperatorBaseD * @param [in] strideNode node stride in state vector * @return @c true if configuration went fine, @c false otherwise */ -bool AxialConvectionDispersionOperatorBaseDG::configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, int polynomial_integration_mode, unsigned int nCells, unsigned int polyDeg, unsigned int strideNode) +bool AxialConvectionDispersionOperatorBaseDG::configureModelDiscretization( + IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, int polynomial_integration_mode, + unsigned int nCells, unsigned int polyDeg, unsigned int strideNode) { _nComp = nComp; - _exactInt = static_cast(polynomial_integration_mode); // only integration mode 0 applies the inexact collocated diagonal LGL mass matrix + _exactInt = static_cast( + polynomial_integration_mode); // only integration mode 0 applies the inexact collocated diagonal LGL mass matrix _nCells = nCells; _polyDeg = polyDeg; _nNodes = _polyDeg + 1u; @@ -94,7 +97,8 @@ bool AxialConvectionDispersionOperatorBaseDG::configureModelDiscretization(IPara _auxState = new active[_nPoints]; _subsState = new active[_nPoints]; - for (int i = 0; i < _nPoints; i++) { + for (int i = 0; i < _nPoints; i++) + { _auxState[i] = 0.0; _subsState[i] = 0.0; } @@ -108,7 +112,7 @@ bool AxialConvectionDispersionOperatorBaseDG::configureModelDiscretization(IPara _invMM = dgtoolbox::invMMatrix(_polyDeg, _nodes); _polyDerM = dgtoolbox::derivativeMatrix(_polyDeg, _nodes); - if(polynomial_integration_mode == 2) // use Gauss quadrature for exact integration + if (polynomial_integration_mode == 2) // use Gauss quadrature for exact integration _invMM = dgtoolbox::gaussQuadratureMMatrix(_nodes, _nNodes).inverse(); if (paramProvider.exists("COL_DISPERSION_DEP")) @@ -134,14 +138,16 @@ bool AxialConvectionDispersionOperatorBaseDG::configureModelDiscretization(IPara * @param [out] parameters Map in which local parameters are inserted * @return @c true if configuration went fine, @c false otherwise */ -bool AxialConvectionDispersionOperatorBaseDG::configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, std::unordered_map& parameters) +bool AxialConvectionDispersionOperatorBaseDG::configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, + std::unordered_map& parameters) { // Read geometry parameters _colLength = paramProvider.getDouble("COL_LENGTH"); _deltaZ = _colLength / _nCells; /* compute dispersion jacobian blocks(without parameters except element spacing, i.e. static entries) */ - // we only need unique dispersion blocks, which are given by cells 1, 2, nCol for inexact integration DG and by cells 1, 2, 3, nCol-1, nCol for eaxct integration DG + // we only need unique dispersion blocks, which are given by cells 1, 2, nCol for inexact integration DG and by + // cells 1, 2, 3, nCol-1, nCol for eaxct integration DG _DGjacAxDispBlocks = new Eigen::MatrixXd[(_exactInt ? std::min(_nCells, 5u) : std::min(_nCells, 3u))]; _DGjacAxDispBlocks[0] = DGjacobianDispBlock(1); if (_nCells > 1) @@ -155,7 +161,8 @@ bool AxialConvectionDispersionOperatorBaseDG::configure(UnitOpIdx unitOpIdx, IPa if (_exactInt && _nCells > 4) _DGjacAxDispBlocks[4] = DGjacobianDispBlock(_nCells); - // note that convection jacobian block is computet in notifyDiscontinuousSectionTransition() since this block needs to be recomputed when flow direction changes + // note that convection jacobian block is computet in notifyDiscontinuousSectionTransition() since this block needs + // to be recomputed when flow direction changes // Read cross section area or set to -1 _crossSection = -1.0; @@ -192,11 +199,16 @@ bool AxialConvectionDispersionOperatorBaseDG::configure(UnitOpIdx unitOpIdx, IPa _dispersionCompIndep = false; if (!_dispersionCompIndep && (_colDispersion.size() % _nComp != 0)) - throw InvalidParameterException("Number of elements in field COL_DISPERSION is not a positive multiple of NCOMP (" + std::to_string(_nComp) + ")"); + throw InvalidParameterException( + "Number of elements in field COL_DISPERSION is not a positive multiple of NCOMP (" + + std::to_string(_nComp) + ")"); if ((mode == 0) && (_colDispersion.size() != 1)) - throw InvalidParameterException("Number of elements in field COL_DISPERSION inconsistent with COL_DISPERSION_MULTIPLEX (should be 1)"); + throw InvalidParameterException( + "Number of elements in field COL_DISPERSION inconsistent with COL_DISPERSION_MULTIPLEX (should be 1)"); if ((mode == 1) && (_colDispersion.size() != _nComp)) - throw InvalidParameterException("Number of elements in field COL_DISPERSION inconsistent with COL_DISPERSION_MULTIPLEX (should be " + std::to_string(_nComp) + ")"); + throw InvalidParameterException( + "Number of elements in field COL_DISPERSION inconsistent with COL_DISPERSION_MULTIPLEX (should be " + + std::to_string(_nComp) + ")"); } else { @@ -233,20 +245,30 @@ bool AxialConvectionDispersionOperatorBaseDG::configure(UnitOpIdx unitOpIdx, IPa { // Register only the first item in each section for (std::size_t i = 0; i < _colDispersion.size() / _nComp; ++i) - parameters[makeParamId(hashString("COL_DISPERSION"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, i)] = &_colDispersion[i * _nComp]; + parameters[makeParamId(hashString("COL_DISPERSION"), unitOpIdx, CompIndep, ParTypeIndep, + BoundStateIndep, ReactionIndep, i)] = &_colDispersion[i * _nComp]; } else { // We have only one parameter - parameters[makeParamId(hashString("COL_DISPERSION"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_colDispersion[0]; + parameters[makeParamId(hashString("COL_DISPERSION"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_colDispersion[0]; } } else - registerParam2DArray(parameters, _colDispersion, [=](bool multi, unsigned int sec, unsigned int comp) { return makeParamId(hashString("COL_DISPERSION"), unitOpIdx, comp, ParTypeIndep, BoundStateIndep, ReactionIndep, multi ? sec : SectionIndep); }, _nComp); + registerParam2DArray( + parameters, _colDispersion, + [=](bool multi, unsigned int sec, unsigned int comp) { + return makeParamId(hashString("COL_DISPERSION"), unitOpIdx, comp, ParTypeIndep, BoundStateIndep, + ReactionIndep, multi ? sec : SectionIndep); + }, + _nComp); registerScalarSectionDependentParam(hashString("VELOCITY"), parameters, _velocity, unitOpIdx, ParTypeIndep); - parameters[makeParamId(hashString("COL_LENGTH"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_colLength; - parameters[makeParamId(hashString("CROSS_SECTION_AREA"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_crossSection; + parameters[makeParamId(hashString("COL_LENGTH"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep)] = &_colLength; + parameters[makeParamId(hashString("CROSS_SECTION_AREA"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_crossSection; return true; } @@ -259,7 +281,8 @@ bool AxialConvectionDispersionOperatorBaseDG::configure(UnitOpIdx unitOpIdx, IPa * @param [in] secIdx Index of the new section that is about to be integrated * @return @c true if flow direction has changed, otherwise @c false */ -bool AxialConvectionDispersionOperatorBaseDG::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, MatrixXd& jacInlet) +bool AxialConvectionDispersionOperatorBaseDG::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + MatrixXd& jacInlet) { _curSection = secIdx; @@ -289,22 +312,31 @@ bool AxialConvectionDispersionOperatorBaseDG::notifyDiscontinuousSectionTransiti // Direction never changes (always forward, that is, _dir = 1)- // No action required. - // recompute convection jacobian block, which depends on flow direction + // recompute convection jacobian block, which depends on flow direction _DGjacAxConvBlock = DGjacobianConvBlock(); - if (_curVelocity >= 0.0) { // forward flow upwind convection + if (_curVelocity >= 0.0) + { // forward flow upwind convection if (_exactInt) - jacInlet = static_cast(_curVelocity) * _DGjacAxConvBlock.col(0); // only first cell depends on inlet concentration + jacInlet = static_cast(_curVelocity) * + _DGjacAxConvBlock.col(0); // only first cell depends on inlet concentration else - jacInlet(0, 0) = static_cast(_curVelocity) * _DGjacAxConvBlock(0, 0); // only first node depends on inlet concentration + jacInlet(0, 0) = static_cast(_curVelocity) * + _DGjacAxConvBlock(0, 0); // only first node depends on inlet concentration } - else { // backward flow upwind convection + else + { // backward flow upwind convection if (_exactInt) - jacInlet = static_cast(_curVelocity) * _DGjacAxConvBlock.col(_DGjacAxConvBlock.cols() - 1); // only last cell depends on inlet concentration + jacInlet = + static_cast(_curVelocity) * + _DGjacAxConvBlock.col(_DGjacAxConvBlock.cols() - 1); // only last cell depends on inlet concentration else - jacInlet(0, 0) = static_cast(_curVelocity) * _DGjacAxConvBlock(_DGjacAxConvBlock.rows() - 1, _DGjacAxConvBlock.cols() - 1); // only last node depends on inlet concentration + jacInlet(0, 0) = + static_cast(_curVelocity) * + _DGjacAxConvBlock(_DGjacAxConvBlock.rows() - 1, + _DGjacAxConvBlock.cols() - 1); // only last node depends on inlet concentration } - + // Detect change in flow direction return (dirOld * _dir < 0); } @@ -316,7 +348,8 @@ bool AxialConvectionDispersionOperatorBaseDG::notifyDiscontinuousSectionTransiti * @param [in] out Total volumetric outlet flow rate * @param [in] colPorosity Porosity used for computing interstitial velocity from volumetric flow rate */ -void AxialConvectionDispersionOperatorBaseDG::setFlowRates(const active& in, const active& out, const active& colPorosity) CADET_NOEXCEPT +void AxialConvectionDispersionOperatorBaseDG::setFlowRates(const active& in, const active& out, + const active& colPorosity) CADET_NOEXCEPT { // If we have cross section area, interstitial velocity is given by network flow rates if (_crossSection > 0.0) @@ -334,66 +367,92 @@ void AxialConvectionDispersionOperatorBaseDG::setFlowRates(const active& in, con * @param [in] jac Matrix that holds the Jacobian * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ -int AxialConvectionDispersionOperatorBaseDG::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, Eigen::SparseMatrix& jac) +int AxialConvectionDispersionOperatorBaseDG::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, double* res, + Eigen::SparseMatrix& jac) { //// Reset Jacobian but keep pattern - //double* val = jac.valuePtr(); - //for (unsigned int entry = 0; entry < jac.nonZeros(); val++) + // double* val = jac.valuePtr(); + // for (unsigned int entry = 0; entry < jac.nonZeros(); val++) // val[0] = 0.0; linalg::BandedEigenSparseRowIterator jacIt(jac, 0); - return residualImpl(model, t, secIdx, y, yDot, res, jacIt); + return residualImpl(model, t, secIdx, y, yDot, + res, jacIt); } -int AxialConvectionDispersionOperatorBaseDG::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, WithoutParamSensitivity) +int AxialConvectionDispersionOperatorBaseDG::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, double* res, + WithoutParamSensitivity) { - return residualImpl(model, t, secIdx, y, yDot, res, linalg::BandedEigenSparseRowIterator()); + return residualImpl( + model, t, secIdx, y, yDot, res, linalg::BandedEigenSparseRowIterator()); } -int AxialConvectionDispersionOperatorBaseDG::residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, WithoutParamSensitivity) +int AxialConvectionDispersionOperatorBaseDG::residual(const IModel& model, double t, unsigned int secIdx, + active const* y, double const* yDot, active* res, + WithoutParamSensitivity) { - return residualImpl(model, t, secIdx, y, yDot, res, linalg::BandedEigenSparseRowIterator()); + return residualImpl( + model, t, secIdx, y, yDot, res, linalg::BandedEigenSparseRowIterator()); } -int AxialConvectionDispersionOperatorBaseDG::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, Eigen::SparseMatrix& jac) +int AxialConvectionDispersionOperatorBaseDG::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, active* res, + Eigen::SparseMatrix& jac) { // todo include jacobian in convDisp operator //// Reset Jacobian but keep pattern - //double* val = jac.valuePtr(); - //for (unsigned int entry = 0; entry < jac.nonZeros(); val++) + // double* val = jac.valuePtr(); + // for (unsigned int entry = 0; entry < jac.nonZeros(); val++) // val[0] = 0.0; linalg::BandedEigenSparseRowIterator jacIt(jac, 0); - return residualImpl(model, t, secIdx, y, yDot, res, jacIt); + return residualImpl(model, t, secIdx, y, yDot, + res, jacIt); } -int AxialConvectionDispersionOperatorBaseDG::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, WithParamSensitivity) +int AxialConvectionDispersionOperatorBaseDG::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, active* res, + WithParamSensitivity) { - return residualImpl(model, t, secIdx, y, yDot, res, linalg::BandedEigenSparseRowIterator()); + return residualImpl( + model, t, secIdx, y, yDot, res, linalg::BandedEigenSparseRowIterator()); } -int AxialConvectionDispersionOperatorBaseDG::residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, WithParamSensitivity) +int AxialConvectionDispersionOperatorBaseDG::residual(const IModel& model, double t, unsigned int secIdx, + active const* y, double const* yDot, active* res, + WithParamSensitivity) { - return residualImpl(model, t, secIdx, y, yDot, res, linalg::BandedEigenSparseRowIterator()); + return residualImpl( + model, t, secIdx, y, yDot, res, linalg::BandedEigenSparseRowIterator()); } template -int AxialConvectionDispersionOperatorBaseDG::residualImpl(const IModel& model, double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, RowIteratorType jacBegin) +int AxialConvectionDispersionOperatorBaseDG::residualImpl(const IModel& model, double t, unsigned int secIdx, + StateType const* y, double const* yDot, ResidualType* res, + RowIteratorType jacBegin) { - for (unsigned int comp = 0; comp < _nComp; comp++) { + for (unsigned int comp = 0; comp < _nComp; comp++) + { // create Eigen objects - Eigen::Map, 0, InnerStride> _C(y + offsetC() + comp, _nPoints, InnerStride(_strideNode)); - Eigen::Map, 0, InnerStride> _resC(res + offsetC() + comp, _nPoints, InnerStride(_strideNode)); - Eigen::Map, 0, InnerStride<>> _h(reinterpret_cast(_subsState), _nPoints, InnerStride<>(1)); - Eigen::Map, 0, InnerStride<>> _g(reinterpret_cast(_auxState), _nPoints, InnerStride<>(1)); + Eigen::Map, 0, InnerStride> _C(y + offsetC() + comp, _nPoints, + InnerStride(_strideNode)); + Eigen::Map, 0, InnerStride> _resC(res + offsetC() + comp, _nPoints, + InnerStride(_strideNode)); + Eigen::Map, 0, InnerStride<>> _h(reinterpret_cast(_subsState), + _nPoints, InnerStride<>(1)); + Eigen::Map, 0, InnerStride<>> _g(reinterpret_cast(_auxState), _nPoints, + InnerStride<>(1)); // Add time derivative to bulk residual if (yDot) { - Eigen::Map> _Cdot(yDot + offsetC() + comp, _nPoints, InnerStride(_strideNode)); + Eigen::Map> _Cdot(yDot + offsetC() + comp, _nPoints, + InnerStride(_strideNode)); _resC = _Cdot.template cast(); } else @@ -414,7 +473,7 @@ int AxialConvectionDispersionOperatorBaseDG::residualImpl(const IModel& model, d // solve auxiliary system g = d c / d x // // ======================================// - // DG volume integral in strong form + // DG volume integral in strong form volumeIntegral(_C, _g); // calculate numerical flux values c* @@ -428,7 +487,8 @@ int AxialConvectionDispersionOperatorBaseDG::residualImpl(const IModel& model, d // ======================================// // calculate the substitute h(g(c), c) and apply inverse mapping jacobian (reference space) - _h = 2.0 / static_cast(_deltaZ) * (-u * _C + d_ax * (-2.0 / static_cast(_deltaZ)) * _g).template cast(); + _h = 2.0 / static_cast(_deltaZ) * + (-u * _C + d_ax * (-2.0 / static_cast(_deltaZ)) * _g).template cast(); // DG volume integral in strong form volumeIntegral(_h, _resC); @@ -440,17 +500,19 @@ int AxialConvectionDispersionOperatorBaseDG::residualImpl(const IModel& model, d InterfaceFlux(y + offsetC() + comp, d_ax); // DG surface integral in strong form - surfaceIntegral(&_h[0], res + offsetC() + comp, - 1u, _nNodes, _strideNode, _strideCell); + surfaceIntegral(&_h[0], res + offsetC() + comp, 1u, _nNodes, _strideNode, + _strideCell); } return 0; } /** -* @brief analytically calculates the (static) state jacobian -* @return 1 if jacobain estimation fits the predefined pattern of the jacobian, 0 if not. -*/ -int AxialConvectionDispersionOperatorBaseDG::calcStaticAnaJacobian(Eigen::SparseMatrix& jacobian, Eigen::MatrixXd& jacInlet, const int bulkOffset) { + * @brief analytically calculates the (static) state jacobian + * @return 1 if jacobain estimation fits the predefined pattern of the jacobian, 0 if not. + */ +int AxialConvectionDispersionOperatorBaseDG::calcStaticAnaJacobian(Eigen::SparseMatrix& jacobian, + Eigen::MatrixXd& jacInlet, const int bulkOffset) +{ // DG convection dispersion Jacobian if (_exactInt) @@ -467,24 +529,32 @@ int AxialConvectionDispersionOperatorBaseDG::calcStaticAnaJacobian(Eigen::Sparse * @brief calculates the number of entris for the DG convection dispersion jacobian * @note only dispersion entries are relevant for jacobian NNZ as the convection entries are a subset of these */ -unsigned int AxialConvectionDispersionOperatorBaseDG::nConvDispEntries(bool pureNNZ) { +unsigned int AxialConvectionDispersionOperatorBaseDG::nConvDispEntries(bool pureNNZ) +{ - if (_exactInt) { - if (pureNNZ) { - return _nComp * ((3u * _nCells - 2u) * _nNodes * _nNodes + (2u * _nCells - 3u) * _nNodes); // dispersion entries + if (_exactInt) + { + if (pureNNZ) + { + return _nComp * + ((3u * _nCells - 2u) * _nNodes * _nNodes + (2u * _nCells - 3u) * _nNodes); // dispersion entries } return _nComp * _nNodes * _nNodes + _nNodes // convection entries - + _nComp * ((3u * _nCells - 2u) * _nNodes * _nNodes + (2u * _nCells - 3u) * _nNodes); // dispersion entries + + + _nComp * ((3u * _nCells - 2u) * _nNodes * _nNodes + (2u * _nCells - 3u) * _nNodes); // dispersion entries } - else { - if (pureNNZ) { + else + { + if (pureNNZ) + { return _nComp * (_nCells * _nNodes * _nNodes + 8u * _nNodes); // dispersion entries } - return _nComp * _nNodes * _nNodes + 1u // convection entries - + _nComp * (_nCells * _nNodes * _nNodes + 8u * _nNodes); // dispersion entries + return _nComp * _nNodes * _nNodes + 1u // convection entries + + _nComp * (_nCells * _nNodes * _nNodes + 8u * _nNodes); // dispersion entries } } -void model::parts::AxialConvectionDispersionOperatorBaseDG::convDispJacPattern(std::vector& tripletList, const int bulkOffset) +void model::parts::AxialConvectionDispersionOperatorBaseDG::convDispJacPattern(std::vector& tripletList, + const int bulkOffset) { if (_exactInt) ConvDispModalPattern(tripletList, bulkOffset); @@ -492,17 +562,20 @@ void model::parts::AxialConvectionDispersionOperatorBaseDG::convDispJacPattern(s ConvDispNodalPattern(tripletList, bulkOffset); } /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ + * with a given vector * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). - * + * * Note that this function only performs multiplication with the Jacobian of the (axial) transport equations. - * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit operation's arrays. + * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit + * operation's arrays. * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] sDot Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ * @param [out] ret Vector @f$ z @f$ which stores the result of the operation */ -void AxialConvectionDispersionOperatorBaseDG::multiplyWithDerivativeJacobian(const SimulationTime& simTime, double const* sDot, double* ret) const +void AxialConvectionDispersionOperatorBaseDG::multiplyWithDerivativeJacobian(const SimulationTime& simTime, + double const* sDot, double* ret) const { double* localRet = ret + offsetC(); double const* localSdot = sDot + offsetC(); @@ -519,18 +592,22 @@ void AxialConvectionDispersionOperatorBaseDG::multiplyWithDerivativeJacobian(con /** * @brief Adds the derivatives with respect to @f$ \dot{y} @f$ of @f$ F(t, y, \dot{y}) @f$ to the Jacobian - * @details This functions computes - * @f[ \begin{align*} \text{_jacCdisc} = \text{_jacCdisc} + \alpha \frac{\partial F}{\partial \dot{y}}. \end{align*} @f] - * The factor @f$ \alpha @f$ is useful when constructing the linear system in the time integration process. + * @details This functions computes + * @f[ \begin{align*} \text{_jacCdisc} = \text{_jacCdisc} + \alpha \frac{\partial F}{\partial \dot{y}}. + * \end{align*} @f] The factor @f$ \alpha @f$ is useful when constructing the linear system in the time integration + * process. * @param [in] alpha Factor in front of @f$ \frac{\partial F}{\partial \dot{y}} @f$ */ -void AxialConvectionDispersionOperatorBaseDG::addTimeDerivativeToJacobian(double alpha, Eigen::SparseMatrix& jacDisc, unsigned int blockOffset) +void AxialConvectionDispersionOperatorBaseDG::addTimeDerivativeToJacobian( + double alpha, Eigen::SparseMatrix& jacDisc, unsigned int blockOffset) { const int gapCell = strideColNode() - static_cast(_nComp) * strideColComp(); linalg::BandedEigenSparseRowIterator jac(jacDisc, blockOffset); - for (unsigned int point = 0; point < _nPoints; ++point, jac+=gapCell) { - for (unsigned int comp = 0; comp < _nComp; ++comp, ++jac) { + for (unsigned int point = 0; point < _nPoints; ++point, jac += gapCell) + { + for (unsigned int comp = 0; comp < _nComp; ++comp, ++jac) + { // dc_b / dt in transport equation jac[0] += alpha; } @@ -539,11 +616,13 @@ void AxialConvectionDispersionOperatorBaseDG::addTimeDerivativeToJacobian(double unsigned int AxialConvectionDispersionOperatorBaseDG::jacobianLowerBandwidth() const CADET_NOEXCEPT { - // @todo use more efficient seed vectors. currently, we treat the jacobian as banded, but the pattern is actually more sparse when multiple components are considered - // (note that active type directions are limited) - // We have different jacobian structure for exact integration and collocation DG scheme, i.e. we need different seed vectors - // collocation DG: 2 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend on the next and last N_n liquid phase entries of same component) - // ex. int. DG: 4 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend on the next and last 2*N_n liquid phase entries of same component) + // @todo use more efficient seed vectors. currently, we treat the jacobian as banded, but the pattern is actually + // more sparse when multiple components are considered (note that active type directions are limited) We have + // different jacobian structure for exact integration and collocation DG scheme, i.e. we need different seed vectors + // collocation DG: 2 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend on the next + // and last N_n liquid phase entries of same component) + // ex. int. DG: 4 * N_n * (N_c + N_q) + 1 = total bandwidth (main diagonal entries maximally depend on the next + // and last 2*N_n liquid phase entries of same component) return (_exactInt) ? 2 * _nNodes * strideColNode() : _nNodes * strideColNode(); } @@ -569,7 +648,8 @@ bool AxialConvectionDispersionOperatorBaseDG::setParameter(const ParameterId& pI if (!_dispersionCompIndep) return false; - if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) + if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || + (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) return false; if (_colDispersion.size() > _nComp) @@ -594,7 +674,8 @@ bool AxialConvectionDispersionOperatorBaseDG::setParameter(const ParameterId& pI return true; } -bool AxialConvectionDispersionOperatorBaseDG::setSensitiveParameterValue(const std::unordered_set& sensParams, const ParameterId& pId, double value) +bool AxialConvectionDispersionOperatorBaseDG::setSensitiveParameterValue(const std::unordered_set& sensParams, + const ParameterId& pId, double value) { // Check if parameter is in parameter dependence of column dispersion coefficient if (_dispersionDep) @@ -611,7 +692,8 @@ bool AxialConvectionDispersionOperatorBaseDG::setSensitiveParameterValue(const s if (!_dispersionCompIndep) return false; - if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) + if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || + (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) return false; if (_colDispersion.size() > _nComp) @@ -642,7 +724,9 @@ bool AxialConvectionDispersionOperatorBaseDG::setSensitiveParameterValue(const s return true; } -bool AxialConvectionDispersionOperatorBaseDG::setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, unsigned int adDirection, double adValue) +bool AxialConvectionDispersionOperatorBaseDG::setSensitiveParameter(std::unordered_set& sensParams, + const ParameterId& pId, unsigned int adDirection, + double adValue) { // Check if parameter is in parameter dependence of column dispersion coefficient if (_dispersionDep) @@ -659,7 +743,8 @@ bool AxialConvectionDispersionOperatorBaseDG::setSensitiveParameter(std::unorder if (!_dispersionCompIndep) return false; - if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) + if ((pId.name != hashString("COL_DISPERSION")) || (pId.component != CompIndep) || + (pId.boundState != BoundStateIndep) || (pId.reaction != ReactionIndep) || (pId.particleType != ParTypeIndep)) return false; if (_colDispersion.size() > _nComp) @@ -691,13 +776,11 @@ bool AxialConvectionDispersionOperatorBaseDG::setSensitiveParameter(std::unorder /** * @brief Creates an ConvectionDispersionOperatorDG */ -template -ConvectionDispersionOperatorDG::ConvectionDispersionOperatorDG() +template ConvectionDispersionOperatorDG::ConvectionDispersionOperatorDG() { } -template -ConvectionDispersionOperatorDG::~ConvectionDispersionOperatorDG() CADET_NOEXCEPT +template ConvectionDispersionOperatorDG::~ConvectionDispersionOperatorDG() CADET_NOEXCEPT { } @@ -706,8 +789,7 @@ ConvectionDispersionOperatorDG::~ConvectionDispersionOperatorDG() CADE * @details Band compression is used to minimize the amount of AD directions. * @return Number of required AD directions */ -template -int ConvectionDispersionOperatorDG::requiredADdirs() const CADET_NOEXCEPT +template int ConvectionDispersionOperatorDG::requiredADdirs() const CADET_NOEXCEPT { return _baseOp.requiredADdirs(); } @@ -723,18 +805,22 @@ int ConvectionDispersionOperatorDG::requiredADdirs() const CADET_NOEXC * @return @c true if configuration went fine, @c false otherwise */ template -bool ConvectionDispersionOperatorDG::configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, int polynomial_integration_mode, unsigned int nCells, unsigned int polyDeg, unsigned int strideNode) +bool ConvectionDispersionOperatorDG::configureModelDiscretization( + IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, int polynomial_integration_mode, + unsigned int nCells, unsigned int polyDeg, unsigned int strideNode) { - const bool retVal = _baseOp.configureModelDiscretization(paramProvider, helper, nComp, polynomial_integration_mode, nCells, polyDeg, strideNode); + const bool retVal = _baseOp.configureModelDiscretization(paramProvider, helper, nComp, polynomial_integration_mode, + nCells, polyDeg, strideNode); // todo: manage jacobians in convDispOp instead of unitOp ? //// Allocate memory - //if (_disc.exactInt) + // if (_disc.exactInt) // _jacInlet.resize(_disc.nNodes, 1); // first cell depends on inlet concentration (same for every component) - //else + // else // _jacInlet.resize(1, 1); // first cell depends on inlet concentration (same for every component) //_jac.resize((_disc.nComp + _disc.strideBound) * _disc.nPoints, (_disc.nComp + _disc.strideBound) * _disc.nPoints); - //_jacDisc.resize((_disc.nComp + _disc.strideBound) * _disc.nPoints, (_disc.nComp + _disc.strideBound) * _disc.nPoints); + //_jacDisc.resize((_disc.nComp + _disc.strideBound) * _disc.nPoints, (_disc.nComp + _disc.strideBound) * + //_disc.nPoints); return retVal; } @@ -748,7 +834,8 @@ bool ConvectionDispersionOperatorDG::configureModelDiscretization(IPar * @return @c true if configuration went fine, @c false otherwise */ template -bool ConvectionDispersionOperatorDG::configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, std::unordered_map& parameters) +bool ConvectionDispersionOperatorDG::configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, + std::unordered_map& parameters) { return _baseOp.configure(unitOpIdx, paramProvider, parameters); } @@ -763,7 +850,8 @@ bool ConvectionDispersionOperatorDG::configure(UnitOpIdx unitOpIdx, IP * @return @c true if flow direction has changed, otherwise @c false */ template -bool ConvectionDispersionOperatorDG::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const AdJacobianParams& adJac) +bool ConvectionDispersionOperatorDG::notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const AdJacobianParams& adJac) { if (_baseOp.exactInt()) _jacInlet.resize(_baseOp.nNodes(), 1); // first cell depends on inlet concentration (same for every component) @@ -778,9 +866,9 @@ bool ConvectionDispersionOperatorDG::notifyDiscontinuousSectionTransit return false; // todo: manage jacobians in convDispOp instead of unitOp ? - //const unsigned int lb = _baseOp.jacobianLowerBandwidth(); - //const unsigned int ub = _baseOp.jacobianUpperBandwidth(); - //if (_baseOp.forwardFlow()) + // const unsigned int lb = _baseOp.jacobianLowerBandwidth(); + // const unsigned int ub = _baseOp.jacobianUpperBandwidth(); + // if (_baseOp.forwardFlow()) //{ // // Forwards flow @@ -788,7 +876,7 @@ bool ConvectionDispersionOperatorDG::notifyDiscontinuousSectionTransit // _jacC.repartition(lb, ub); // _jacCdisc.repartition(lb, ub); //} - //else + // else //{ // // Backwards flow @@ -815,11 +903,14 @@ void ConvectionDispersionOperatorDG::prepareADvectors(const AdJacobian return; // Get bandwidths of blocks - const unsigned int lowerColBandwidth = _baseOp.exactInt() ? 2 * _baseOp.nNodes() * _baseOp.strideColNode() : _baseOp.nNodes() * _baseOp.strideColNode(); + const unsigned int lowerColBandwidth = _baseOp.exactInt() ? 2 * _baseOp.nNodes() * _baseOp.strideColNode() + : _baseOp.nNodes() * _baseOp.strideColNode(); const unsigned int upperColBandwidth = lowerColBandwidth; // Column block - ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + _baseOp.nComp(), adJac.adDirOffset, _baseOp.nComp() * _baseOp.nPoints(), lowerColBandwidth, upperColBandwidth, lowerColBandwidth); + ad::prepareAdVectorSeedsForBandMatrix(adJac.adY + _baseOp.nComp(), adJac.adDirOffset, + _baseOp.nComp() * _baseOp.nPoints(), lowerColBandwidth, upperColBandwidth, + lowerColBandwidth); } /** @@ -830,7 +921,8 @@ void ConvectionDispersionOperatorDG::prepareADvectors(const AdJacobian * @param [in] colPorosity Porosity used for computing interstitial velocity from volumetric flow rate */ template -void ConvectionDispersionOperatorDG::setFlowRates(const active& in, const active& out, const active& colPorosity) CADET_NOEXCEPT +void ConvectionDispersionOperatorDG::setFlowRates(const active& in, const active& out, + const active& colPorosity) CADET_NOEXCEPT { _baseOp.setFlowRates(in, out, colPorosity); } @@ -847,7 +939,9 @@ void ConvectionDispersionOperatorDG::setFlowRates(const active& in, co * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ template -int ConvectionDispersionOperatorDG::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, bool wantJac, WithoutParamSensitivity) +int ConvectionDispersionOperatorDG::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, double* res, bool wantJac, + WithoutParamSensitivity) { if (wantJac) return _baseOp.residual(model, t, secIdx, y, yDot, res, _jacC); @@ -856,19 +950,25 @@ int ConvectionDispersionOperatorDG::residual(const IModel& model, doub } template -int ConvectionDispersionOperatorDG::residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithoutParamSensitivity) +int ConvectionDispersionOperatorDG::residual(const IModel& model, double t, unsigned int secIdx, + active const* y, double const* yDot, active* res, bool wantJac, + WithoutParamSensitivity) { return _baseOp.residual(model, t, secIdx, y, yDot, res, WithoutParamSensitivity()); } template -int ConvectionDispersionOperatorDG::residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity) +int ConvectionDispersionOperatorDG::residual(const IModel& model, double t, unsigned int secIdx, + active const* y, double const* yDot, active* res, bool wantJac, + WithParamSensitivity) { return _baseOp.residual(model, t, secIdx, y, yDot, res, WithParamSensitivity()); } template -int ConvectionDispersionOperatorDG::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity) +int ConvectionDispersionOperatorDG::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, active* res, bool wantJac, + WithParamSensitivity) { if (wantJac) return _baseOp.residual(model, t, secIdx, y, yDot, res, _jacC); @@ -877,30 +977,35 @@ int ConvectionDispersionOperatorDG::residual(const IModel& model, doub } /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ + * with a given vector * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). - * + * * Note that this function only performs multiplication with the Jacobian of the (axial) transport equations. - * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit operation's arrays. + * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit + * operation's arrays. * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] sDot Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ * @param [out] ret Vector @f$ z @f$ which stores the result of the operation */ template -void ConvectionDispersionOperatorDG::multiplyWithDerivativeJacobian(const SimulationTime& simTime, double const* sDot, double* ret) const +void ConvectionDispersionOperatorDG::multiplyWithDerivativeJacobian(const SimulationTime& simTime, + double const* sDot, double* ret) const { _baseOp.multiplyWithDerivativeJacobian(simTime, sDot, ret); } /** * @brief Extracts the system Jacobian from band compressed AD seed vectors - * @details The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit operation's arrays. + * @details The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit + * operation's arrays. * @param [in] adRes Residual vector of AD datatypes with band compressed seed vectors * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) */ template -void ConvectionDispersionOperatorDG::extractJacobianFromAD(active const* const adRes, unsigned int adDirOffset) +void ConvectionDispersionOperatorDG::extractJacobianFromAD(active const* const adRes, + unsigned int adDirOffset) { const active* const adVec = adRes + offsetC(); @@ -919,10 +1024,10 @@ void ConvectionDispersionOperatorDG::extractJacobianFromAD(active cons // Loop over diagonals for (int diag = 0; diag < stride; ++diag) { - if (eq - lowerBandwidth + diag >= 0 && // left boundary - eq - lowerBandwidth + diag < _jacC.cols() && // right boundary + if (eq - lowerBandwidth + diag >= 0 && // left boundary + eq - lowerBandwidth + diag < _jacC.cols() && // right boundary adVec[eq].getADValue(adDirOffset + dir) != 0.0 // keep pattern - ) + ) _jacC.coeffRef(eq, eq - lowerBandwidth + diag) = adVec[eq].getADValue(adDirOffset + dir); // Wrap around at end of row and jump to lowest subdiagonal @@ -939,21 +1044,23 @@ void ConvectionDispersionOperatorDG::extractJacobianFromAD(active cons /** * @brief Compares the analytical Jacobian with a Jacobian derived by AD * @details The analytical Jacobian is assumed to be stored in the corresponding band matrices. - * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit operation's arrays. + * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit + * operation's arrays. * @param [in] adRes Residual vector of AD datatypes with band compressed seed vectors * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) * @return Maximum elementwise absolute difference between analytic and AD Jacobian */ template -double ConvectionDispersionOperatorDG::checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const +double ConvectionDispersionOperatorDG::checkAnalyticJacobianAgainstAd(active const* const adRes, + unsigned int adDirOffset) const { // todo implement this function //// Column - //const double maxDiffCol = ad::compareBandedJacobianWithAd(adRes + offsetC(), adDirOffset, _jacC.lowerBandwidth(), _jacC); - //LOG(Debug) << "-> Col block diff: " << maxDiffCol; + // const double maxDiffCol = ad::compareBandedJacobianWithAd(adRes + offsetC(), adDirOffset, _jacC.lowerBandwidth(), + // _jacC); LOG(Debug) << "-> Col block diff: " << maxDiffCol; + + // return maxDiffCol; - //return maxDiffCol; - LOG(Debug) << "checkAnalyticJacobianAgainstAd not implemented in ConvectionDispersionOperatorDG"; return 1; } @@ -962,21 +1069,17 @@ double ConvectionDispersionOperatorDG::checkAnalyticJacobianAgainstAd( /** * @brief Assembles the axial transport Jacobian @f$ J_0 @f$ of the time-discretized equations - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The system Jacobian of the original equations, - * \f[ \frac{\partial F}{\partial y}, \f] - * is already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible - * for adding - * \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] - * to the system Jacobian, which yields the Jacobian of the time-discretized equations - * \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] - * when a BDF method is used. The time integrator needs to solve this equation for @f$ y_0 @f$, which requires - * the solution of the linear system mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + * b \f] has to be solved. The system Jacobian of the original equations, \f[ \frac{\partial F}{\partial y}, \f] is + * already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible for + * adding \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] to the system Jacobian, which yields the Jacobian of the + * time-discretized equations \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] when a BDF method is used. + * The time integrator needs to solve this equation for @f$ y_0 @f$, which requires the solution of the linear system + * mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). * * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) */ -template -void ConvectionDispersionOperatorDG::assembleDiscretizedJacobian(double alpha) +template void ConvectionDispersionOperatorDG::assembleDiscretizedJacobian(double alpha) { // set to static jacobian entries _jacCdisc = _jacC; @@ -985,16 +1088,15 @@ void ConvectionDispersionOperatorDG::assembleDiscretizedJacobian(doubl addTimeDerivativeToJacobian(alpha); } - /** * @brief Adds the derivatives with respect to @f$ \dot{y} @f$ of @f$ F(t, y, \dot{y}) @f$ to the Jacobian - * @details This functions computes - * @f[ \begin{align*} \text{_jacCdisc} = \text{_jacCdisc} + \alpha \frac{\partial F}{\partial \dot{y}}. \end{align*} @f] - * The factor @f$ \alpha @f$ is useful when constructing the linear system in the time integration process. + * @details This functions computes + * @f[ \begin{align*} \text{_jacCdisc} = \text{_jacCdisc} + \alpha \frac{\partial F}{\partial \dot{y}}. + * \end{align*} @f] The factor @f$ \alpha @f$ is useful when constructing the linear system in the time integration + * process. * @param [in] alpha Factor in front of @f$ \frac{\partial F}{\partial \dot{y}} @f$ */ -template -void ConvectionDispersionOperatorDG::addTimeDerivativeToJacobian(double alpha) +template void ConvectionDispersionOperatorDG::addTimeDerivativeToJacobian(double alpha) { _baseOp.addTimeDerivativeToJacobian(alpha, _jacCdisc); } @@ -1008,9 +1110,10 @@ void ConvectionDispersionOperatorDG::addTimeDerivativeToJacobian(doubl template bool ConvectionDispersionOperatorDG::assembleAndFactorizeDiscretizedJacobian(double alpha) { - // todo: this functionality is currently not needed for DG since we assemble and factorize a global jacobian, not blocks (as in FV) - //assembleDiscretizedJacobian(alpha); - //return _jacCdisc.factorize(); + // todo: this functionality is currently not needed for DG since we assemble and factorize a global jacobian, not + // blocks (as in FV) + // assembleDiscretizedJacobian(alpha); + // return _jacCdisc.factorize(); return true; } @@ -1019,15 +1122,15 @@ bool ConvectionDispersionOperatorDG::assembleAndFactorizeDiscretizedJa * @details The (time discretized) Jacobian matrix has to be factorized before calling this function. * Note that the given right hand side vector @p rhs is not shifted by the inlet DOFs. That * is, it is assumed to point directly to the first axial DOF. - * + * * @param [in,out] rhs On entry, right hand side of the equation system. On exit, solution of the system. * @return @c true if the system was solved correctly, otherwise @c false */ -template -bool ConvectionDispersionOperatorDG::solveDiscretizedJacobian(double* rhs) const +template bool ConvectionDispersionOperatorDG::solveDiscretizedJacobian(double* rhs) const { - // todo: this functionality is currently not needed for DG since we assemble and factorize a global jacobian, not blocks (as in FV) - return true;// _jacCdisc.solve(rhs); + // todo: this functionality is currently not needed for DG since we assemble and factorize a global jacobian, not + // blocks (as in FV) + return true; // _jacCdisc.solve(rhs); } /** @@ -1039,7 +1142,8 @@ bool ConvectionDispersionOperatorDG::solveDiscretizedJacobian(double* * @return @c true if the system was solved correctly, @c false otherwise */ template -bool ConvectionDispersionOperatorDG::solveTimeDerivativeSystem(const SimulationTime& simTime, double* const rhs) +bool ConvectionDispersionOperatorDG::solveTimeDerivativeSystem(const SimulationTime& simTime, + double* const rhs) { // Assemble double* vals = _jacCdisc.valuePtr(); @@ -1052,15 +1156,17 @@ bool ConvectionDispersionOperatorDG::solveTimeDerivativeSystem(const S _linSolver.analyzePattern(_jacCdisc); _linSolver.factorize(_jacCdisc); - if (_linSolver.info() != Success) { + if (_linSolver.info() != Success) + { LOG(Error) << "factorization failed in sensitivity initialization"; } Eigen::Map ret_vec(rhs, _jacCdisc.rows()); ret_vec = _linSolver.solve(ret_vec); - // Use the factors to solve the linear system - if (_linSolver.info() != Success) { + // Use the factors to solve the linear system + if (_linSolver.info() != Success) + { LOG(Error) << "solve failed in sensitivity initialization"; } @@ -1069,10 +1175,10 @@ bool ConvectionDispersionOperatorDG::solveTimeDerivativeSystem(const S // Template instantiations template class ConvectionDispersionOperatorDG; -//template class ConvectionDispersionOperatorDG; // todo +// template class ConvectionDispersionOperatorDG; // todo -} // namespace parts +} // namespace parts -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/parts/ConvectionDispersionOperatorDG.hpp b/src/libcadet/model/parts/ConvectionDispersionOperatorDG.hpp index 38e0c4a7e..c813814ac 100644 --- a/src/libcadet/model/parts/ConvectionDispersionOperatorDG.hpp +++ b/src/libcadet/model/parts/ConvectionDispersionOperatorDG.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -21,7 +21,7 @@ #include "ParamIdUtil.hpp" #include "AutoDiff.hpp" #include "Memory.hpp" -//#include "Weno.hpp" // todo weno DG +// #include "Weno.hpp" // todo weno DG #include "SimulationTypes.hpp" #include #include "linalg/BandedEigenSparseRowIterator.hpp" @@ -38,1259 +38,1648 @@ using namespace Eigen; namespace cadet { - class IParameterProvider; - class IConfigHelper; - struct AdJacobianParams; - struct SimulationTime; - class IModel; +class IParameterProvider; +class IConfigHelper; +struct AdJacobianParams; +struct SimulationTime; +class IModel; - namespace model +namespace model +{ + +class IParameterParameterDependence; + +namespace parts +{ + +/** + * @brief Convection dispersion transport operator + * @details Implements the equation + * + * @f[\begin{align} + \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 +c_i}{\partial z^2} \\ \end{align} @f] + * with Danckwerts boundary conditions (see @cite Danckwerts1953) +@f[ \begin{align} +u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partial z}(t,0) \\ +\frac{\partial c_i}{\partial z}(t,L) &= 0 +\end{align} @f] + * Methods are described in @cite todo, and @cite Puttmann2013, @cite Puttmann2016 (forward sensitivities, AD, band +compression) + * + * This class does not store the Jacobian. It only fills existing matrices given to its residual() functions. + * It assumes that there is no offset to the inlet in the local state vector and that the firsts cell is placed + * directly after the inlet DOFs. + */ +class AxialConvectionDispersionOperatorBaseDG +{ +public: + AxialConvectionDispersionOperatorBaseDG(); + ~AxialConvectionDispersionOperatorBaseDG() CADET_NOEXCEPT; + + void setFlowRates(const active& in, const active& out, const active& colPorosity) CADET_NOEXCEPT; + + bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, + unsigned int nComp, int polynomial_integration_mode, unsigned int nCells, + unsigned int polyDeg, unsigned int strideNode); + bool configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, + std::unordered_map& parameters); + bool notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, Eigen::MatrixXd& jacInlet); + + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, + Eigen::SparseMatrix& jac); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, + Eigen::SparseMatrix& jac); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, + WithoutParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, + WithParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, + WithParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, + WithoutParamSensitivity); + + int calcStaticAnaJacobian(Eigen::SparseMatrix& jacobian, Eigen::MatrixXd& jacInlet, + const int bulkOffset = 0); + typedef Eigen::Triplet T; + void convDispJacPattern(std::vector& tripletList, const int bulkOffset = 0); + unsigned int nConvDispEntries(bool pureNNZ = false); + void multiplyWithDerivativeJacobian(const SimulationTime& simTime, double const* sDot, double* ret) const; + void addTimeDerivativeToJacobian(double alpha, Eigen::SparseMatrix& jacDisc, + unsigned int blockOffset = 0); + + inline const active& columnLength() const CADET_NOEXCEPT + { + return _colLength; + } + inline const active& currentVelocity(double pos) const CADET_NOEXCEPT + { + return _curVelocity; + } + inline bool forwardFlow() const CADET_NOEXCEPT { + return _curVelocity >= 0.0; + } - class IParameterParameterDependence; + inline double cellLeftBound(unsigned int idx) const CADET_NOEXCEPT + { + return idx * static_cast(_deltaZ); + } + double relativeCoordinate(unsigned int idx) const CADET_NOEXCEPT + { + // const unsigned int cell = floor(idx / _nNodes); + // const unsigned int node = idx % _nNodes; + // divide by column length to get relative position + return (floor(idx / _nNodes) * static_cast(_deltaZ) + + 0.5 * static_cast(_deltaZ) * (1.0 + _nodes[idx % _nNodes])) / + static_cast(_colLength); + } + + inline const double* LGLnodes() const CADET_NOEXCEPT + { + return &_nodes[0]; + } + inline const active& currentVelocity() const CADET_NOEXCEPT + { + return _curVelocity; + } + inline const active* currentDispersion(const int secIdx) const CADET_NOEXCEPT + { + return getSectionDependentSlice(_colDispersion, _nComp, secIdx); + } + inline const bool dispersionCompIndep() const CADET_NOEXCEPT + { + return _dispersionCompIndep; + } + + inline unsigned int nComp() const CADET_NOEXCEPT + { + return _nComp; + } + inline unsigned int nCells() const CADET_NOEXCEPT + { + return _nCells; + } + inline unsigned int nNodes() const CADET_NOEXCEPT + { + return _nNodes; + } + inline unsigned int nPoints() const CADET_NOEXCEPT + { + return _nPoints; + } + inline bool exactInt() const CADET_NOEXCEPT + { + return _exactInt; + } + + // Indexer functionality: + // Strides + inline int strideColCell() const CADET_NOEXCEPT + { + return static_cast(_strideCell); + } + inline int strideColNode() const CADET_NOEXCEPT + { + return static_cast(_strideNode); + } + inline int strideColComp() const CADET_NOEXCEPT + { + return 1; + } + // Offsets + inline int offsetC() const CADET_NOEXCEPT + { + return _nComp; + } + + unsigned int jacobianLowerBandwidth() const CADET_NOEXCEPT; + unsigned int jacobianUpperBandwidth() const CADET_NOEXCEPT; + double inletJacobianFactor() const CADET_NOEXCEPT; + + // @todo use more efficient seed vectors. currently, we treat the jacobian as banded, but the pattern is actually + // more sparse when multiple components are considered (note that active type directions are limited) We have + // different jacobian structure for exact integration and collocation DG scheme, i.e. we need different seed vectors + // collocation DG: 2 * N_n * (strideNode) + 1 = total bandwidth (main diagonal entries maximally depend on the next + // and last N_n liquid phase entries of same component) + // ex. int. DG: 4 * N_n * (strideNode) + 1 = total bandwidth (main diagonal entries maximally depend on the next + // and last 2*N_n liquid phase entries of same component) + int requiredADdirs() const CADET_NOEXCEPT + { + return (_exactInt) ? 4 * _nNodes * strideColNode() + 1 : 2 * _nNodes * strideColNode() + 1; + } + + bool setParameter(const ParameterId& pId, double value); + bool setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, + unsigned int adDirection, double adValue); + bool setSensitiveParameterValue(const std::unordered_set& sensParams, const ParameterId& id, double value); + +protected: + template + int residualImpl(const IModel& model, double t, unsigned int secIdx, StateType const* y, double const* yDot, + ResidualType* res, RowIteratorType jacBegin); + + // discretization parameters + unsigned int _nComp; //!< Number of components + bool _exactInt; //!< specifies whether integrals are calculated exactly or approximated with LGL quadrature + unsigned int _polyDeg; //!< DG discretization polynomial degree + unsigned int _nCells; //!< Number of axial cells + unsigned int _nNodes; //!< Number of nodes per cell + unsigned int _nPoints; //!< Number of axial cells + + unsigned int _strideNode; //!< Number of values between the same item in two adjacent nodes + unsigned int _strideCell; //!< Number of values between the same item in two adjacent cells + + // discretization toolbox and memory buffers + active _deltaZ; //!< Cell spacing + Eigen::VectorXd _nodes; //!< LGL nodes in [-1, 1] + Eigen::MatrixXd _polyDerM; //!< Polynomial derivative Matrix + Eigen::VectorXd _invWeights; //!< Inverse LGL quadrature weights -> diagonal (lumped) LGL mass matrix + Eigen::MatrixXd _invMM; //!< Inverse (exact) mass matrix + Eigen::MatrixXd* _DGjacAxDispBlocks; //!< Unique Jacobian blocks for axial dispersion + Eigen::MatrixXd _DGjacAxConvBlock; //!< Unique Jacobian blocks for axial convection + + active* _auxState; //!< auxiliary variable + active* _subsState; //!< auxiliary substitute + Eigen::Vector _surfaceFlux; //!< stores the surface flux values + Eigen::Vector _boundary; //!< stores the boundary values from Danckwert boundary conditions + + // Simulation parameters + active _colLength; //!< Column length \f$ L \f$ + active _crossSection; //!< Cross section area + + // Section dependent parameters + std::vector _colDispersion; //!< Column dispersion (may be section dependent) \f$ D_{\text{ax}} \f$ + std::vector _velocity; //!< Interstitial velocity (may be section dependent) \f$ u \f$ + active _curVelocity; //!< Current interstitial velocity \f$ u \f$ in this time section + int _dir; //!< Current flow direction in this time section + + // needed? + int _curSection; //!< current section index + bool _newStaticJac; //!< determines wether static analytical jacobian needs to be computed (every section) + + bool _dispersionCompIndep; //!< Determines whether dispersion is component independent + IParameterParameterDependence* _dispersionDep; + + /* =================================================================================== + * Functions to calculate Jacobian blocks + * =================================================================================== */ + + /** + * @brief calculates the convection part of the DG jacobian + */ + Eigen::MatrixXd DGjacobianConvBlock() + { + + // Convection block [ d RHS_conv / d c ], additionally depends on upwind flux part from corresponding neighbour + // cell + Eigen::MatrixXd convBlock = Eigen::MatrixXd::Zero(_nNodes, _nNodes + 1); + + if (_curVelocity >= 0.0) + { // forward flow -> Convection block additionally depends on last entry of previous cell + convBlock.block(0, 1, _nNodes, _nNodes) -= _polyDerM; + + if (_exactInt) + { + convBlock.block(0, 0, _nNodes, 1) += _invMM.block(0, 0, _nNodes, 1); + convBlock.block(0, 1, _nNodes, 1) -= _invMM.block(0, 0, _nNodes, 1); + } + else + { + convBlock(0, 0) += _invWeights[0]; + convBlock(0, 1) -= _invWeights[0]; + } + } + else + { // backward flow -> Convection block additionally depends on first entry of subsequent cell + convBlock.block(0, 0, _nNodes, _nNodes) -= _polyDerM; + + if (_exactInt) + { + convBlock.block(0, _nNodes - 1, _nNodes, 1) += _invMM.block(0, _nNodes - 1, _nNodes, 1); + convBlock.block(0, _nNodes, _nNodes, 1) -= _invMM.block(0, _nNodes - 1, _nNodes, 1); + } + else + { + convBlock(_nNodes - 1, _nNodes - 1) += _invWeights[_nNodes - 1]; + convBlock(_nNodes - 1, _nNodes) -= _invWeights[_nNodes - 1]; + } + } + convBlock *= 2 / static_cast(_deltaZ); + + return -convBlock; // *-1 for residual + } + + /** + * @brief calculates the DG Jacobian auxiliary block + * @param [in] exInt true if exact integration DG scheme + * @param [in] cellIdx cell index + */ + Eigen::MatrixXd getGBlock(unsigned int cellIdx) + { - namespace parts + // Auxiliary Block [ d g(c) / d c ], additionally depends on boundary entries of neighbouring cells + Eigen::MatrixXd gBlock = Eigen::MatrixXd::Zero(_nNodes, _nNodes + 2); + gBlock.block(0, 1, _nNodes, _nNodes) = _polyDerM; + if (_exactInt) { + if (cellIdx != 1 && cellIdx != _nCells) + { + gBlock.block(0, 0, _nNodes, 1) -= 0.5 * _invMM.block(0, 0, _nNodes, 1); + gBlock.block(0, 1, _nNodes, 1) += 0.5 * _invMM.block(0, 0, _nNodes, 1); + gBlock.block(0, _nNodes, _nNodes, 1) -= 0.5 * _invMM.block(0, _nNodes - 1, _nNodes, 1); + gBlock.block(0, _nNodes + 1, _nNodes, 1) += 0.5 * _invMM.block(0, _nNodes - 1, _nNodes, 1); + } + else if (cellIdx == 1) + { // left + if (cellIdx == _nCells) + return gBlock * 2 / static_cast(_deltaZ); + ; + gBlock.block(0, _nNodes, _nNodes, 1) -= 0.5 * _invMM.block(0, _nNodes - 1, _nNodes, 1); + gBlock.block(0, _nNodes + 1, _nNodes, 1) += 0.5 * _invMM.block(0, _nNodes - 1, _nNodes, 1); + } + else if (cellIdx == _nCells) + { // right + gBlock.block(0, 0, _nNodes, 1) -= 0.5 * _invMM.block(0, 0, _nNodes, 1); + gBlock.block(0, 1, _nNodes, 1) += 0.5 * _invMM.block(0, 0, _nNodes, 1); + } + else if (cellIdx == 0 || cellIdx == _nCells + 1) + { + gBlock.setZero(); + } + gBlock *= 2 / static_cast(_deltaZ); + } + else + { + if (cellIdx == 0 || cellIdx == _nCells + 1) + return Eigen::MatrixXd::Zero(_nNodes, _nNodes + 2); - /** - * @brief Convection dispersion transport operator - * @details Implements the equation - * - * @f[\begin{align} - \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i} \frac{\partial^2 c_i}{\partial z^2} \\ - \end{align} @f] - * with Danckwerts boundary conditions (see @cite Danckwerts1953) - @f[ \begin{align} - u c_{\text{in},i}(t) &= u c_i(t,0) - D_{\text{ax},i} \frac{\partial c_i}{\partial z}(t,0) \\ - \frac{\partial c_i}{\partial z}(t,L) &= 0 - \end{align} @f] - * Methods are described in @cite todo, and @cite Puttmann2013, @cite Puttmann2016 (forward sensitivities, AD, band compression) - * - * This class does not store the Jacobian. It only fills existing matrices given to its residual() functions. - * It assumes that there is no offset to the inlet in the local state vector and that the firsts cell is placed - * directly after the inlet DOFs. - */ - class AxialConvectionDispersionOperatorBaseDG + gBlock(0, 0) -= 0.5 * _invWeights[0]; + gBlock(0, 1) += 0.5 * _invWeights[0]; + gBlock(_nNodes - 1, _nNodes) -= 0.5 * _invWeights[_nNodes - 1]; + gBlock(_nNodes - 1, _nNodes + 1) += 0.5 * _invWeights[_nNodes - 1]; + gBlock *= 2 / static_cast(_deltaZ); + + if (cellIdx == 1) + { + // adjust auxiliary Block [ d g(c) / d c ] for left boundary cell + gBlock(0, 1) -= 0.5 * _invWeights[0] * 2 / static_cast(_deltaZ); + if (cellIdx == _nCells) + { // adjust for special case one cell + gBlock(0, 0) += 0.5 * _invWeights[0] * 2 / static_cast(_deltaZ); + gBlock(_nNodes - 1, _nNodes + 1) -= + 0.5 * _invWeights[_nNodes - 1] * 2 / static_cast(_deltaZ); + gBlock(_nNodes - 1, _nNodes) += 0.5 * _invWeights[_polyDeg] * 2 / static_cast(_deltaZ); + } + } + else if (cellIdx == _nCells) { - public: + // adjust auxiliary Block [ d g(c) / d c ] for right boundary cell + gBlock(_nNodes - 1, _nNodes) += 0.5 * _invWeights[_polyDeg] * 2 / static_cast(_deltaZ); + } + } + + return gBlock; + } + /** + * @brief calculates the num. flux part of a dispersion DG Jacobian block + * @param [in] cellIdx cell index + * @param [in] leftG left neighbour auxiliary block + * @param [in] middleG neighbour auxiliary block + * @param [in] rightG neighbour auxiliary block + */ + Eigen::MatrixXd auxBlockGstar(unsigned int cellIdx, Eigen::MatrixXd leftG, Eigen::MatrixXd middleG, + Eigen::MatrixXd rightG) + { - AxialConvectionDispersionOperatorBaseDG(); - ~AxialConvectionDispersionOperatorBaseDG() CADET_NOEXCEPT; + // auxiliary block [ d g^* / d c ], depends on whole previous and subsequent cell plus first entries of + // subsubsequent cells + Eigen::MatrixXd gStarDC = Eigen::MatrixXd::Zero(_nNodes, 3 * _nNodes + 2); + // NOTE: N = polyDeg + // indices gStarDC : 0 , 1 , ..., _nNodes; _nNodes+1, ..., 2 * _nNodes; 2*_nNodes+1, ..., 3 * + // _nNodes; 3*_nNodes+1 derivative index j : -(N+1)-1, -(N+1),... , -1 ; 0 , ..., N ; N + 1 + // , ..., 2N + 2 ; 2(N+1) +1 auxiliary block [d g^* / d c] + if (cellIdx != 1) + { + gStarDC.block(0, _nNodes, 1, _nNodes + 2) += middleG.block(0, 0, 1, _nNodes + 2); + gStarDC.block(0, 0, 1, _nNodes + 2) += leftG.block(_nNodes - 1, 0, 1, _nNodes + 2); + } + if (cellIdx != _nCells) + { + gStarDC.block(_nNodes - 1, _nNodes, 1, _nNodes + 2) += middleG.block(_nNodes - 1, 0, 1, _nNodes + 2); + gStarDC.block(_nNodes - 1, 2 * _nNodes, 1, _nNodes + 2) += rightG.block(0, 0, 1, _nNodes + 2); + } + gStarDC *= 0.5; - void setFlowRates(const active& in, const active& out, const active& colPorosity) CADET_NOEXCEPT; + return gStarDC; + } - bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, int polynomial_integration_mode, unsigned int nCells, unsigned int polyDeg, unsigned int strideNode); - bool configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, std::unordered_map& parameters); - bool notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, Eigen::MatrixXd& jacInlet); + Eigen::MatrixXd getBMatrix() + { - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, Eigen::SparseMatrix& jac); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, Eigen::SparseMatrix& jac); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, WithoutParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, WithParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, WithParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, WithoutParamSensitivity); + Eigen::MatrixXd B = Eigen::MatrixXd::Zero(_nNodes, _nNodes); + B(0, 0) = -1.0; + B(_nNodes - 1, _nNodes - 1) = 1.0; - int calcStaticAnaJacobian(Eigen::SparseMatrix& jacobian, Eigen::MatrixXd& jacInlet, const int bulkOffset = 0); - typedef Eigen::Triplet T; - void convDispJacPattern(std::vector& tripletList, const int bulkOffset = 0); - unsigned int nConvDispEntries(bool pureNNZ = false); - void multiplyWithDerivativeJacobian(const SimulationTime& simTime, double const* sDot, double* ret) const; - void addTimeDerivativeToJacobian(double alpha, Eigen::SparseMatrix& jacDisc, unsigned int blockOffset = 0); + return B; + } - inline const active& columnLength() const CADET_NOEXCEPT { return _colLength; } - inline const active& currentVelocity(double pos) const CADET_NOEXCEPT { return _curVelocity; } - inline bool forwardFlow() const CADET_NOEXCEPT { return _curVelocity >= 0.0; } + /** + * @brief calculates the dispersion part of the DG jacobian + * @param [in] exInt true if exact integration DG scheme + * @param [in] cellIdx cell index + */ + Eigen::MatrixXd DGjacobianDispBlock(unsigned int cellIdx) + { + + int offC = 0; // inlet DOFs not included in Jacobian + + Eigen::MatrixXd dispBlock; + + if (_exactInt) + { + + // Inner dispersion block [ d RHS_disp / d c ], depends on whole previous and subsequent cell plus first + // entries of subsubsequent cells + dispBlock = Eigen::MatrixXd::Zero(_nNodes, 3 * _nNodes + 2); + + Eigen::MatrixXd B = getBMatrix(); // "Lifting" matrix + Eigen::MatrixXd gBlock = getGBlock(cellIdx); // current cell auxiliary block matrix + Eigen::MatrixXd gStarDC = + auxBlockGstar(cellIdx, getGBlock(cellIdx - 1), gBlock, getGBlock(cellIdx + 1)); // Numerical flux block + + // indices dispBlock : 0 , 1 , ..., _nNodes; _nNodes+1, ..., 2 * _nNodes; 2*_nNodes+1, + // ..., 3 * _nNodes; 3*_nNodes+1 + // derivative index j : -(N+1)-1, -(N+1),..., -1 ; 0 , ..., N ; N + 1 , ..., 2N + 2 + //; 2(N+1) +1 + dispBlock.block(0, _nNodes, _nNodes, _nNodes + 2) += _polyDerM * gBlock - _invMM * B * gBlock; + dispBlock += _invMM * B * gStarDC; + dispBlock *= 2 / static_cast(_deltaZ); + } + else + { // inexact integration collocation DGSEM + + dispBlock = Eigen::MatrixXd::Zero(_nNodes, 3 * _nNodes); + Eigen::MatrixXd GBlockLeft = getGBlock(cellIdx - 1); + Eigen::MatrixXd GBlock = getGBlock(cellIdx); + Eigen::MatrixXd GBlockRight = getGBlock(cellIdx + 1); + + // Dispersion block [ d RHS_disp / d c ], depends on whole previous and subsequent cell + // NOTE: N = polyDeg + // cell indices : 0 , ..., _nNodes - 1; _nNodes, ..., 2 * _nNodes - 1; 2 * _nNodes, ..., 3 * _nNodes - + // 1 + // j : -N-1, ..., -1 ; 0 , ..., N ; N + 1, ..., 2N + 1 + dispBlock.block(0, _nNodes - 1, _nNodes, _nNodes + 2) = _polyDerM * GBlock; + + if (cellIdx > 1) + { + dispBlock(0, _nNodes - 1) += + -_invWeights[0] * + (-0.5 * GBlock(0, 0) + 0.5 * GBlockLeft(_nNodes - 1, _nNodes)); // G_N,N i=0, j=-1 + dispBlock(0, _nNodes) += + -_invWeights[0] * + (-0.5 * GBlock(0, 1) + 0.5 * GBlockLeft(_nNodes - 1, _nNodes + 1)); // G_N,N+1 i=0, j=0 + dispBlock.block(0, _nNodes + 1, 1, _nNodes) += + -_invWeights[0] * (-0.5 * GBlock.block(0, 2, 1, _nNodes)); // G_i,j i=0, j=1,...,N+1 + dispBlock.block(0, 0, 1, _nNodes - 1) += + -_invWeights[0] * + (0.5 * GBlockLeft.block(_nNodes - 1, 1, 1, _nNodes - 1)); // G_N,j+N+1 i=0, j=-N-1,...,-2 + } + else if (cellIdx == 1) + { // left boundary cell + dispBlock.block(0, _nNodes - 1, 1, _nNodes + 2) += + -_invWeights[0] * (-GBlock.block(0, 0, 1, _nNodes + 2)); // G_N,N i=0, j=-1,...,N+1 + } + if (cellIdx < _nCells) + { + dispBlock.block(_nNodes - 1, _nNodes - 1, 1, _nNodes) += + _invWeights[_nNodes - 1] * + (-0.5 * GBlock.block(_nNodes - 1, 0, 1, _nNodes)); // G_i,j+N+1 i=N, j=-1,...,N-1 + dispBlock(_nNodes - 1, 2 * _nNodes - 1) += + _invWeights[_nNodes - 1] * + (-0.5 * GBlock(_nNodes - 1, _nNodes) + 0.5 * GBlockRight(0, 0)); // G_i,j i=N, j=N + dispBlock(_nNodes - 1, 2 * _nNodes) += + _invWeights[_nNodes - 1] * + (-0.5 * GBlock(_nNodes - 1, _nNodes + 1) + 0.5 * GBlockRight(0, 1)); // G_i,j i=N, j=N+1 + dispBlock.block(_nNodes - 1, 2 * _nNodes + 1, 1, _nNodes - 1) += + _invWeights[_nNodes - 1] * + (0.5 * GBlockRight.block(0, 2, 1, _nNodes - 1)); // G_0,j-N-1 i=N, j=N+2,...,2N+1 + } + else if (cellIdx == _nCells) + { // right boundary cell + dispBlock.block(_nNodes - 1, _nNodes - 1, 1, _nNodes + 2) += + _invWeights[_nNodes - 1] * + (-GBlock.block(_nNodes - 1, 0, 1, _nNodes + 2)); // G_i,j+N+1 i=N, j=--1,...,N+1 + } + + dispBlock *= 2 / static_cast(_deltaZ); + } + + return -dispBlock; // *-1 for residual + } + + /* =================================================================================== + * Residual functions + * =================================================================================== */ + /** + * @brief calculates the volume Integral of the auxiliary equation + * @param [in] state state vector + * @param [in, out] stateDer state derivative vector + * @detail performs matrix-vector multiplication optimized depending on state types and stores the result in + * stateDer. + */ + template + void volumeIntegral(Eigen::Map, 0, InnerStride>& state, + Eigen::Map, 0, InnerStride>& stateDer) + { - inline double cellLeftBound(unsigned int idx) const CADET_NOEXCEPT { return idx * static_cast(_deltaZ); } - double relativeCoordinate(unsigned int idx) const CADET_NOEXCEPT + for (unsigned int Cell = 0; Cell < _nCells; Cell++) + { + + // exploit Eigen3 performance if no mixed scalar types + if constexpr (std::is_same_v) + { + if constexpr (std::is_same_v) + { + stateDer.segment(Cell * _nNodes, _nNodes) -= _polyDerM * state.segment(Cell * _nNodes, _nNodes); + } + else { - //const unsigned int cell = floor(idx / _nNodes); - //const unsigned int node = idx % _nNodes; - // divide by column length to get relative position - return (floor(idx / _nNodes) * static_cast(_deltaZ) + 0.5 * static_cast(_deltaZ) * (1.0 + _nodes[idx % _nNodes])) / static_cast(_colLength); + stateDer.segment(Cell * _nNodes, _nNodes) -= + (_polyDerM * state.segment(Cell * _nNodes, _nNodes)).template cast(); } + } + else + { // both active types + // todo use custom (mixed scalar-type) matrix vector multiplication? + stateDer.segment(Cell * _nNodes, _nNodes) -= + _polyDerM.template cast() * state.segment(Cell * _nNodes, _nNodes); + } + } + } + template + void volumeIntegral(Eigen::Map, 0, InnerStride>& state, + Eigen::Map, 0, InnerStride>& stateDer) + { + Eigen::Map, 0, InnerStride> state_const(state.data(), state.size(), + InnerStride(1)); + volumeIntegral(state_const, stateDer); + } + /* + * @brief calculates the interface fluxes h* of Convection Dispersion equation + */ + template void InterfaceFlux(const StateType* C, ParamType _dispersion) + { - inline const double* LGLnodes() const CADET_NOEXCEPT { return &_nodes[0]; } - inline const active& currentVelocity() const CADET_NOEXCEPT { return _curVelocity; } - inline const active* currentDispersion(const int secIdx) const CADET_NOEXCEPT { return getSectionDependentSlice(_colDispersion, _nComp, secIdx); } - inline const bool dispersionCompIndep() const CADET_NOEXCEPT { return _dispersionCompIndep; } - - inline unsigned int nComp() const CADET_NOEXCEPT { return _nComp; } - inline unsigned int nCells() const CADET_NOEXCEPT { return _nCells; } - inline unsigned int nNodes() const CADET_NOEXCEPT { return _nNodes; } - inline unsigned int nPoints() const CADET_NOEXCEPT { return _nPoints; } - inline bool exactInt() const CADET_NOEXCEPT { return _exactInt; } - - // Indexer functionality: - // Strides - inline int strideColCell() const CADET_NOEXCEPT { return static_cast(_strideCell); } - inline int strideColNode() const CADET_NOEXCEPT { return static_cast(_strideNode); } - inline int strideColComp() const CADET_NOEXCEPT { return 1; } - // Offsets - inline int offsetC() const CADET_NOEXCEPT { return _nComp; } - - unsigned int jacobianLowerBandwidth() const CADET_NOEXCEPT; - unsigned int jacobianUpperBandwidth() const CADET_NOEXCEPT; - double inletJacobianFactor() const CADET_NOEXCEPT; - - // @todo use more efficient seed vectors. currently, we treat the jacobian as banded, but the pattern is actually more sparse when multiple components are considered - // (note that active type directions are limited) - // We have different jacobian structure for exact integration and collocation DG scheme, i.e. we need different seed vectors - // collocation DG: 2 * N_n * (strideNode) + 1 = total bandwidth (main diagonal entries maximally depend on the next and last N_n liquid phase entries of same component) - // ex. int. DG: 4 * N_n * (strideNode) + 1 = total bandwidth (main diagonal entries maximally depend on the next and last 2*N_n liquid phase entries of same component) - int requiredADdirs() const CADET_NOEXCEPT { return (_exactInt) ? 4 * _nNodes * strideColNode() + 1 : 2 * _nNodes * strideColNode() + 1; } - - - bool setParameter(const ParameterId& pId, double value); - bool setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, unsigned int adDirection, double adValue); - bool setSensitiveParameterValue(const std::unordered_set& sensParams, const ParameterId& id, double value); - - protected: - - template - int residualImpl(const IModel& model, double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res, RowIteratorType jacBegin); - - // discretization parameters - unsigned int _nComp; //!< Number of components - bool _exactInt; //!< specifies whether integrals are calculated exactly or approximated with LGL quadrature - unsigned int _polyDeg; //!< DG discretization polynomial degree - unsigned int _nCells; //!< Number of axial cells - unsigned int _nNodes; //!< Number of nodes per cell - unsigned int _nPoints; //!< Number of axial cells - - unsigned int _strideNode; //!< Number of values between the same item in two adjacent nodes - unsigned int _strideCell; //!< Number of values between the same item in two adjacent cells - - // discretization toolbox and memory buffers - active _deltaZ; //!< Cell spacing - Eigen::VectorXd _nodes; //!< LGL nodes in [-1, 1] - Eigen::MatrixXd _polyDerM; //!< Polynomial derivative Matrix - Eigen::VectorXd _invWeights; //!< Inverse LGL quadrature weights -> diagonal (lumped) LGL mass matrix - Eigen::MatrixXd _invMM; //!< Inverse (exact) mass matrix - Eigen::MatrixXd* _DGjacAxDispBlocks; //!< Unique Jacobian blocks for axial dispersion - Eigen::MatrixXd _DGjacAxConvBlock; //!< Unique Jacobian blocks for axial convection - - active* _auxState; //!< auxiliary variable - active* _subsState; //!< auxiliary substitute - Eigen::Vector _surfaceFlux; //!< stores the surface flux values - Eigen::Vector _boundary; //!< stores the boundary values from Danckwert boundary conditions - - // Simulation parameters - active _colLength; //!< Column length \f$ L \f$ - active _crossSection; //!< Cross section area - - // Section dependent parameters - std::vector _colDispersion; //!< Column dispersion (may be section dependent) \f$ D_{\text{ax}} \f$ - std::vector _velocity; //!< Interstitial velocity (may be section dependent) \f$ u \f$ - active _curVelocity; //!< Current interstitial velocity \f$ u \f$ in this time section - int _dir; //!< Current flow direction in this time section - - // needed? - int _curSection; //!< current section index - bool _newStaticJac; //!< determines wether static analytical jacobian needs to be computed (every section) - - bool _dispersionCompIndep; //!< Determines whether dispersion is component independent - IParameterParameterDependence* _dispersionDep; - - /* =================================================================================== - * Functions to calculate Jacobian blocks - * =================================================================================== */ - - /** - * @brief calculates the convection part of the DG jacobian - */ - Eigen::MatrixXd DGjacobianConvBlock() { - - // Convection block [ d RHS_conv / d c ], additionally depends on upwind flux part from corresponding neighbour cell - Eigen::MatrixXd convBlock = Eigen::MatrixXd::Zero(_nNodes, _nNodes + 1); - - if (_curVelocity >= 0.0) { // forward flow -> Convection block additionally depends on last entry of previous cell - convBlock.block(0, 1, _nNodes, _nNodes) -= _polyDerM; - - if (_exactInt) { - convBlock.block(0, 0, _nNodes, 1) += _invMM.block(0, 0, _nNodes, 1); - convBlock.block(0, 1, _nNodes, 1) -= _invMM.block(0, 0, _nNodes, 1); - } - else { - convBlock(0, 0) += _invWeights[0]; - convBlock(0, 1) -= _invWeights[0]; - } - } - else { // backward flow -> Convection block additionally depends on first entry of subsequent cell - convBlock.block(0, 0, _nNodes, _nNodes) -= _polyDerM; + StateType* g = reinterpret_cast(_auxState); - if (_exactInt) { - convBlock.block(0, _nNodes - 1, _nNodes, 1) += _invMM.block(0, _nNodes - 1, _nNodes, 1); - convBlock.block(0, _nNodes, _nNodes, 1) -= _invMM.block(0, _nNodes - 1, _nNodes, 1); - } - else { - convBlock(_nNodes - 1, _nNodes - 1) += _invWeights[_nNodes - 1]; - convBlock(_nNodes - 1, _nNodes) -= _invWeights[_nNodes - 1]; - } - } - convBlock *= 2 / static_cast(_deltaZ); + // component-wise strides + unsigned int strideNode = _strideNode; + unsigned int strideCell = _nNodes * strideNode; + unsigned int strideNode_g = 1u; + unsigned int strideCell_g = _nNodes * strideNode_g; + + // Conv.Disp. flux: h* = h*_conv + h*_disp = v c_up + 0.5 sqrt(D_ax) (S_l + S_r) + + if (_curVelocity >= 0.0) + { // forward flow (upwind num. flux) + // calculate inner interface fluxes + for (unsigned int Cell = 1; Cell < _nCells; Cell++) + { + // h* = h*_conv + h*_disp + _surfaceFlux[Cell] // inner interfaces + = _curVelocity * (C[Cell * strideCell - strideNode]) // left cell (i.e. forward flow upwind) + - 0.5 * (-2.0 / static_cast(_deltaZ)) * _dispersion * + (g[Cell * strideCell_g - strideNode_g] // left cell + + g[Cell * strideCell_g]); // right cell + } + + // boundary fluxes + // inlet (left) boundary interface + _surfaceFlux[0] = _curVelocity * _boundary[0]; + + // outlet (right) boundary interface + _surfaceFlux[_nCells] = _curVelocity * (C[_nCells * strideCell - strideNode]) - + 0.5 * (-2.0 / static_cast(_deltaZ)) * _dispersion * + (g[_nCells * strideCell_g - strideNode_g] // last cell last node + + _boundary[3]); // right boundary value g + } + else + { // backward flow (upwind num. flux) + // calculate inner interface fluxes + for (unsigned int Cell = 1; Cell < _nCells; Cell++) + { + // h* = h*_conv + h*_disp + _surfaceFlux[Cell] // inner interfaces + = _curVelocity * (C[Cell * strideCell]) // right cell (i.e. backward flow upwind) + - 0.5 * (-2.0 / static_cast(_deltaZ)) * _dispersion * + (g[Cell * strideCell_g - strideNode_g] // left cell + + g[Cell * strideCell_g]); // right cell + } + + // boundary fluxes + // inlet boundary interface + _surfaceFlux[_nCells] = _curVelocity * _boundary[0]; + + // outlet boundary interface + _surfaceFlux[0] = _curVelocity * (C[0]) - 0.5 * (-2.0 / static_cast(_deltaZ)) * _dispersion * + (g[0] // first cell first node + + _boundary[2]); // left boundary value g + } + // apply inverse mapping jacobian (reference space) + _surfaceFlux *= -2.0 / static_cast(_deltaZ); + } + /** + * @brief calculates and fills the surface flux values for auxiliary equation + * @param [in] C bulk liquid phase + * @param [in] strideNode node stride w.r.t. C + * @param [in] strideCell cell stride w.r.t. C + */ + template + void InterfaceFluxAuxiliary(const StateType* C, unsigned int strideNode, unsigned int strideCell) + { - return -convBlock; // *-1 for residual + // Auxiliary flux: c* = 0.5 (c_l + c_r) + + // calculate inner interface fluxes + for (unsigned int Cell = 1; Cell < _nCells; Cell++) + { + _surfaceFlux[Cell] // left interfaces + = 0.5 * (C[Cell * strideCell - strideNode] + // left node + C[Cell * strideCell]); // right node + } + // calculate boundary interface fluxes + + _surfaceFlux[0] // left boundary interface + = 0.5 * (C[0] + // boundary value + C[0]); // first cell first node + + _surfaceFlux[(_nCells)] // right boundary interface + = 0.5 * (C[_nCells * strideCell - strideNode] + // last cell last node + C[_nCells * strideCell - strideNode]); // // boundary value + } + /** + * @brief calculates the string form surface Integral + * @param [in] state relevant state vector + * @param [in] stateDer state derivative vector the solution is added to + * @param [in] strideNode_state node stride w.r.t. state + * @param [in] strideCell_state cell stride w.r.t. state + * @param [in] strideNode_stateDer node stride w.r.t. stateDer + * @param [in] strideCell_stateDer cell stride w.r.t. stateDer + * @detail calculates stateDer = M^-1 * B * (state - state^*) and exploits LGL sparsity if applied + */ + template + void surfaceIntegral(const StateType* state, ResidualType* stateDer, unsigned int strideNode_state, + unsigned int strideCell_state, unsigned int strideNode_stateDer, + unsigned int strideCell_stateDer) + { + + if (_exactInt) + { // non-collocated integration -> dense mass matrix + for (unsigned int Cell = 0; Cell < _nCells; Cell++) + { + // strong surface integral -> M^-1 B [state - state*] + for (unsigned int Node = 0; Node < _nNodes; Node++) + { + stateDer[Cell * strideCell_stateDer + Node * strideNode_stateDer] -= static_cast( + _invMM(Node, 0) * (state[Cell * strideCell_state] - _surfaceFlux[Cell]) - + _invMM(Node, _polyDeg) * + (state[Cell * strideCell_state + _polyDeg * strideNode_state] - _surfaceFlux[(Cell + 1)])); } + } + } + else + { // collocated numerical integration -> diagonal mass matrix + for (unsigned int Cell = 0; Cell < _nCells; Cell++) + { + // strong surface integral -> M^-1 B [state - state*] + stateDer[Cell * strideCell_stateDer] // first cell, node + -= + static_cast(_invWeights[0] * (state[Cell * strideCell_state] - _surfaceFlux(Cell))); + + stateDer[Cell * strideCell_stateDer + _polyDeg * strideNode_stateDer] // last cell, node + += static_cast( + _invWeights[_polyDeg] * + (state[Cell * strideCell_state + _polyDeg * strideNode_state] - _surfaceFlux(Cell + 1))); + } + } + } + /** + * @brief computes ghost nodes to implement boundary conditions + * @detail to implement Danckwert boundary conditions, we only need to set the solid wall BC values for auxiliary + * variable + */ + template void calcBoundaryValues() + { + // cache.boundary[0] = c_in -> inlet DOF already set + //_boundary[1] = (_velocity >= 0.0) ? C[_nPoints - 1] : C[0]; // c_r outlet not required in Danckwerts BC + _boundary[2] = + -reinterpret_cast(_auxState)[0]; // g_l left boundary (inlet/outlet for forward/backward flow) + _boundary[3] = -reinterpret_cast( + _auxState)[_nPoints - 1]; // g_r right boundary (outlet/inlet for forward/backward flow) + } + + // ========================================================================================================================================================== + // // + // ======================================== DG Jacobian + // ========================================================= // + // ========================================================================================================================================================== + // // + + /** + * @brief sets the sparsity pattern of the convection dispersion Jacobian for the nodal DG scheme + */ + int ConvDispNodalPattern(std::vector& tripletList, const int offC = 0) + { - /** - * @brief calculates the DG Jacobian auxiliary block - * @param [in] exInt true if exact integration DG scheme - * @param [in] cellIdx cell index - */ - Eigen::MatrixXd getGBlock(unsigned int cellIdx) { - - // Auxiliary Block [ d g(c) / d c ], additionally depends on boundary entries of neighbouring cells - Eigen::MatrixXd gBlock = Eigen::MatrixXd::Zero(_nNodes, _nNodes + 2); - gBlock.block(0, 1, _nNodes, _nNodes) = _polyDerM; - if (_exactInt) { - if (cellIdx != 1 && cellIdx != _nCells) { - gBlock.block(0, 0, _nNodes, 1) -= 0.5 * _invMM.block(0, 0, _nNodes, 1); - gBlock.block(0, 1, _nNodes, 1) += 0.5 * _invMM.block(0, 0, _nNodes, 1); - gBlock.block(0, _nNodes, _nNodes, 1) -= 0.5 * _invMM.block(0, _nNodes - 1, _nNodes, 1); - gBlock.block(0, _nNodes + 1, _nNodes, 1) += 0.5 * _invMM.block(0, _nNodes - 1, _nNodes, 1); - } - else if (cellIdx == 1) { // left - if (cellIdx == _nCells) - return gBlock * 2 / static_cast(_deltaZ); - ; - gBlock.block(0, _nNodes, _nNodes, 1) -= 0.5 * _invMM.block(0, _nNodes - 1, _nNodes, 1); - gBlock.block(0, _nNodes + 1, _nNodes, 1) += 0.5 * _invMM.block(0, _nNodes - 1, _nNodes, 1); - } - else if (cellIdx == _nCells) { // right - gBlock.block(0, 0, _nNodes, 1) -= 0.5 * _invMM.block(0, 0, _nNodes, 1); - gBlock.block(0, 1, _nNodes, 1) += 0.5 * _invMM.block(0, 0, _nNodes, 1); - } - else if (cellIdx == 0 || cellIdx == _nCells + 1) { - gBlock.setZero(); - } - gBlock *= 2 / static_cast(_deltaZ); + /*======================================================*/ + /* Define Convection Jacobian Block */ + /*======================================================*/ + + // Convection block [ d RHS_conv / d c ], also depends on upwind entry + + if (_curVelocity >= 0.0) + { // forward flow upwind entry -> last node of previous cell + // special inlet DOF treatment for inlet boundary cell (first cell) + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + // tripletList.push_back(T(offC + comp * sComp + i * sNode, comp * sComp, 0.0)); // inlet DOFs not + // included in Jacobian + for (unsigned int j = 1; j < _nNodes + 1; j++) + { + tripletList.push_back(T(offC + comp * strideColComp() + i * strideColNode(), + offC + comp * strideColComp() + (j - 1) * strideColNode(), 0.0)); } - else { - if (cellIdx == 0 || cellIdx == _nCells + 1) - return Eigen::MatrixXd::Zero(_nNodes, _nNodes + 2); - - gBlock(0, 0) -= 0.5 * _invWeights[0]; - gBlock(0, 1) += 0.5 * _invWeights[0]; - gBlock(_nNodes - 1, _nNodes) -= 0.5 * _invWeights[_nNodes - 1]; - gBlock(_nNodes - 1, _nNodes + 1) += 0.5 * _invWeights[_nNodes - 1]; - gBlock *= 2 / static_cast(_deltaZ); - - if (cellIdx == 1) { - // adjust auxiliary Block [ d g(c) / d c ] for left boundary cell - gBlock(0, 1) -= 0.5 * _invWeights[0] * 2 / static_cast(_deltaZ); - if (cellIdx == _nCells) { // adjust for special case one cell - gBlock(0, 0) += 0.5 * _invWeights[0] * 2 / static_cast(_deltaZ); - gBlock(_nNodes - 1, _nNodes + 1) -= 0.5 * _invWeights[_nNodes - 1] * 2 / static_cast(_deltaZ); - gBlock(_nNodes - 1, _nNodes) += 0.5 * _invWeights[_polyDeg] * 2 / static_cast(_deltaZ); - } - } - else if (cellIdx == _nCells) { - // adjust auxiliary Block [ d g(c) / d c ] for right boundary cell - gBlock(_nNodes - 1, _nNodes) += 0.5 * _invWeights[_polyDeg] * 2 / static_cast(_deltaZ); + } + } + for (unsigned int cell = 1; cell < _nCells; cell++) + { + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + for (unsigned int j = 0; j < _nNodes + 1; j++) + { + // row: jump over inlet DOFs and previous cells, add component offset and go node strides + // from there for each convection block entry col: jump over inlet DOFs and previous cells, + // go back one node, add component offset and go node strides from there for each convection + // block entry + tripletList.push_back( + T(offC + cell * strideColCell() + comp * strideColComp() + i * strideColNode(), + offC + cell * strideColCell() - strideColNode() + comp * strideColComp() + + j * strideColNode(), + 0.0)); } } - - return gBlock; } - /** - * @brief calculates the num. flux part of a dispersion DG Jacobian block - * @param [in] cellIdx cell index - * @param [in] leftG left neighbour auxiliary block - * @param [in] middleG neighbour auxiliary block - * @param [in] rightG neighbour auxiliary block - */ - Eigen::MatrixXd auxBlockGstar(unsigned int cellIdx, Eigen::MatrixXd leftG, Eigen::MatrixXd middleG, Eigen::MatrixXd rightG) { - - // auxiliary block [ d g^* / d c ], depends on whole previous and subsequent cell plus first entries of subsubsequent cells - Eigen::MatrixXd gStarDC = Eigen::MatrixXd::Zero(_nNodes, 3 * _nNodes + 2); - // NOTE: N = polyDeg - // indices gStarDC : 0 , 1 , ..., _nNodes; _nNodes+1, ..., 2 * _nNodes; 2*_nNodes+1, ..., 3 * _nNodes; 3*_nNodes+1 - // derivative index j : -(N+1)-1, -(N+1),... , -1 ; 0 , ..., N ; N + 1 , ..., 2N + 2 ; 2(N+1) +1 - // auxiliary block [d g^* / d c] - if (cellIdx != 1) { - gStarDC.block(0, _nNodes, 1, _nNodes + 2) += middleG.block(0, 0, 1, _nNodes + 2); - gStarDC.block(0, 0, 1, _nNodes + 2) += leftG.block(_nNodes - 1, 0, 1, _nNodes + 2); + } + } + else + { // backward flow upwind entry -> first node of subsequent cell + // special inlet DOF treatment for inlet boundary cell (last cell) + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + // inlet DOFs not included in Jacobian + for (unsigned int j = 0; j < _nNodes; j++) + { + tripletList.push_back( + T(offC + (_nCells - 1) * strideColCell() + comp * strideColComp() + i * strideColNode(), + offC + (_nCells - 1) * strideColCell() + comp * strideColComp() + j * strideColNode(), + 0.0)); } - if (cellIdx != _nCells) { - gStarDC.block(_nNodes - 1, _nNodes, 1, _nNodes + 2) += middleG.block(_nNodes - 1, 0, 1, _nNodes + 2); - gStarDC.block(_nNodes - 1, 2 * _nNodes, 1, _nNodes + 2) += rightG.block(0, 0, 1, _nNodes + 2); + } + } + for (unsigned int cell = 0; cell < _nCells - 1u; cell++) + { + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + for (unsigned int j = 0; j < _nNodes + 1; j++) + { + // row: jump over inlet DOFs and previous cells, add component offset and go node strides + // from there for each convection block entry col: jump over inlet DOFs and previous cells, + // add component offset and go node strides from there for each convection block entry + tripletList.push_back( + T(offC + cell * strideColCell() + comp * strideColComp() + i * strideColNode(), + offC + cell * strideColCell() + comp * strideColComp() + j * strideColNode(), 0.0)); + } } - gStarDC *= 0.5; - - return gStarDC; } + } + } + + /*======================================================*/ + /* Define Dispersion Jacobian Block */ + /*======================================================*/ - Eigen::MatrixXd getBMatrix() { + /* Inner cell dispersion blocks */ - Eigen::MatrixXd B = Eigen::MatrixXd::Zero(_nNodes, _nNodes); - B(0, 0) = -1.0; - B(_nNodes - 1, _nNodes - 1) = 1.0; + // Dispersion block [ d RHS_disp / d c ], depends on whole previous and subsequent cell - return B; + // insert Blocks to Jacobian inner cells (only for _nCells >= 3) + if (_nCells >= 3u) + { + for (unsigned int cell = 1; cell < _nCells - 1; cell++) + { + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + for (unsigned int j = 0; j < 3 * _nNodes; j++) + { + // pattern is more sparse than a nNodes x 3*nNodes block. + if ((j >= _nNodes - 1 && j <= 2 * _nNodes) || (i == 0 && j <= 2 * _nNodes) || + (i == _nNodes - 1 && j >= _nNodes - 1)) + // row: jump over inlet DOFs and previous cells, add component offset and go node + // strides from there for each entry col: jump over inlet DOFs and previous cells, go + // back one cell, add component offset and go node strides from there for each entry + tripletList.push_back(T( + offC + cell * strideColCell() + comp * strideColComp() + i * strideColNode(), + offC + (cell - 1) * strideColCell() + comp * strideColComp() + j * strideColNode(), + 0.0)); + } + } } + } + } - /** - * @brief calculates the dispersion part of the DG jacobian - * @param [in] exInt true if exact integration DG scheme - * @param [in] cellIdx cell index - */ - Eigen::MatrixXd DGjacobianDispBlock(unsigned int cellIdx) { + /* Boundary cell Dispersion blocks */ - int offC = 0; // inlet DOFs not included in Jacobian + if (_nCells != 1) + { // Note: special case _nCells = 1 already set by advection block + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + for (unsigned int j = _nNodes; j < 3 * _nNodes; j++) + { + // pattern is more sparse than a nNodes x 2*nNodes block. + if ((j >= _nNodes - 1 && j <= 2 * _nNodes) || (i == 0 && j <= 2 * _nNodes) || + (i == _nNodes - 1 && j >= _nNodes - 1)) + tripletList.push_back(T(offC + comp * strideColComp() + i * strideColNode(), + offC + comp * strideColComp() + (j - _nNodes) * strideColNode(), + 0.0)); + } + } + } - Eigen::MatrixXd dispBlock; + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + for (unsigned int j = 0; j < 2 * _nNodes; j++) + { + // pattern is more sparse than a nNodes x 2*nNodes block. + if ((j >= _nNodes - 1 && j <= 2 * _nNodes) || (i == 0 && j <= 2 * _nNodes) || + (i == _nNodes - 1 && j >= _nNodes - 1)) + tripletList.push_back( + T(offC + (_nCells - 1) * strideColCell() + comp * strideColComp() + i * strideColNode(), + offC + (_nCells - 1 - 1) * strideColCell() + comp * strideColComp() + + j * strideColNode(), + 0.0)); + } + } + } + } - if (_exactInt) { + return 0; + } - // Inner dispersion block [ d RHS_disp / d c ], depends on whole previous and subsequent cell plus first entries of subsubsequent cells - dispBlock = Eigen::MatrixXd::Zero(_nNodes, 3 * _nNodes + 2); + /** + * @brief sets the sparsity pattern of the convection dispersion Jacobian for the exact integration (her: modal) DG + * scheme + */ + int ConvDispModalPattern(std::vector& tripletList, const int offC = 0) + { - Eigen::MatrixXd B = getBMatrix(); // "Lifting" matrix - Eigen::MatrixXd gBlock = getGBlock(cellIdx); // current cell auxiliary block matrix - Eigen::MatrixXd gStarDC = auxBlockGstar(cellIdx, getGBlock(cellIdx - 1), gBlock, getGBlock(cellIdx + 1)); // Numerical flux block + /*======================================================*/ + /* Define Convection Jacobian Block */ + /*======================================================*/ - // indices dispBlock : 0 , 1 , ..., _nNodes; _nNodes+1, ..., 2 * _nNodes; 2*_nNodes+1, ..., 3 * _nNodes; 3*_nNodes+1 - // derivative index j : -(N+1)-1, -(N+1),..., -1 ; 0 , ..., N ; N + 1 , ..., 2N + 2 ; 2(N+1) +1 - dispBlock.block(0, _nNodes, _nNodes, _nNodes + 2) += _polyDerM * gBlock - _invMM * B * gBlock; - dispBlock += _invMM * B * gStarDC; - dispBlock *= 2 / static_cast(_deltaZ); - } - else { // inexact integration collocation DGSEM - - dispBlock = Eigen::MatrixXd::Zero(_nNodes, 3 * _nNodes); - Eigen::MatrixXd GBlockLeft = getGBlock(cellIdx - 1); - Eigen::MatrixXd GBlock = getGBlock(cellIdx); - Eigen::MatrixXd GBlockRight = getGBlock(cellIdx + 1); - - // Dispersion block [ d RHS_disp / d c ], depends on whole previous and subsequent cell - // NOTE: N = polyDeg - // cell indices : 0 , ..., _nNodes - 1; _nNodes, ..., 2 * _nNodes - 1; 2 * _nNodes, ..., 3 * _nNodes - 1 - // j : -N-1, ..., -1 ; 0 , ..., N ; N + 1, ..., 2N + 1 - dispBlock.block(0, _nNodes - 1, _nNodes, _nNodes + 2) = _polyDerM * GBlock; - - if (cellIdx > 1) { - dispBlock(0, _nNodes - 1) += -_invWeights[0] * (-0.5 * GBlock(0, 0) + 0.5 * GBlockLeft(_nNodes - 1, _nNodes)); // G_N,N i=0, j=-1 - dispBlock(0, _nNodes) += -_invWeights[0] * (-0.5 * GBlock(0, 1) + 0.5 * GBlockLeft(_nNodes - 1, _nNodes + 1)); // G_N,N+1 i=0, j=0 - dispBlock.block(0, _nNodes + 1, 1, _nNodes) += -_invWeights[0] * (-0.5 * GBlock.block(0, 2, 1, _nNodes)); // G_i,j i=0, j=1,...,N+1 - dispBlock.block(0, 0, 1, _nNodes - 1) += -_invWeights[0] * (0.5 * GBlockLeft.block(_nNodes - 1, 1, 1, _nNodes - 1)); // G_N,j+N+1 i=0, j=-N-1,...,-2 - } - else if (cellIdx == 1) { // left boundary cell - dispBlock.block(0, _nNodes - 1, 1, _nNodes + 2) += -_invWeights[0] * (-GBlock.block(0, 0, 1, _nNodes + 2)); // G_N,N i=0, j=-1,...,N+1 - } - if (cellIdx < _nCells) { - dispBlock.block(_nNodes - 1, _nNodes - 1, 1, _nNodes) += _invWeights[_nNodes - 1] * (-0.5 * GBlock.block(_nNodes - 1, 0, 1, _nNodes)); // G_i,j+N+1 i=N, j=-1,...,N-1 - dispBlock(_nNodes - 1, 2 * _nNodes - 1) += _invWeights[_nNodes - 1] * (-0.5 * GBlock(_nNodes - 1, _nNodes) + 0.5 * GBlockRight(0, 0)); // G_i,j i=N, j=N - dispBlock(_nNodes - 1, 2 * _nNodes) += _invWeights[_nNodes - 1] * (-0.5 * GBlock(_nNodes - 1, _nNodes + 1) + 0.5 * GBlockRight(0, 1)); // G_i,j i=N, j=N+1 - dispBlock.block(_nNodes - 1, 2 * _nNodes + 1, 1, _nNodes - 1) += _invWeights[_nNodes - 1] * (0.5 * GBlockRight.block(0, 2, 1, _nNodes - 1)); // G_0,j-N-1 i=N, j=N+2,...,2N+1 - } - else if (cellIdx == _nCells) { // right boundary cell - dispBlock.block(_nNodes - 1, _nNodes - 1, 1, _nNodes + 2) += _invWeights[_nNodes - 1] * (-GBlock.block(_nNodes - 1, 0, 1, _nNodes + 2)); // G_i,j+N+1 i=N, j=--1,...,N+1 - } + // Convection block [ d RHS_conv / d c ], also depends on upwind entry - dispBlock *= 2 / static_cast(_deltaZ); + if (_curVelocity >= 0.0) + { // forward flow upwind entry -> last node of previous cell + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + // tripletList.push_back(T(offC + comp * strideColComp() + i * strideColNode(), comp * + // strideColComp(), 0.0)); // inlet DOFs not included in Jacobian + for (unsigned int j = 1; j < _nNodes + 1; j++) + { + tripletList.push_back(T(offC + comp * strideColComp() + i * strideColNode(), + offC + comp * strideColComp() + (j - 1) * strideColNode(), 0.0)); } - - return -dispBlock; // *-1 for residual } - - /* =================================================================================== - * Residual functions - * =================================================================================== */ - /** - * @brief calculates the volume Integral of the auxiliary equation - * @param [in] state state vector - * @param [in, out] stateDer state derivative vector - * @detail performs matrix-vector multiplication optimized depending on state types and stores the result in stateDer. - */ - template - void volumeIntegral(Eigen::Map, 0, InnerStride>& state, Eigen::Map, 0, InnerStride>& stateDer) { - - for (unsigned int Cell = 0; Cell < _nCells; Cell++) { - - // exploit Eigen3 performance if no mixed scalar types - if constexpr (std::is_same_v) { - if constexpr (std::is_same_v) { - stateDer.segment(Cell * _nNodes, _nNodes) -= _polyDerM * state.segment(Cell * _nNodes, _nNodes); - } - else { - stateDer.segment(Cell * _nNodes, _nNodes) -= (_polyDerM * state.segment(Cell * _nNodes, _nNodes)).template cast(); - } - } - else { // both active types - // todo use custom (mixed scalar-type) matrix vector multiplication? - stateDer.segment(Cell * _nNodes, _nNodes) -= _polyDerM.template cast() * state.segment(Cell * _nNodes, _nNodes); + } + for (unsigned int cell = 1; cell < _nCells; cell++) + { + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + for (unsigned int j = 0; j < _nNodes + 1; j++) + { + // row: jump over inlet DOFs and previous cells, add component offset and go node strides + // from there for each convection block entry col: jump over inlet DOFs and previous cells, + // go back one node, add component offset and go node strides from there for each convection + // block entry + tripletList.push_back( + T(offC + cell * strideColCell() + comp * strideColComp() + i * strideColNode(), + offC + cell * strideColCell() - strideColNode() + comp * strideColComp() + + j * strideColNode(), + 0.0)); } } } - template - void volumeIntegral(Eigen::Map, 0, InnerStride>& state, Eigen::Map, 0, InnerStride>& stateDer) { - Eigen::Map, 0, InnerStride> state_const(state.data(), state.size(), InnerStride(1)); - volumeIntegral(state_const, stateDer); + } + } + else + { // backward flow upwind entry -> first node of subsequent cell + // special inlet DOF treatment for inlet boundary cell (last cell) + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + // inlet DOFs not included in Jacobian + for (unsigned int j = 0; j < _nNodes; j++) + { + tripletList.push_back( + T(offC + (_nCells - 1) * strideColCell() + comp * strideColComp() + i * strideColNode(), + offC + (_nCells - 1) * strideColCell() + comp * strideColComp() + j * strideColNode(), + 0.0)); + } } - /* - * @brief calculates the interface fluxes h* of Convection Dispersion equation - */ - template - void InterfaceFlux(const StateType* C, ParamType _dispersion) { - - StateType* g = reinterpret_cast(_auxState); - - // component-wise strides - unsigned int strideNode = _strideNode; - unsigned int strideCell = _nNodes * strideNode; - unsigned int strideNode_g = 1u; - unsigned int strideCell_g = _nNodes * strideNode_g; - - // Conv.Disp. flux: h* = h*_conv + h*_disp = v c_up + 0.5 sqrt(D_ax) (S_l + S_r) - - if (_curVelocity >= 0.0) { // forward flow (upwind num. flux) - // calculate inner interface fluxes - for (unsigned int Cell = 1; Cell < _nCells; Cell++) { - // h* = h*_conv + h*_disp - _surfaceFlux[Cell] // inner interfaces - = _curVelocity * (C[Cell * strideCell - strideNode]) // left cell (i.e. forward flow upwind) - - 0.5 * (-2.0 / static_cast(_deltaZ)) * _dispersion * - (g[Cell * strideCell_g - strideNode_g] // left cell - + g[Cell * strideCell_g]); // right cell + } + for (unsigned int cell = 0; cell < _nCells - 1u; cell++) + { + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + for (unsigned int j = 0; j < _nNodes + 1; j++) + { + // row: jump over inlet DOFs and previous cells, add component offset and go node strides + // from there for each convection block entry col: jump over inlet DOFs and previous cells, + // add component offset and go node strides from there for each convection block entry + tripletList.push_back( + T(offC + cell * strideColCell() + comp * strideColComp() + i * strideColNode(), + offC + cell * strideColCell() + comp * strideColComp() + j * strideColNode(), 0.0)); } - - // boundary fluxes - // inlet (left) boundary interface - _surfaceFlux[0] - = _curVelocity * _boundary[0]; - - // outlet (right) boundary interface - _surfaceFlux[_nCells] - = _curVelocity * (C[_nCells * strideCell - strideNode]) - - 0.5 * (-2.0 / static_cast(_deltaZ)) * _dispersion * - (g[_nCells * strideCell_g - strideNode_g] // last cell last node - + _boundary[3]); // right boundary value g } - else { // backward flow (upwind num. flux) - // calculate inner interface fluxes - for (unsigned int Cell = 1; Cell < _nCells; Cell++) { - // h* = h*_conv + h*_disp - _surfaceFlux[Cell] // inner interfaces - = _curVelocity * (C[Cell * strideCell]) // right cell (i.e. backward flow upwind) - - 0.5 * (-2.0 / static_cast(_deltaZ)) * _dispersion * - (g[Cell * strideCell_g - strideNode_g] // left cell - + g[Cell * strideCell_g]); // right cell - } + } + } + } + + /*======================================================*/ + /* Define Dispersion Jacobian Block */ + /*======================================================*/ - // boundary fluxes - // inlet boundary interface - _surfaceFlux[_nCells] - = _curVelocity * _boundary[0]; - - // outlet boundary interface - _surfaceFlux[0] - = _curVelocity * (C[0]) - - 0.5 * (-2.0 / static_cast(_deltaZ)) * _dispersion * - (g[0] // first cell first node - + _boundary[2]); // left boundary value g + /* Inner cells */ + if (_nCells >= 5u) + { + // Inner dispersion block [ d RHS_disp / d c ], depends on whole previous and subsequent cell plus first + // entries of subsubsequent cells + for (unsigned int cell = 2; cell < _nCells - 2; cell++) + { + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + for (unsigned int j = 0; j < 3 * _nNodes + 2; j++) + { + // row: jump over inlet DOFs and previous cells, add component offset and go node strides + // from there for each dispersion block entry col: jump over inlet DOFs and previous cells, + // go back one cell and one node, add component offset and go node strides from there for + // each dispersion block entry + tripletList.push_back( + T(offC + cell * strideColCell() + comp * strideColComp() + i * strideColNode(), + offC + cell * strideColCell() - (_nNodes + 1) * strideColNode() + + comp * strideColComp() + j * strideColNode(), + 0.0)); + } } - // apply inverse mapping jacobian (reference space) - _surfaceFlux *= -2.0 / static_cast(_deltaZ); } - /** - * @brief calculates and fills the surface flux values for auxiliary equation - * @param [in] C bulk liquid phase - * @param [in] strideNode node stride w.r.t. C - * @param [in] strideCell cell stride w.r.t. C - */ - template - void InterfaceFluxAuxiliary(const StateType* C, unsigned int strideNode, unsigned int strideCell) { - - // Auxiliary flux: c* = 0.5 (c_l + c_r) - - // calculate inner interface fluxes - for (unsigned int Cell = 1; Cell < _nCells; Cell++) { - _surfaceFlux[Cell] // left interfaces - = 0.5 * (C[Cell * strideCell - strideNode] + // left node - C[Cell * strideCell]); // right node - } - // calculate boundary interface fluxes + } + } - _surfaceFlux[0] // left boundary interface - = 0.5 * (C[0] + // boundary value - C[0]); // first cell first node + /* boundary cell neighbours */ - _surfaceFlux[(_nCells)] // right boundary interface - = 0.5 * (C[_nCells * strideCell - strideNode] + // last cell last node - C[_nCells * strideCell - strideNode]);// // boundary value - } - /** - * @brief calculates the string form surface Integral - * @param [in] state relevant state vector - * @param [in] stateDer state derivative vector the solution is added to - * @param [in] strideNode_state node stride w.r.t. state - * @param [in] strideCell_state cell stride w.r.t. state - * @param [in] strideNode_stateDer node stride w.r.t. stateDer - * @param [in] strideCell_stateDer cell stride w.r.t. stateDer - * @detail calculates stateDer = M^-1 * B * (state - state^*) and exploits LGL sparsity if applied - */ - template - void surfaceIntegral(const StateType* state, ResidualType* stateDer, - unsigned int strideNode_state, unsigned int strideCell_state, - unsigned int strideNode_stateDer, unsigned int strideCell_stateDer) { - - if (_exactInt) { // non-collocated integration -> dense mass matrix - for (unsigned int Cell = 0; Cell < _nCells; Cell++) { - // strong surface integral -> M^-1 B [state - state*] - for (unsigned int Node = 0; Node < _nNodes; Node++) { - stateDer[Cell * strideCell_stateDer + Node * strideNode_stateDer] - -= static_cast(_invMM(Node, 0) * (state[Cell * strideCell_state] - _surfaceFlux[Cell]) - - _invMM(Node, _polyDeg) * (state[Cell * strideCell_state + _polyDeg * strideNode_state] - _surfaceFlux[(Cell + 1)])); - } - } + // left boundary cell neighbour + if (_nCells >= 4u) + { + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + for (unsigned int j = 1; j < 3 * _nNodes + 2; j++) + { + // row: jump over inlet DOFs and previous cell, add component offset and go node strides from + // there for each dispersion block entry col: jump over inlet DOFs, add component offset and go + // node strides from there for each dispersion block entry. Also adjust for iterator j (-1) + tripletList.push_back( + T(offC + _nNodes * strideColNode() + comp * strideColComp() + i * strideColNode(), + offC + comp * strideColComp() + (j - 1) * strideColNode(), 0.0)); } - else { // collocated numerical integration -> diagonal mass matrix - for (unsigned int Cell = 0; Cell < _nCells; Cell++) { - // strong surface integral -> M^-1 B [state - state*] - stateDer[Cell * strideCell_stateDer] // first cell, node - -= static_cast(_invWeights[0] - * (state[Cell * strideCell_state] - _surfaceFlux(Cell))); - - stateDer[Cell * strideCell_stateDer + _polyDeg * strideNode_stateDer] // last cell, node - += static_cast(_invWeights[_polyDeg] - * (state[Cell * strideCell_state + _polyDeg * strideNode_state] - _surfaceFlux(Cell + 1))); - } + } + } + } + else if (_nCells == 3u) + { // special case: only depends on the two neighbouring cells + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + for (unsigned int j = 1; j < 3 * _nNodes + 1; j++) + { + // row: jump over inlet DOFs and previous cell, add component offset and go node strides from + // there for each dispersion block entry col: jump over inlet DOFs, add component offset and go + // node strides from there for each dispersion block entry. Also adjust for iterator j (-1) + tripletList.push_back( + T(offC + _nNodes * strideColNode() + comp * strideColComp() + i * strideColNode(), + offC + comp * strideColComp() + (j - 1) * strideColNode(), 0.0)); } } - /** - * @brief computes ghost nodes to implement boundary conditions - * @detail to implement Danckwert boundary conditions, we only need to set the solid wall BC values for auxiliary variable - */ - template - void calcBoundaryValues() { - //cache.boundary[0] = c_in -> inlet DOF already set - //_boundary[1] = (_velocity >= 0.0) ? C[_nPoints - 1] : C[0]; // c_r outlet not required in Danckwerts BC - _boundary[2] = -reinterpret_cast(_auxState)[0]; // g_l left boundary (inlet/outlet for forward/backward flow) - _boundary[3] = -reinterpret_cast(_auxState)[_nPoints - 1]; // g_r right boundary (outlet/inlet for forward/backward flow) + } + } + // right boundary cell neighbour + if (_nCells >= 4u) + { + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + for (unsigned int j = 0; j < 3 * _nNodes + 2 - 1; j++) + { + // row: jump over inlet DOFs and previous cells, add component offset and go node strides from + // there for each dispersion block entry col: jump over inlet DOFs and previous cells, go back + // one cell and one node, add component offset and go node strides from there for each + // dispersion block entry. + tripletList.push_back( + T(offC + (_nCells - 2) * strideColCell() + comp * strideColComp() + i * strideColNode(), + offC + (_nCells - 2) * strideColCell() - (_nNodes + 1) * strideColNode() + + comp * strideColComp() + j * strideColNode(), + 0.0)); + } } - - // ========================================================================================================================================================== // - // ======================================== DG Jacobian ========================================================= // - // ========================================================================================================================================================== // - - /** - * @brief sets the sparsity pattern of the convection dispersion Jacobian for the nodal DG scheme - */ - int ConvDispNodalPattern(std::vector& tripletList, const int offC = 0) { - - /*======================================================*/ - /* Define Convection Jacobian Block */ - /*======================================================*/ - - // Convection block [ d RHS_conv / d c ], also depends on upwind entry - - if (_curVelocity >= 0.0) { // forward flow upwind entry -> last node of previous cell - // special inlet DOF treatment for inlet boundary cell (first cell) - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - //tripletList.push_back(T(offC + comp * sComp + i * sNode, comp * sComp, 0.0)); // inlet DOFs not included in Jacobian - for (unsigned int j = 1; j < _nNodes + 1; j++) { - tripletList.push_back(T(offC + comp * strideColComp() + i * strideColNode(), - offC + comp * strideColComp() + (j - 1) * strideColNode(), - 0.0)); - } - } - } - for (unsigned int cell = 1; cell < _nCells; cell++) { - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - for (unsigned int j = 0; j < _nNodes + 1; j++) { - // row: jump over inlet DOFs and previous cells, add component offset and go node strides from there for each convection block entry - // col: jump over inlet DOFs and previous cells, go back one node, add component offset and go node strides from there for each convection block entry - tripletList.push_back(T(offC + cell * strideColCell() + comp * strideColComp() + i * strideColNode(), - offC + cell * strideColCell() - strideColNode() + comp * strideColComp() + j * strideColNode(), + } + } + /* boundary cells */ + + // left boundary cell + unsigned int end = 3u * _nNodes + 2u; + if (_nCells == 1u) + end = 2u * _nNodes + 1u; + else if (_nCells == 2u) + end = 3u * _nNodes + 1u; + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + for (unsigned int j = _nNodes + 1; j < end; j++) + { + // row: jump over inlet DOFs, add component offset and go node strides from there for each + // dispersion block entry col: jump over inlet DOFs, add component offset, adjust for iterator j + // (-Nnodes-1) and go node strides from there for each dispersion block entry. + tripletList.push_back(T(offC + comp * strideColComp() + i * strideColNode(), + offC + comp * strideColComp() + (j - (_nNodes + 1)) * strideColNode(), 0.0)); - } - } - } - } + } + } + } + // right boundary cell + if (_nCells >= 3u) + { + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + for (unsigned int j = 0; j < 2 * _nNodes + 1; j++) + { + // row: jump over inlet DOFs and previous cells, add component offset and go node strides from + // there for each dispersion block entry col: jump over inlet DOFs and previous cells, go back + // one cell and one node, add component offset and go node strides from there for each + // dispersion block entry. + tripletList.push_back( + T(offC + (_nCells - 1) * strideColCell() + comp * strideColComp() + i * strideColNode(), + offC + (_nCells - 1) * strideColCell() - (_nNodes + 1) * strideColNode() + + comp * strideColComp() + j * strideColNode(), + 0.0)); } - else { // backward flow upwind entry -> first node of subsequent cell - // special inlet DOF treatment for inlet boundary cell (last cell) - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - // inlet DOFs not included in Jacobian - for (unsigned int j = 0; j < _nNodes; j++) { - tripletList.push_back(T(offC + (_nCells - 1) * strideColCell() + comp * strideColComp() + i * strideColNode(), - offC + (_nCells - 1) * strideColCell() + comp * strideColComp() + j * strideColNode(), - 0.0)); - } - } - } - for (unsigned int cell = 0; cell < _nCells - 1u; cell++) { - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - for (unsigned int j = 0; j < _nNodes + 1; j++) { - // row: jump over inlet DOFs and previous cells, add component offset and go node strides from there for each convection block entry - // col: jump over inlet DOFs and previous cells, add component offset and go node strides from there for each convection block entry - tripletList.push_back(T(offC + cell * strideColCell() + comp * strideColComp() + i * strideColNode(), - offC + cell * strideColCell() + comp * strideColComp() + j * strideColNode(), - 0.0)); - } - } - } - } + } + } + } + else if (_nCells == 2u) + { // special case for _nCells == 2: depends only on left cell + for (unsigned int comp = 0; comp < _nComp; comp++) + { + for (unsigned int i = 0; i < _nNodes; i++) + { + for (unsigned int j = 0; j < 2 * _nNodes; j++) + { + // row: jump over inlet DOFs and previous cells, add component offset and go node strides from + // there for each dispersion block entry col: jump over inlet DOFs and previous cells, go back + // one cell, add component offset and go node strides from there for each dispersion block + // entry. + tripletList.push_back( + T(offC + (_nCells - 1) * strideColCell() + comp * strideColComp() + i * strideColNode(), + offC + (_nCells - 1) * strideColCell() - _nNodes * strideColNode() + + comp * strideColComp() + j * strideColNode(), + 0.0)); } + } + } + } + + return 0; + } + /** + * @brief analytically calculates the convection dispersion jacobian for the nodal DG scheme + */ + int calcConvDispCollocationDGSEMJacobian(Eigen::SparseMatrix& jacobian, + Eigen::MatrixXd& jacInlet, const int offC = 0) + { - /*======================================================*/ - /* Define Dispersion Jacobian Block */ - /*======================================================*/ - - /* Inner cell dispersion blocks */ - - // Dispersion block [ d RHS_disp / d c ], depends on whole previous and subsequent cell - - // insert Blocks to Jacobian inner cells (only for _nCells >= 3) - if (_nCells >= 3u) { - for (unsigned int cell = 1; cell < _nCells - 1; cell++) { - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - for (unsigned int j = 0; j < 3 * _nNodes; j++) { - // pattern is more sparse than a nNodes x 3*nNodes block. - if ((j >= _nNodes - 1 && j <= 2 * _nNodes) || - (i == 0 && j <= 2 * _nNodes) || - (i == _nNodes - 1 && j >= _nNodes - 1)) - // row: jump over inlet DOFs and previous cells, add component offset and go node strides from there for each entry - // col: jump over inlet DOFs and previous cells, go back one cell, add component offset and go node strides from there for each entry - tripletList.push_back(T(offC + cell * strideColCell() + comp * strideColComp() + i * strideColNode(), - offC + (cell - 1) * strideColCell() + comp * strideColComp() + j * strideColNode(), - 0.0)); - } - } - } - } - } + const int strideColBound = strideColNode() - _nComp; - /* Boundary cell Dispersion blocks */ - - if (_nCells != 1) { // Note: special case _nCells = 1 already set by advection block - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - for (unsigned int j = _nNodes; j < 3 * _nNodes; j++) { - // pattern is more sparse than a nNodes x 2*nNodes block. - if ((j >= _nNodes - 1 && j <= 2 * _nNodes) || - (i == 0 && j <= 2 * _nNodes) || - (i == _nNodes - 1 && j >= _nNodes - 1)) - tripletList.push_back(T(offC + comp * strideColComp() + i * strideColNode(), - offC + comp * strideColComp() + (j - _nNodes) * strideColNode(), - 0.0)); - } - } - } + /*======================================================*/ + /* Compute Dispersion Jacobian Block */ + /*======================================================*/ - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - for (unsigned int j = 0; j < 2 * _nNodes; j++) { - // pattern is more sparse than a nNodes x 2*nNodes block. - if ((j >= _nNodes - 1 && j <= 2 * _nNodes) || - (i == 0 && j <= 2 * _nNodes) || - (i == _nNodes - 1 && j >= _nNodes - 1)) - tripletList.push_back(T(offC + (_nCells - 1) * strideColCell() + comp * strideColComp() + i * strideColNode(), - offC + (_nCells - 1 - 1) * strideColCell() + comp * strideColComp() + j * strideColNode(), - 0.0)); - } - } - } - } + /* Inner cell dispersion blocks */ - return 0; - } + if (_nCells >= 3u) + { + MatrixXd dispBlock = _DGjacAxDispBlocks[1]; + linalg::BandedEigenSparseRowIterator jacIt( + jacobian, offC + strideColCell()); // row iterator starting at second cell and component - /** - * @brief sets the sparsity pattern of the convection dispersion Jacobian for the exact integration (her: modal) DG scheme - */ - int ConvDispModalPattern(std::vector& tripletList, const int offC = 0) { - - /*======================================================*/ - /* Define Convection Jacobian Block */ - /*======================================================*/ - - // Convection block [ d RHS_conv / d c ], also depends on upwind entry - - if (_curVelocity >= 0.0) { // forward flow upwind entry -> last node of previous cell - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - //tripletList.push_back(T(offC + comp * strideColComp() + i * strideColNode(), comp * strideColComp(), 0.0)); // inlet DOFs not included in Jacobian - for (unsigned int j = 1; j < _nNodes + 1; j++) { - tripletList.push_back(T(offC + comp * strideColComp() + i * strideColNode(), - offC + comp * strideColComp() + (j - 1) * strideColNode(), - 0.0)); - } - } - } - for (unsigned int cell = 1; cell < _nCells; cell++) { - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - for (unsigned int j = 0; j < _nNodes + 1; j++) { - // row: jump over inlet DOFs and previous cells, add component offset and go node strides from there for each convection block entry - // col: jump over inlet DOFs and previous cells, go back one node, add component offset and go node strides from there for each convection block entry - tripletList.push_back(T(offC + cell * strideColCell() + comp * strideColComp() + i * strideColNode(), - offC + cell * strideColCell() - strideColNode() + comp * strideColComp() + j * strideColNode(), - 0.0)); - } - } - } - } - } - else { // backward flow upwind entry -> first node of subsequent cell - // special inlet DOF treatment for inlet boundary cell (last cell) - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - // inlet DOFs not included in Jacobian - for (unsigned int j = 0; j < _nNodes; j++) { - tripletList.push_back(T(offC + (_nCells - 1) * strideColCell() + comp * strideColComp() + i * strideColNode(), - offC + (_nCells - 1) * strideColCell() + comp * strideColComp() + j * strideColNode(), - 0.0)); - } - } - } - for (unsigned int cell = 0; cell < _nCells - 1u; cell++) { - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - for (unsigned int j = 0; j < _nNodes + 1; j++) { - // row: jump over inlet DOFs and previous cells, add component offset and go node strides from there for each convection block entry - // col: jump over inlet DOFs and previous cells, add component offset and go node strides from there for each convection block entry - tripletList.push_back(T(offC + cell * strideColCell() + comp * strideColComp() + i * strideColNode(), - offC + cell * strideColCell() + comp * strideColComp() + j * strideColNode(), - 0.0)); - } - } - } + for (unsigned int cell = 1; cell < _nCells - 1; cell++) + { + for (unsigned int i = 0; i < dispBlock.rows(); i++, jacIt += strideColBound) + { + for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) + { + for (unsigned int j = 0; j < dispBlock.cols(); j++) + { + // pattern is more sparse than a nNodes x 3*nNodes block. + if ((j >= _nNodes - 1 && j <= 2 * _nNodes) || (i == 0 && j <= 2 * _nNodes) || + (i == _nNodes - 1 && j >= _nNodes - 1)) + // row: iterator is at current node i and current component comp + // col: start at previous cell and jump to node j + jacIt[-strideColCell() + (j - i) * strideColNode()] = + dispBlock(i, j) * static_cast(currentDispersion(_curSection)[comp]); } } + } + } + } - /*======================================================*/ - /* Define Dispersion Jacobian Block */ - /*======================================================*/ - - /* Inner cells */ - if (_nCells >= 5u) { - // Inner dispersion block [ d RHS_disp / d c ], depends on whole previous and subsequent cell plus first entries of subsubsequent cells - for (unsigned int cell = 2; cell < _nCells - 2; cell++) { - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - for (unsigned int j = 0; j < 3 * _nNodes + 2; j++) { - // row: jump over inlet DOFs and previous cells, add component offset and go node strides from there for each dispersion block entry - // col: jump over inlet DOFs and previous cells, go back one cell and one node, add component offset and go node strides from there for each dispersion block entry - tripletList.push_back(T(offC + cell * strideColCell() + comp * strideColComp() + i * strideColNode(), - offC + cell * strideColCell() - (_nNodes + 1) * strideColNode() + comp * strideColComp() + j * strideColNode(), - 0.0)); - } - } - } - } - } + /* Boundary cell Dispersion blocks */ - /* boundary cell neighbours */ - - // left boundary cell neighbour - if (_nCells >= 4u) { - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - for (unsigned int j = 1; j < 3 * _nNodes + 2; j++) { - // row: jump over inlet DOFs and previous cell, add component offset and go node strides from there for each dispersion block entry - // col: jump over inlet DOFs, add component offset and go node strides from there for each dispersion block entry. Also adjust for iterator j (-1) - tripletList.push_back(T(offC + _nNodes * strideColNode() + comp * strideColComp() + i * strideColNode(), - offC + comp * strideColComp() + (j - 1) * strideColNode(), - 0.0)); - } - } - } - } - else if (_nCells == 3u) { // special case: only depends on the two neighbouring cells - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - for (unsigned int j = 1; j < 3 * _nNodes + 1; j++) { - // row: jump over inlet DOFs and previous cell, add component offset and go node strides from there for each dispersion block entry - // col: jump over inlet DOFs, add component offset and go node strides from there for each dispersion block entry. Also adjust for iterator j (-1) - tripletList.push_back(T(offC + _nNodes * strideColNode() + comp * strideColComp() + i * strideColNode(), - offC + comp * strideColComp() + (j - 1) * strideColNode(), - 0.0)); - } - } - } - } - // right boundary cell neighbour - if (_nCells >= 4u) { - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - for (unsigned int j = 0; j < 3 * _nNodes + 2 - 1; j++) { - // row: jump over inlet DOFs and previous cells, add component offset and go node strides from there for each dispersion block entry - // col: jump over inlet DOFs and previous cells, go back one cell and one node, add component offset and go node strides from there for each dispersion block entry. - tripletList.push_back(T(offC + (_nCells - 2) * strideColCell() + comp * strideColComp() + i * strideColNode(), - offC + (_nCells - 2) * strideColCell() - (_nNodes + 1) * strideColNode() + comp * strideColComp() + j * strideColNode(), - 0.0)); - } - } - } - } - /* boundary cells */ - - // left boundary cell - unsigned int end = 3u * _nNodes + 2u; - if (_nCells == 1u) end = 2u * _nNodes + 1u; - else if (_nCells == 2u) end = 3u * _nNodes + 1u; - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - for (unsigned int j = _nNodes + 1; j < end; j++) { - // row: jump over inlet DOFs, add component offset and go node strides from there for each dispersion block entry - // col: jump over inlet DOFs, add component offset, adjust for iterator j (-Nnodes-1) and go node strides from there for each dispersion block entry. - tripletList.push_back(T(offC + comp * strideColComp() + i * strideColNode(), - offC + comp * strideColComp() + (j - (_nNodes + 1)) * strideColNode(), - 0.0)); - } - } - } - // right boundary cell - if (_nCells >= 3u) { - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - for (unsigned int j = 0; j < 2 * _nNodes + 1; j++) { - // row: jump over inlet DOFs and previous cells, add component offset and go node strides from there for each dispersion block entry - // col: jump over inlet DOFs and previous cells, go back one cell and one node, add component offset and go node strides from there for each dispersion block entry. - tripletList.push_back(T(offC + (_nCells - 1) * strideColCell() + comp * strideColComp() + i * strideColNode(), - offC + (_nCells - 1) * strideColCell() - (_nNodes + 1) * strideColNode() + comp * strideColComp() + j * strideColNode(), - 0.0)); - } - } - } - } - else if (_nCells == 2u) { // special case for _nCells == 2: depends only on left cell - for (unsigned int comp = 0; comp < _nComp; comp++) { - for (unsigned int i = 0; i < _nNodes; i++) { - for (unsigned int j = 0; j < 2 * _nNodes; j++) { - // row: jump over inlet DOFs and previous cells, add component offset and go node strides from there for each dispersion block entry - // col: jump over inlet DOFs and previous cells, go back one cell, add component offset and go node strides from there for each dispersion block entry. - tripletList.push_back(T(offC + (_nCells - 1) * strideColCell() + comp * strideColComp() + i * strideColNode(), - offC + (_nCells - 1) * strideColCell() - _nNodes * strideColNode() + comp * strideColComp() + j * strideColNode(), - 0.0)); - } - } - } - } + /* left cell */ + MatrixXd dispBlock = _DGjacAxDispBlocks[0]; - return 0; - } - /** - * @brief analytically calculates the convection dispersion jacobian for the nodal DG scheme - */ - int calcConvDispCollocationDGSEMJacobian(Eigen::SparseMatrix& jacobian, Eigen::MatrixXd& jacInlet, const int offC = 0) { - - const int strideColBound = strideColNode() - _nComp; - - /*======================================================*/ - /* Compute Dispersion Jacobian Block */ - /*======================================================*/ - - /* Inner cell dispersion blocks */ - - if (_nCells >= 3u) { - MatrixXd dispBlock = _DGjacAxDispBlocks[1]; - linalg::BandedEigenSparseRowIterator jacIt(jacobian, offC + strideColCell()); // row iterator starting at second cell and component - - for (unsigned int cell = 1; cell < _nCells - 1; cell++) { - for (unsigned int i = 0; i < dispBlock.rows(); i++, jacIt += strideColBound) { - for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) { - for (unsigned int j = 0; j < dispBlock.cols(); j++) { - // pattern is more sparse than a nNodes x 3*nNodes block. - if ((j >= _nNodes - 1 && j <= 2 * _nNodes) || - (i == 0 && j <= 2 * _nNodes) || - (i == _nNodes - 1 && j >= _nNodes - 1)) - // row: iterator is at current node i and current component comp - // col: start at previous cell and jump to node j - jacIt[-strideColCell() + (j - i) * strideColNode()] = dispBlock(i, j) * static_cast(currentDispersion(_curSection)[comp]); - } - } - } - } - } + if (_nCells != 1u) + { // "standard" case + linalg::BandedEigenSparseRowIterator jacIt(jacobian, + offC); // row iterator starting at first cell and component - /* Boundary cell Dispersion blocks */ - - /* left cell */ - MatrixXd dispBlock = _DGjacAxDispBlocks[0]; - - if (_nCells != 1u) { // "standard" case - linalg::BandedEigenSparseRowIterator jacIt(jacobian, offC); // row iterator starting at first cell and component - - for (unsigned int i = 0; i < dispBlock.rows(); i++, jacIt += strideColBound) { - for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) { - for (unsigned int j = _nNodes; j < dispBlock.cols(); j++) { - // pattern is more sparse than a nNodes x 2*nNodes block. - if ((j >= _nNodes - 1 && j <= 2 * _nNodes) || - (i == 0 && j <= 2 * _nNodes) || - (i == _nNodes - 1 && j >= _nNodes - 1)) - // row: iterator is at current node i and current component comp - // col: jump to node j - jacIt[((j - _nNodes) - i) * strideColNode()] = dispBlock(i, j) * static_cast(currentDispersion(_curSection)[comp]); - } - } - } + for (unsigned int i = 0; i < dispBlock.rows(); i++, jacIt += strideColBound) + { + for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) + { + for (unsigned int j = _nNodes; j < dispBlock.cols(); j++) + { + // pattern is more sparse than a nNodes x 2*nNodes block. + if ((j >= _nNodes - 1 && j <= 2 * _nNodes) || (i == 0 && j <= 2 * _nNodes) || + (i == _nNodes - 1 && j >= _nNodes - 1)) + // row: iterator is at current node i and current component comp + // col: jump to node j + jacIt[((j - _nNodes) - i) * strideColNode()] = + dispBlock(i, j) * static_cast(currentDispersion(_curSection)[comp]); } - else { // special case - linalg::BandedEigenSparseRowIterator jacIt(jacobian, offC); // row iterator starting at first cell and component - for (unsigned int i = 0; i < dispBlock.rows(); i++, jacIt += strideColBound) { - for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) { - for (unsigned int j = _nNodes; j < _nNodes * 2u; j++) { - // row: iterator is at current node i and current component comp - // col: jump to node j - jacIt[((j - _nNodes) - i) * strideColNode()] = dispBlock(i, j) * static_cast(currentDispersion(_curSection)[comp]); - } - } - } + } + } + } + else + { // special case + linalg::BandedEigenSparseRowIterator jacIt(jacobian, + offC); // row iterator starting at first cell and component + for (unsigned int i = 0; i < dispBlock.rows(); i++, jacIt += strideColBound) + { + for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) + { + for (unsigned int j = _nNodes; j < _nNodes * 2u; j++) + { + // row: iterator is at current node i and current component comp + // col: jump to node j + jacIt[((j - _nNodes) - i) * strideColNode()] = + dispBlock(i, j) * static_cast(currentDispersion(_curSection)[comp]); } + } + } + } - /* right cell */ - if (_nCells != 1u) { // "standard" case - dispBlock = _DGjacAxDispBlocks[std::min(_nCells, 3u) - 1]; - linalg::BandedEigenSparseRowIterator jacIt(jacobian, offC + (_nCells - 1) * strideColCell()); // row iterator starting at last cell - - for (unsigned int i = 0; i < dispBlock.rows(); i++, jacIt += strideColBound) { - for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) { - for (unsigned int j = 0; j < 2 * _nNodes; j++) { - // pattern is more sparse than a nNodes x 2*nNodes block. - if ((j >= _nNodes - 1 && j <= 2 * _nNodes) || - (i == 0 && j <= 2 * _nNodes) || - (i == _nNodes - 1 && j >= _nNodes - 1)) - // row: iterator is at current node i and current component comp - // col: start at previous cell and jump to node j - jacIt[-strideColCell() + (j - i) * strideColNode()] = dispBlock(i, j) * static_cast(currentDispersion(_curSection)[comp]); - } - } - } - } + /* right cell */ + if (_nCells != 1u) + { // "standard" case + dispBlock = _DGjacAxDispBlocks[std::min(_nCells, 3u) - 1]; + linalg::BandedEigenSparseRowIterator jacIt( + jacobian, offC + (_nCells - 1) * strideColCell()); // row iterator starting at last cell - /*======================================================*/ - /* Compute Convection Jacobian Block */ - /*======================================================*/ - - // Convection block [ d RHS_conv / d c ], also depends on first entry of previous cell - MatrixXd convBlock = _DGjacAxConvBlock; - linalg::BandedEigenSparseRowIterator jacIt(jacobian, offC); // row iterator starting at first cell and component - - if (_curVelocity >= 0.0) { // forward flow upwind convection - // special inlet DOF treatment for first cell (inlet boundary cell) - jacInlet(0, 0) = static_cast(_curVelocity) * convBlock(0, 0); // only first node depends on inlet concentration - for (unsigned int i = 0; i < convBlock.rows(); i++, jacIt += strideColBound) { - for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) { - //jacIt[0] = -convBlock(i, 0); // dependency on inlet DOFs is handled in _jacInlet - for (unsigned int j = 1; j < convBlock.cols(); j++) { - jacIt[((j - 1) - i) * strideColNode()] += static_cast(_curVelocity) * convBlock(i, j); - } - } - } - // remaining cells - for (unsigned int cell = 1; cell < _nCells; cell++) { - for (unsigned int i = 0; i < convBlock.rows(); i++, jacIt += strideColBound) { - for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) { - for (unsigned int j = 0; j < convBlock.cols(); j++) { - // row: iterator is at current cell and component - // col: start at previous cells last node and go to node j. - jacIt[-strideColNode() + (j - i) * strideColNode()] += static_cast(_curVelocity) * convBlock(i, j); - } - } - } - } + for (unsigned int i = 0; i < dispBlock.rows(); i++, jacIt += strideColBound) + { + for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) + { + for (unsigned int j = 0; j < 2 * _nNodes; j++) + { + // pattern is more sparse than a nNodes x 2*nNodes block. + if ((j >= _nNodes - 1 && j <= 2 * _nNodes) || (i == 0 && j <= 2 * _nNodes) || + (i == _nNodes - 1 && j >= _nNodes - 1)) + // row: iterator is at current node i and current component comp + // col: start at previous cell and jump to node j + jacIt[-strideColCell() + (j - i) * strideColNode()] = + dispBlock(i, j) * static_cast(currentDispersion(_curSection)[comp]); } - else { // backward flow upwind convection - // non-inlet cells - for (unsigned int cell = 0; cell < _nCells - 1u; cell++) { - for (unsigned int i = 0; i < convBlock.rows(); i++, jacIt += strideColBound) { - for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) { - for (unsigned int j = 0; j < convBlock.cols(); j++) { - // row: iterator is at current cell and component - // col: start at current cells first node and go to node j. - jacIt[(j - i) * strideColNode()] += static_cast(_curVelocity) * convBlock(i, j); - } - } - } - } - // special inlet DOF treatment for last cell (inlet boundary cell) - jacInlet(0, 0) = static_cast(_curVelocity) * convBlock(convBlock.rows() - 1, convBlock.cols() - 1); // only last node depends on inlet concentration - for (unsigned int i = 0; i < convBlock.rows(); i++, jacIt += strideColBound) { - for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) { - for (unsigned int j = 0; j < convBlock.cols() - 1; j++) { - jacIt[(j - i) * strideColNode()] += static_cast(_curVelocity) * convBlock(i, j); - } - } - } + } + } + } + + /*======================================================*/ + /* Compute Convection Jacobian Block */ + /*======================================================*/ + + // Convection block [ d RHS_conv / d c ], also depends on first entry of previous cell + MatrixXd convBlock = _DGjacAxConvBlock; + linalg::BandedEigenSparseRowIterator jacIt(jacobian, offC); // row iterator starting at first cell and component + + if (_curVelocity >= 0.0) + { // forward flow upwind convection + // special inlet DOF treatment for first cell (inlet boundary cell) + jacInlet(0, 0) = + static_cast(_curVelocity) * convBlock(0, 0); // only first node depends on inlet concentration + for (unsigned int i = 0; i < convBlock.rows(); i++, jacIt += strideColBound) + { + for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) + { + // jacIt[0] = -convBlock(i, 0); // dependency on inlet DOFs is handled in _jacInlet + for (unsigned int j = 1; j < convBlock.cols(); j++) + { + jacIt[((j - 1) - i) * strideColNode()] += static_cast(_curVelocity) * convBlock(i, j); } - - return 0; } - /** - * @brief inserts a liquid state block with different factors for components into the system jacobian - * @param [in] block (sub)block to be added - * @param [in] jac row iterator at first (i.e. upper) entry - * @param [in] offCol column to row offset (i.e. start at upper left corner of block) - * @param [in] nCells determines how often the block is added (diagonally) - * @param [in] Compfactor component dependend factors - */ - void insertCompDepLiquidJacBlock(Eigen::MatrixXd block, linalg::BandedEigenSparseRowIterator& jac, int offCol, unsigned int nCells, const active* Compfactor) { - - const int strideColBound = strideColNode() - _nComp; - - for (unsigned int cell = 0; cell < nCells; cell++) { - for (unsigned int i = 0; i < block.rows(); i++, jac += strideColBound) { - for (unsigned int comp = 0; comp < _nComp; comp++, ++jac) { - for (unsigned int j = 0; j < block.cols(); j++) { - // row: at current node component - // col: jump to node j - jac[(j - i) * strideColNode() + offCol] = block(i, j) * static_cast(Compfactor[comp]); - } - } + } + // remaining cells + for (unsigned int cell = 1; cell < _nCells; cell++) + { + for (unsigned int i = 0; i < convBlock.rows(); i++, jacIt += strideColBound) + { + for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) + { + for (unsigned int j = 0; j < convBlock.cols(); j++) + { + // row: iterator is at current cell and component + // col: start at previous cells last node and go to node j. + jacIt[-strideColNode() + (j - i) * strideColNode()] += + static_cast(_curVelocity) * convBlock(i, j); } } } - /** - * @brief adds liquid state blocks for all components to the system jacobian - * @param [in] block to be added - * @param [in] jac row iterator at first (i.e. upper left) entry - * @param [in] column to row offset (i.e. start at upper left corner of block) - * @param [in] nCells determines how often the block is added (diagonally) - */ - void addLiquidJacBlock(Eigen::MatrixXd block, linalg::BandedEigenSparseRowIterator& jac, int offCol, unsigned int nCells) { - - unsigned int strideColBound = strideColNode() - _nComp; - - for (unsigned int cell = 0; cell < nCells; cell++) { - for (unsigned int i = 0; i < block.rows(); i++, jac += strideColBound) { - for (unsigned int comp = 0; comp < _nComp; comp++, ++jac) { - for (unsigned int j = 0; j < block.cols(); j++) { - // row: at current node component - // col: jump to node j - jac[(j - i) * strideColNode() + offCol] += block(i, j); - } - } + } + } + else + { // backward flow upwind convection + // non-inlet cells + for (unsigned int cell = 0; cell < _nCells - 1u; cell++) + { + for (unsigned int i = 0; i < convBlock.rows(); i++, jacIt += strideColBound) + { + for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) + { + for (unsigned int j = 0; j < convBlock.cols(); j++) + { + // row: iterator is at current cell and component + // col: start at current cells first node and go to node j. + jacIt[(j - i) * strideColNode()] += static_cast(_curVelocity) * convBlock(i, j); } } } - /** - * @brief analytically calculates the convection dispersion jacobian for the exact integration (here: modal) DG scheme - */ - int calcConvDispDGSEMJacobian(Eigen::SparseMatrix& jacobian, Eigen::MatrixXd& jacInlet, const int offC = 0) { - - /*======================================================*/ - /* Compute Dispersion Jacobian Block */ - /*======================================================*/ - - /* Inner cells (exist only if nCells >= 5) */ - if (_nCells >= 5) { - linalg::BandedEigenSparseRowIterator jacIt(jacobian, offC + strideColCell() * 2); // row iterator starting at third cell, first component - // insert all (nCol - 4) inner cell blocks - insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[2], jacIt, -(strideColCell() + strideColNode()), _nCells - 4u, currentDispersion(_curSection)); - } - - /* boundary cell neighbours (exist only if nCells >= 4) */ - if (_nCells >= 4) { - linalg::BandedEigenSparseRowIterator jacIt(jacobian, offC + strideColCell()); // row iterator starting at second cell, first component - - insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[1].block(0, 1, _nNodes, 3 * _nNodes + 1), jacIt, -strideColCell(), 1u, currentDispersion(_curSection)); - - jacIt += (_nCells - 4) * strideColCell(); // move iterator to preultimate cell (already at third cell) - insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[_nCells > 4 ? 3 : 2].block(0, 0, _nNodes, 3 * _nNodes + 1), jacIt, -(strideColCell() + strideColNode()), 1u, currentDispersion(_curSection)); + } + // special inlet DOF treatment for last cell (inlet boundary cell) + jacInlet(0, 0) = + static_cast(_curVelocity) * + convBlock(convBlock.rows() - 1, convBlock.cols() - 1); // only last node depends on inlet concentration + for (unsigned int i = 0; i < convBlock.rows(); i++, jacIt += strideColBound) + { + for (unsigned int comp = 0; comp < _nComp; comp++, ++jacIt) + { + for (unsigned int j = 0; j < convBlock.cols() - 1; j++) + { + jacIt[(j - i) * strideColNode()] += static_cast(_curVelocity) * convBlock(i, j); } + } + } + } + + return 0; + } + /** + * @brief inserts a liquid state block with different factors for components into the system jacobian + * @param [in] block (sub)block to be added + * @param [in] jac row iterator at first (i.e. upper) entry + * @param [in] offCol column to row offset (i.e. start at upper left corner of block) + * @param [in] nCells determines how often the block is added (diagonally) + * @param [in] Compfactor component dependend factors + */ + void insertCompDepLiquidJacBlock(Eigen::MatrixXd block, linalg::BandedEigenSparseRowIterator& jac, int offCol, + unsigned int nCells, const active* Compfactor) + { - /* boundary cells (exist only if nCells >= 3) */ - if (_nCells >= 3) { - - linalg::BandedEigenSparseRowIterator jacIt(jacobian, offC); // row iterator starting at first cell, first component - - insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[0].block(0, _nNodes + 1, _nNodes, 2 * _nNodes + 1), jacIt, 0, 1u, currentDispersion(_curSection)); + const int strideColBound = strideColNode() - _nComp; - jacIt += (_nCells - 2) * strideColCell(); // move iterator to last cell (already at second cell) - insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[std::min(_nCells, 5u) - 1u].block(0, 0, _nNodes, 2 * _nNodes + 1), jacIt, -(strideColCell() + strideColNode()), 1u, currentDispersion(_curSection)); + for (unsigned int cell = 0; cell < nCells; cell++) + { + for (unsigned int i = 0; i < block.rows(); i++, jac += strideColBound) + { + for (unsigned int comp = 0; comp < _nComp; comp++, ++jac) + { + for (unsigned int j = 0; j < block.cols(); j++) + { + // row: at current node component + // col: jump to node j + jac[(j - i) * strideColNode() + offCol] = block(i, j) * static_cast(Compfactor[comp]); } + } + } + } + } + /** + * @brief adds liquid state blocks for all components to the system jacobian + * @param [in] block to be added + * @param [in] jac row iterator at first (i.e. upper left) entry + * @param [in] column to row offset (i.e. start at upper left corner of block) + * @param [in] nCells determines how often the block is added (diagonally) + */ + void addLiquidJacBlock(Eigen::MatrixXd block, linalg::BandedEigenSparseRowIterator& jac, int offCol, + unsigned int nCells) + { - /* For special cases nCells = 1, 2, 3, some cells still have to be treated separately*/ + unsigned int strideColBound = strideColNode() - _nComp; - if (_nCells == 1) { - linalg::BandedEigenSparseRowIterator jacIt(jacobian, offC); // row iterator starting at first cell, first component - // insert the only block - insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[0].block(0, _nNodes + 1, _nNodes, _nNodes), jacIt, 0, 1u, currentDispersion(_curSection)); - } - else if (_nCells == 2) { - linalg::BandedEigenSparseRowIterator jacIt(jacobian, offC); // row iterator starting at first cell, first component - // left Bacobian block - insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[0].block(0, _nNodes + 1, _nNodes, 2 * _nNodes), jacIt, 0, 1u, currentDispersion(_curSection)); - // right Bacobian block, iterator is already moved to second cell - insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[1].block(0, 1, _nNodes, 2 * _nNodes), jacIt, -strideColCell(), 1u, currentDispersion(_curSection)); - } - else if (_nCells == 3) { - linalg::BandedEigenSparseRowIterator jacIt(jacobian, offC + strideColCell()); // row iterator starting at first cell, first component - insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[1].block(0, 1, _nNodes, 3 * _nNodes), jacIt, -strideColCell(), 1u, currentDispersion(_curSection)); + for (unsigned int cell = 0; cell < nCells; cell++) + { + for (unsigned int i = 0; i < block.rows(); i++, jac += strideColBound) + { + for (unsigned int comp = 0; comp < _nComp; comp++, ++jac) + { + for (unsigned int j = 0; j < block.cols(); j++) + { + // row: at current node component + // col: jump to node j + jac[(j - i) * strideColNode() + offCol] += block(i, j); } + } + } + } + } + /** + * @brief analytically calculates the convection dispersion jacobian for the exact integration (here: modal) DG + * scheme + */ + int calcConvDispDGSEMJacobian(Eigen::SparseMatrix& jacobian, Eigen::MatrixXd& jacInlet, + const int offC = 0) + { - /*======================================================*/ - /* Compute Convection Jacobian Block */ - /*======================================================*/ - - linalg::BandedEigenSparseRowIterator jac(jacobian, offC); + /*======================================================*/ + /* Compute Dispersion Jacobian Block */ + /*======================================================*/ - if (_curVelocity >= 0.0) { // Forward flow - // special inlet DOF treatment for inlet (first) cell - jacInlet = static_cast(_curVelocity) * _DGjacAxConvBlock.col(0); // only first cell depends on inlet concentration - addLiquidJacBlock(static_cast(_curVelocity) * _DGjacAxConvBlock.block(0, 1, _nNodes, _nNodes), jac, 0, 1); - if (_nCells > 1) // iterator already moved to second cell - addLiquidJacBlock(static_cast(_curVelocity) * _DGjacAxConvBlock, jac, -strideColNode(), _nCells - 1); - } - else { // Backward flow - // non-inlet cells first - if (_nCells > 1) - addLiquidJacBlock(static_cast(_curVelocity) * _DGjacAxConvBlock, jac, 0, _nCells - 1); - // special inlet DOF treatment for inlet (last) cell. Iterator already moved to last cell - jacInlet = static_cast(_curVelocity) * _DGjacAxConvBlock.col(_DGjacAxConvBlock.cols() - 1); // only last cell depends on inlet concentration - addLiquidJacBlock(static_cast(_curVelocity) * _DGjacAxConvBlock.block(0, 0, _nNodes, _nNodes), jac, 0, 1); - } + /* Inner cells (exist only if nCells >= 5) */ + if (_nCells >= 5) + { + linalg::BandedEigenSparseRowIterator jacIt( + jacobian, offC + strideColCell() * 2); // row iterator starting at third cell, first component + // insert all (nCol - 4) inner cell blocks + insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[2], jacIt, -(strideColCell() + strideColNode()), + _nCells - 4u, currentDispersion(_curSection)); + } + + /* boundary cell neighbours (exist only if nCells >= 4) */ + if (_nCells >= 4) + { + linalg::BandedEigenSparseRowIterator jacIt( + jacobian, offC + strideColCell()); // row iterator starting at second cell, first component - return 0; - } - // todo time derivative pattern - ///** - //* @brief adds time derivative to the jacobian - //*/ - //void addTimederJacobian(Eigen::SparseMatrix& jacobian, double alpha) { - - // unsigned int offC = 0; // inlet DOFs not included in Jacobian - - // // =================================================================================================== // - // // Time derivative Jacobian: d Residual / d y_t // - // // =================================================================================================== // - - // linalg::BandedEigenSparseRowIterator jac(jacobian, offC); - - // double Beta = (1.0 - static_cast(_totalPorosity)) / static_cast(_totalPorosity); - - // for (unsigned int point = 0; point < _disc.nPoints; point++) { - // for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - // // d convDispRHS / d c_t - // jac[0] += alpha; - - // if (_disc.nBound[comp]) { // either one or null; no loop necessary - // // d convDispRHS / d q_t - // jac[idxr.strideColLiquid() - comp + idxr.offsetBoundComp(comp)] += alpha * Beta; - // } - // ++jac; - // } - // for (unsigned int comp = 0; comp < _disc.nComp; comp++) { - // if (_disc.nBound[comp]) { // either one or null; no loop over bound states necessary - // if (!_binding[0]->hasQuasiStationaryReactions()) { - // // d isotherm / d q_t - // jac[0] += alpha; - // } - // ++jac; - // } - // } - // } - //} - }; - - - //// todo radial flow DG - //class RadialConvectionDispersionOperatorBaseDG { }; - - - /** - * @brief Convection dispersion transport operator - * @details This class wraps AxialConvectionDispersionOperatorBaseDG and provides all the functionality it does. - * In addition, the Jacobian is stored and corresponding functions are provided - * (assembly, factorization, solution, retrieval). - * - * This class assumes that the first cell is offset by the number of components (inlet DOFs) in the global state vector. - */ - template - class ConvectionDispersionOperatorDG - { - public: + insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[1].block(0, 1, _nNodes, 3 * _nNodes + 1), jacIt, + -strideColCell(), 1u, currentDispersion(_curSection)); - ConvectionDispersionOperatorDG(); - ~ConvectionDispersionOperatorDG() CADET_NOEXCEPT; + jacIt += (_nCells - 4) * strideColCell(); // move iterator to preultimate cell (already at third cell) + insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[_nCells > 4 ? 3 : 2].block(0, 0, _nNodes, 3 * _nNodes + 1), + jacIt, -(strideColCell() + strideColNode()), 1u, + currentDispersion(_curSection)); + } - int requiredADdirs() const CADET_NOEXCEPT; + /* boundary cells (exist only if nCells >= 3) */ + if (_nCells >= 3) + { - void setFlowRates(const active& in, const active& out, const active& colPorosity) CADET_NOEXCEPT; + linalg::BandedEigenSparseRowIterator jacIt(jacobian, + offC); // row iterator starting at first cell, first component - bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, int polynomial_integration_mode, unsigned int nCells, unsigned int polyDeg, unsigned int strideNode); - bool configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, std::unordered_map& parameters); - bool notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const AdJacobianParams& adJac); + insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[0].block(0, _nNodes + 1, _nNodes, 2 * _nNodes + 1), jacIt, 0, + 1u, currentDispersion(_curSection)); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, Eigen::SparseMatrix& jac); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, Eigen::SparseMatrix& jac); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, bool wantJac, WithoutParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithoutParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity); + jacIt += (_nCells - 2) * strideColCell(); // move iterator to last cell (already at second cell) + insertCompDepLiquidJacBlock( + _DGjacAxDispBlocks[std::min(_nCells, 5u) - 1u].block(0, 0, _nNodes, 2 * _nNodes + 1), jacIt, + -(strideColCell() + strideColNode()), 1u, currentDispersion(_curSection)); + } - void prepareADvectors(const AdJacobianParams& adJac) const; - void extractJacobianFromAD(active const* const adRes, unsigned int adDirOffset); + /* For special cases nCells = 1, 2, 3, some cells still have to be treated separately*/ - bool solveTimeDerivativeSystem(const SimulationTime& simTime, double* const rhs); - void multiplyWithDerivativeJacobian(const SimulationTime& simTime, double const* sDot, double* ret) const; + if (_nCells == 1) + { + linalg::BandedEigenSparseRowIterator jacIt(jacobian, + offC); // row iterator starting at first cell, first component + // insert the only block + insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[0].block(0, _nNodes + 1, _nNodes, _nNodes), jacIt, 0, 1u, + currentDispersion(_curSection)); + } + else if (_nCells == 2) + { + linalg::BandedEigenSparseRowIterator jacIt(jacobian, + offC); // row iterator starting at first cell, first component + // left Bacobian block + insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[0].block(0, _nNodes + 1, _nNodes, 2 * _nNodes), jacIt, 0, 1u, + currentDispersion(_curSection)); + // right Bacobian block, iterator is already moved to second cell + insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[1].block(0, 1, _nNodes, 2 * _nNodes), jacIt, + -strideColCell(), 1u, currentDispersion(_curSection)); + } + else if (_nCells == 3) + { + linalg::BandedEigenSparseRowIterator jacIt( + jacobian, offC + strideColCell()); // row iterator starting at first cell, first component + insertCompDepLiquidJacBlock(_DGjacAxDispBlocks[1].block(0, 1, _nNodes, 3 * _nNodes), jacIt, + -strideColCell(), 1u, currentDispersion(_curSection)); + } + + /*======================================================*/ + /* Compute Convection Jacobian Block */ + /*======================================================*/ + + linalg::BandedEigenSparseRowIterator jac(jacobian, offC); + + if (_curVelocity >= 0.0) + { // Forward flow + // special inlet DOF treatment for inlet (first) cell + jacInlet = static_cast(_curVelocity) * + _DGjacAxConvBlock.col(0); // only first cell depends on inlet concentration + addLiquidJacBlock(static_cast(_curVelocity) * _DGjacAxConvBlock.block(0, 1, _nNodes, _nNodes), jac, + 0, 1); + if (_nCells > 1) // iterator already moved to second cell + addLiquidJacBlock(static_cast(_curVelocity) * _DGjacAxConvBlock, jac, -strideColNode(), + _nCells - 1); + } + else + { // Backward flow + // non-inlet cells first + if (_nCells > 1) + addLiquidJacBlock(static_cast(_curVelocity) * _DGjacAxConvBlock, jac, 0, _nCells - 1); + // special inlet DOF treatment for inlet (last) cell. Iterator already moved to last cell + jacInlet = + static_cast(_curVelocity) * + _DGjacAxConvBlock.col(_DGjacAxConvBlock.cols() - 1); // only last cell depends on inlet concentration + addLiquidJacBlock(static_cast(_curVelocity) * _DGjacAxConvBlock.block(0, 0, _nNodes, _nNodes), jac, + 0, 1); + } + + return 0; + } + // todo time derivative pattern + ///** + //* @brief adds time derivative to the jacobian + //*/ + // void addTimederJacobian(Eigen::SparseMatrix& jacobian, double alpha) { + + // unsigned int offC = 0; // inlet DOFs not included in Jacobian + + // // =================================================================================================== // + // // Time derivative Jacobian: d Residual / d y_t // + // // =================================================================================================== // + + // linalg::BandedEigenSparseRowIterator jac(jacobian, offC); + + // double Beta = (1.0 - static_cast(_totalPorosity)) / static_cast(_totalPorosity); + + // for (unsigned int point = 0; point < _disc.nPoints; point++) { + // for (unsigned int comp = 0; comp < _disc.nComp; comp++) { + // // d convDispRHS / d c_t + // jac[0] += alpha; + + // if (_disc.nBound[comp]) { // either one or null; no loop necessary + // // d convDispRHS / d q_t + // jac[idxr.strideColLiquid() - comp + idxr.offsetBoundComp(comp)] += alpha * Beta; + // } + // ++jac; + // } + // for (unsigned int comp = 0; comp < _disc.nComp; comp++) { + // if (_disc.nBound[comp]) { // either one or null; no loop over bound states necessary + // if (!_binding[0]->hasQuasiStationaryReactions()) { + // // d isotherm / d q_t + // jac[0] += alpha; + // } + // ++jac; + // } + // } + // } + //} +}; + +//// todo radial flow DG +// class RadialConvectionDispersionOperatorBaseDG { }; - bool assembleAndFactorizeDiscretizedJacobian(double alpha); - bool solveDiscretizedJacobian(double* rhs) const; +/** + * @brief Convection dispersion transport operator + * @details This class wraps AxialConvectionDispersionOperatorBaseDG and provides all the functionality it does. + * In addition, the Jacobian is stored and corresponding functions are provided + * (assembly, factorization, solution, retrieval). + * + * This class assumes that the first cell is offset by the number of components (inlet DOFs) in the global state vector. + */ +template class ConvectionDispersionOperatorDG +{ +public: + ConvectionDispersionOperatorDG(); + ~ConvectionDispersionOperatorDG() CADET_NOEXCEPT; + + int requiredADdirs() const CADET_NOEXCEPT; + + void setFlowRates(const active& in, const active& out, const active& colPorosity) CADET_NOEXCEPT; + + bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, + unsigned int nComp, int polynomial_integration_mode, unsigned int nCells, + unsigned int polyDeg, unsigned int strideNode); + bool configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, + std::unordered_map& parameters); + bool notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const AdJacobianParams& adJac); + + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, + Eigen::SparseMatrix& jac); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, + Eigen::SparseMatrix& jac); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, + bool wantJac, WithoutParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, + bool wantJac, WithParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, + bool wantJac, WithoutParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, + bool wantJac, WithParamSensitivity); + + void prepareADvectors(const AdJacobianParams& adJac) const; + void extractJacobianFromAD(active const* const adRes, unsigned int adDirOffset); + + bool solveTimeDerivativeSystem(const SimulationTime& simTime, double* const rhs); + void multiplyWithDerivativeJacobian(const SimulationTime& simTime, double const* sDot, double* ret) const; + + bool assembleAndFactorizeDiscretizedJacobian(double alpha); + bool solveDiscretizedJacobian(double* rhs) const; #ifdef CADET_CHECK_ANALYTIC_JACOBIAN - double checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const; + double checkAnalyticJacobianAgainstAd(active const* const adRes, unsigned int adDirOffset) const; #endif - inline const active& columnLength() const CADET_NOEXCEPT { return _baseOp.columnLength(); } - inline active currentVelocity(double pos) const CADET_NOEXCEPT { return _baseOp.currentVelocity(pos); } - inline bool forwardFlow() const CADET_NOEXCEPT { return _baseOp.forwardFlow(); } - - inline double cellLeftBound(unsigned int idx) const CADET_NOEXCEPT { return _baseOp.cellLeftBound(idx); } - inline double relativeCoordinate(unsigned int idx) const CADET_NOEXCEPT { return _baseOp.relativeCoordinate(idx); } - - inline Eigen::SparseMatrix& jacobian() CADET_NOEXCEPT { return _jacC; } - inline const Eigen::SparseMatrix& jacobian() const CADET_NOEXCEPT { return _jacC; } + inline const active& columnLength() const CADET_NOEXCEPT + { + return _baseOp.columnLength(); + } + inline active currentVelocity(double pos) const CADET_NOEXCEPT + { + return _baseOp.currentVelocity(pos); + } + inline bool forwardFlow() const CADET_NOEXCEPT + { + return _baseOp.forwardFlow(); + } - inline Eigen::SparseMatrix& jacobianDisc() CADET_NOEXCEPT { return _jacCdisc; } - inline const Eigen::SparseMatrix& jacobianDisc() const CADET_NOEXCEPT { return _jacCdisc; } + inline double cellLeftBound(unsigned int idx) const CADET_NOEXCEPT + { + return _baseOp.cellLeftBound(idx); + } + inline double relativeCoordinate(unsigned int idx) const CADET_NOEXCEPT + { + return _baseOp.relativeCoordinate(idx); + } - inline bool setParameter(const ParameterId& pId, double value) - { - return _baseOp.setParameter(pId, value); - } - inline bool setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, unsigned int adDirection, double adValue) - { - return _baseOp.setSensitiveParameter(sensParams, pId, adDirection, adValue); - } - inline bool setSensitiveParameterValue(const std::unordered_set& sensParams, const ParameterId& id, double value) - { - return _baseOp.setSensitiveParameterValue(sensParams, id, value); - } + inline Eigen::SparseMatrix& jacobian() CADET_NOEXCEPT + { + return _jacC; + } + inline const Eigen::SparseMatrix& jacobian() const CADET_NOEXCEPT + { + return _jacC; + } - protected: + inline Eigen::SparseMatrix& jacobianDisc() CADET_NOEXCEPT + { + return _jacCdisc; + } + inline const Eigen::SparseMatrix& jacobianDisc() const CADET_NOEXCEPT + { + return _jacCdisc; + } - void addTimeDerivativeToJacobian(double alpha); - void assembleDiscretizedJacobian(double alpha); + inline bool setParameter(const ParameterId& pId, double value) + { + return _baseOp.setParameter(pId, value); + } + inline bool setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, + unsigned int adDirection, double adValue) + { + return _baseOp.setSensitiveParameter(sensParams, pId, adDirection, adValue); + } + inline bool setSensitiveParameterValue(const std::unordered_set& sensParams, const ParameterId& id, + double value) + { + return _baseOp.setSensitiveParameterValue(sensParams, id, value); + } - BaseOperator _baseOp; +protected: + void addTimeDerivativeToJacobian(double alpha); + void assembleDiscretizedJacobian(double alpha); - Eigen::MatrixXd _jacInlet; //!< inlet Jacobian - Eigen::SparseMatrix _jacC; //!< Jacobian - Eigen::SparseMatrix _jacCdisc; //!< Jacobian with time derivatives from BDF method + BaseOperator _baseOp; - // Indexer functionality - // Offsets - inline int offsetC() const CADET_NOEXCEPT { return _baseOp.nComp(); } + Eigen::MatrixXd _jacInlet; //!< inlet Jacobian + Eigen::SparseMatrix _jacC; //!< Jacobian + Eigen::SparseMatrix _jacCdisc; //!< Jacobian with time derivatives from BDF method - }; + // Indexer functionality + // Offsets + inline int offsetC() const CADET_NOEXCEPT + { + return _baseOp.nComp(); + } +}; - extern template class ConvectionDispersionOperatorDG; - //extern template class ConvectionDispersionOperatorDG; // todo radial flow DG +extern template class ConvectionDispersionOperatorDG; +// extern template class ConvectionDispersionOperatorDG; // todo radial flow +// DG - typedef ConvectionDispersionOperatorDG AxialConvectionDispersionOperatorDG; - //typedef ConvectionDispersionOperatorDG RadialConvectionDispersionOperatorDG; // todo radial flow DG +typedef ConvectionDispersionOperatorDG AxialConvectionDispersionOperatorDG; +// typedef ConvectionDispersionOperatorDG +// RadialConvectionDispersionOperatorDG; // todo radial flow DG - } // namespace parts - } // namespace model +} // namespace parts +} // namespace model } // namespace cadet -#endif // LIBCADET_CONVECTIONDISPERSIONOPERATORDG_HPP_ +#endif // LIBCADET_CONVECTIONDISPERSIONOPERATORDG_HPP_ diff --git a/src/libcadet/model/parts/DGToolbox.cpp b/src/libcadet/model/parts/DGToolbox.cpp index d3741d3aa..f752415cd 100644 --- a/src/libcadet/model/parts/DGToolbox.cpp +++ b/src/libcadet/model/parts/DGToolbox.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -39,14 +39,16 @@ namespace dgtoolbox * @param [in] q <- q(x) = L_N+1 (x) - L_N-2(x) * @param [in] qder <- q'(x) = [L_N+1 (x) - L_N-2(x)]' */ -void qAndL(const unsigned int polyDeg, const double x, double& L, double& q, double& qder) { +void qAndL(const unsigned int polyDeg, const double x, double& L, double& q, double& qder) +{ // auxiliary variables (Legendre polynomials) double L_2 = 1.0; double L_1 = x; double Lder_2 = 0.0; double Lder_1 = 1.0; double Lder = 0.0; - for (double k = 2; k <= polyDeg; k++) { // note that this function is only called for polyDeg >= 2. + for (double k = 2; k <= polyDeg; k++) + { // note that this function is only called for polyDeg >= 2. L = ((2 * k - 1) * x * L_1 - (k - 1) * L_2) / k; Lder = Lder_2 + (2 * k - 1) * L_1; L_2 = L_1; @@ -64,7 +66,8 @@ void qAndL(const unsigned int polyDeg, const double x, double& L, double& q, dou * @param [in, out] invWeights Legendre Gauss quadrature weights * @param [in] invertWeights specifies if weights should be inverted */ -void lglNodesWeights(const unsigned int polyDeg, VectorXd& nodes, VectorXd& invWeights, bool invertWeights) { +void lglNodesWeights(const unsigned int polyDeg, VectorXd& nodes, VectorXd& invWeights, bool invertWeights) +{ const double pi = 3.1415926535897932384626434; @@ -75,7 +78,8 @@ void lglNodesWeights(const unsigned int polyDeg, VectorXd& nodes, VectorXd& invW double L = 0; double q = 0; double qder = 0; - switch (polyDeg) { + switch (polyDeg) + { case 0: throw std::invalid_argument("Polynomial degree must be at least 1 !"); break; @@ -91,14 +95,17 @@ void lglNodesWeights(const unsigned int polyDeg, VectorXd& nodes, VectorXd& invW invWeights[0] = 2.0 / (polyDeg * (polyDeg + 1.0)); invWeights[polyDeg] = invWeights[0]; // use symmetrie, only compute half of points and weights - for (unsigned int j = 1; j <= floor((polyDeg + 1) / 2) - 1; j++) { + for (unsigned int j = 1; j <= floor((polyDeg + 1) / 2) - 1; j++) + { // first guess for Newton iteration nodes[j] = -cos(pi * (j + 0.25) / polyDeg - 3 / (8.0 * polyDeg * pi * (j + 0.25))); // Newton iteration to find roots of Legendre Polynomial - for (unsigned int k = 0; k <= nIterations; k++) { + for (unsigned int k = 0; k <= nIterations; k++) + { qAndL(polyDeg, nodes[j], L, q, qder); nodes[j] = nodes[j] - q / qder; - if (abs(q / qder) <= tolerance * abs(nodes[j])) { + if (abs(q / qder) <= tolerance * abs(nodes[j])) + { break; } } @@ -109,7 +116,8 @@ void lglNodesWeights(const unsigned int polyDeg, VectorXd& nodes, VectorXd& invW invWeights[polyDeg - j] = invWeights[j]; } } - if (polyDeg % 2 == 0) { // for even polyDeg we have an odd number of points which include 0.0 + if (polyDeg % 2 == 0) + { // for even polyDeg we have an odd number of points which include 0.0 qAndL(polyDeg, 0.0, L, q, qder); nodes[polyDeg / 2] = 0; invWeights[polyDeg / 2] = 2.0 / (polyDeg * (polyDeg + 1.0) * pow(L, 2.0)); @@ -124,9 +132,11 @@ void lglNodesWeights(const unsigned int polyDeg, VectorXd& nodes, VectorXd& invW * @param [in, out] legDer Legendre polynomial derivative * @param [in] x evaluation point */ -void legendrePolynomialAndDerivative(const int polyDeg, double& leg, double& legDer, const double x) { +void legendrePolynomialAndDerivative(const int polyDeg, double& leg, double& legDer, const double x) +{ - switch (polyDeg) { + switch (polyDeg) + { case 0: leg = 1.0; legDer = 0.0; @@ -141,7 +151,8 @@ void legendrePolynomialAndDerivative(const int polyDeg, double& leg, double& leg double legDer_2 = 0.0; double legDer_1 = 1.0; - for (int k = 2; k <= polyDeg; k++) { + for (int k = 2; k <= polyDeg; k++) + { leg = (2.0 * k - 1.0) / k * x * leg_1 - (k - 1.0) / k * leg_2; legDer = legDer_2 + (2.0 * k - 1.0) * leg_1; leg_2 = leg_1; @@ -158,7 +169,8 @@ void legendrePolynomialAndDerivative(const int polyDeg, double& leg, double& leg * @param [in, out] weights Legendre Gauss quadrature weights * @param [in] invertWeights specifies if weights should be inverted */ -void lgNodesWeights(const unsigned int polyDeg, VectorXd& nodes, VectorXd& weights, bool invertWeights = true) { +void lgNodesWeights(const unsigned int polyDeg, VectorXd& nodes, VectorXd& weights, bool invertWeights = true) +{ const double pi = 3.1415926535897932384626434; @@ -166,7 +178,8 @@ void lgNodesWeights(const unsigned int polyDeg, VectorXd& nodes, VectorXd& weigh int nIterations = 10; double tolerance = 1e-15; - switch (polyDeg) { + switch (polyDeg) + { case 0: nodes[0] = 0.0; weights[0] = 2.0; @@ -215,7 +228,8 @@ void lgNodesWeights(const unsigned int polyDeg, VectorXd& nodes, VectorXd& weigh * @param [in] intNodes interpolation nodes the Lagrange basis is constructed with * @param [in] evalNodes nodes the Lagrange basis is evaluated at */ -VectorXd evalLagrangeBasis(const int j, const VectorXd intNodes, const VectorXd evalNodes) { +VectorXd evalLagrangeBasis(const int j, const VectorXd intNodes, const VectorXd evalNodes) +{ const int nIntNodes = intNodes.size(); const int nEvalNodes = evalNodes.size(); @@ -255,7 +269,8 @@ VectorXd evalLagrangeBasis(const int j, const VectorXd intNodes, const VectorXd * @param [in] LGLnodes Legendre Gauss Lobatto nodes * @param [in] nLGNodes number of Gauss quadrature nodes */ -MatrixXd gaussQuadratureMMatrix(const VectorXd LGLnodes, const int nLGNodes) { +MatrixXd gaussQuadratureMMatrix(const VectorXd LGLnodes, const int nLGNodes) +{ const int Ldegree = nLGNodes - 1; // Legendre polynomial degree const int nLGLnodes = LGLnodes.size(); @@ -288,20 +303,24 @@ MatrixXd gaussQuadratureMMatrix(const VectorXd LGLnodes, const int nLGNodes) { * @param [in] polyDeg polynomial degree * @param [in, out] baryWeights vector to store barycentric weights. Must already be initialized with ones! */ -VectorXd barycentricWeights(const unsigned int polyDeg, const VectorXd nodes) { +VectorXd barycentricWeights(const unsigned int polyDeg, const VectorXd nodes) +{ VectorXd baryWeights = VectorXd::Ones(polyDeg + 1u); - for (unsigned int j = 1; j <= polyDeg; j++) { - for (unsigned int k = 0; k <= j - 1; k++) { + for (unsigned int j = 1; j <= polyDeg; j++) + { + for (unsigned int k = 0; k <= j - 1; k++) + { baryWeights[k] = baryWeights[k] * (nodes[k] - nodes[j]) * 1.0; baryWeights[j] = baryWeights[j] * (nodes[j] - nodes[k]) * 1.0; } } - for (unsigned int j = 0; j <= polyDeg; j++) { + for (unsigned int j = 0; j <= polyDeg; j++) + { baryWeights[j] = 1 / baryWeights[j]; } - + return baryWeights; } /** @@ -309,14 +328,18 @@ VectorXd barycentricWeights(const unsigned int polyDeg, const VectorXd nodes) { * @param [in] polyDeg polynomial degree * @param [in] nodes polynomial interpolation nodes */ -MatrixXd derivativeMatrix(const unsigned int polyDeg, const VectorXd nodes) { +MatrixXd derivativeMatrix(const unsigned int polyDeg, const VectorXd nodes) +{ MatrixXd polyDerM = MatrixXd::Zero(polyDeg + 1u, polyDeg + 1u); VectorXd baryWeights = barycentricWeights(polyDeg, nodes); - for (unsigned int i = 0; i <= polyDeg; i++) { - for (unsigned int j = 0; j <= polyDeg; j++) { - if (i != j) { + for (unsigned int i = 0; i <= polyDeg; i++) + { + for (unsigned int j = 0; j <= polyDeg; j++) + { + if (i != j) + { polyDerM(i, j) = baryWeights[j] / (baryWeights[i] * (nodes[i] - nodes[j])); polyDerM(i, i) += -polyDerM(i, j); } @@ -328,11 +351,12 @@ MatrixXd derivativeMatrix(const unsigned int polyDeg, const VectorXd nodes) { /** * @brief factor to normalize Jacobi polynomials */ -double orthonFactor(const int polyDeg, double a, double b) { +double orthonFactor(const int polyDeg, double a, double b) +{ - double n = static_cast (polyDeg); - return std::sqrt(((2.0 * n + a + b + 1.0) * std::tgamma(n + 1.0) * std::tgamma(n + a + b + 1.0)) - / (std::pow(2.0, a + b + 1.0) * std::tgamma(n + a + 1.0) * std::tgamma(n + b + 1.0))); + double n = static_cast(polyDeg); + return std::sqrt(((2.0 * n + a + b + 1.0) * std::tgamma(n + 1.0) * std::tgamma(n + a + b + 1.0)) / + (std::pow(2.0, a + b + 1.0) * std::tgamma(n + a + 1.0) * std::tgamma(n + b + 1.0))); } /** * @brief calculates the Vandermonde matrix of the normalized Jacobi polynomials @@ -341,7 +365,8 @@ double orthonFactor(const int polyDeg, double a, double b) { * @param [in] a Jacobi polynomial parameter * @param [in] b Jacobi polynomial parameter */ -MatrixXd jacVandermondeMatrix(const unsigned int polyDeg, const VectorXd nodes, const double a, const double b) { +MatrixXd jacVandermondeMatrix(const unsigned int polyDeg, const VectorXd nodes, const double a, const double b) +{ const unsigned int nNodes = polyDeg + 1u; MatrixXd V(nNodes, nNodes); @@ -349,56 +374,74 @@ MatrixXd jacVandermondeMatrix(const unsigned int polyDeg, const VectorXd nodes, // degree 0 V.block(0, 0, nNodes, 1) = VectorXd::Ones(nNodes) * orthonFactor(0, a, b); // degree 1 - for (int node = 0; node < static_cast(nNodes); node++) { + for (int node = 0; node < static_cast(nNodes); node++) + { V(node, 1) = ((nodes[node] - 1.0) / 2.0 * (a + b + 2.0) + (a + 1.0)) * orthonFactor(1, a, b); } - for (int deg = 2; deg <= static_cast(nNodes - 1); deg++) { + for (int deg = 2; deg <= static_cast(nNodes - 1); deg++) + { - for (int node = 0; node < static_cast(nNodes); node++) { + for (int node = 0; node < static_cast(nNodes); node++) + { double orthn_1 = orthonFactor(deg, a, b) / orthonFactor(deg - 1, a, b); double orthn_2 = orthonFactor(deg, a, b) / orthonFactor(deg - 2, a, b); // recurrence relation - V(node, deg) = orthn_1 * ((2.0 * deg + a + b - 1.0) * ((2.0 * deg + a + b) * (2.0 * deg + a + b - 2.0) * nodes[node] + a * a - b * b) * V(node, deg - 1)); - V(node, deg) -= orthn_2 * (2.0 * (deg + a - 1.0) * (deg + b - 1.0) * (2.0 * deg + a + b) * V(node, deg - 2)); + V(node, deg) = orthn_1 * ((2.0 * deg + a + b - 1.0) * + ((2.0 * deg + a + b) * (2.0 * deg + a + b - 2.0) * nodes[node] + a * a - b * b) * + V(node, deg - 1)); + V(node, deg) -= + orthn_2 * (2.0 * (deg + a - 1.0) * (deg + b - 1.0) * (2.0 * deg + a + b) * V(node, deg - 2)); V(node, deg) /= 2.0 * deg * (deg + a + b) * (2.0 * deg + a + b - 2.0); } } return V; } -double jacPDerivativePreFactor(const unsigned int pIndex, const unsigned int derOrder, const double a, const double b) { +double jacPDerivativePreFactor(const unsigned int pIndex, const unsigned int derOrder, const double a, const double b) +{ - double prefac = std::tgamma(a + b + static_cast(pIndex) + 1.0 + static_cast(derOrder)) / (std::pow(2.0, static_cast(derOrder)) * std::tgamma(a + b + static_cast(pIndex) + 1.0)); + double prefac = + std::tgamma(a + b + static_cast(pIndex) + 1.0 + static_cast(derOrder)) / + (std::pow(2.0, static_cast(derOrder)) * std::tgamma(a + b + static_cast(pIndex) + 1.0)); - return prefac * orthonFactor(pIndex - derOrder, a + static_cast(derOrder), b + static_cast(derOrder)) / orthonFactor(pIndex, a, b); + return prefac * + orthonFactor(pIndex - derOrder, a + static_cast(derOrder), b + static_cast(derOrder)) / + orthonFactor(pIndex, a, b); } /** * @brief calculates the Vandermonde matrix of the normalized Legendre polynomials * @param [in] polyDeg polynomial degree * @param [in] nodes polynomial interpolation nodes */ -MatrixXd legVandermondeMatrix(const unsigned int polyDeg, const VectorXd nodes) { +MatrixXd legVandermondeMatrix(const unsigned int polyDeg, const VectorXd nodes) +{ return jacVandermondeMatrix(polyDeg, nodes, 0.0, 0.0); } /** * @brief calculates the inverse mass matrix via transformation to orthonormal Jacobi (modal) basis - * @detail the mass matrix used to compute integrals of the form \int_E \ell_i(\xi) \ell_j(\xi) (1 - \xi)^\alpha (1 + \xi)^\beta d\xi + * @detail the mass matrix used to compute integrals of the form \int_E \ell_i(\xi) \ell_j(\xi) (1 - \xi)^\alpha (1 + + * \xi)^\beta d\xi * @param [in] polyDeg polynomial degree * @param [in] nodes polynomial interpolation nodes */ -Eigen::MatrixXd invMMatrix(const unsigned int polyDeg, const Eigen::VectorXd nodes, const double alpha, const double beta) { - return (jacVandermondeMatrix(polyDeg, nodes, alpha, beta) * (jacVandermondeMatrix(polyDeg, nodes, alpha, beta).transpose())); +Eigen::MatrixXd invMMatrix(const unsigned int polyDeg, const Eigen::VectorXd nodes, const double alpha, + const double beta) +{ + return (jacVandermondeMatrix(polyDeg, nodes, alpha, beta) * + (jacVandermondeMatrix(polyDeg, nodes, alpha, beta).transpose())); } /** * @brief calculates the mass matrix via transformation to orthonormal Jacobi (modal) basis - * @detail mass matrix used to compute integrals of the form \int_E \ell_i(\xi) \ell_j(\xi) (1 - \xi)^\alpha (1 + \xi)^\beta d\xi + * @detail mass matrix used to compute integrals of the form \int_E \ell_i(\xi) \ell_j(\xi) (1 - \xi)^\alpha (1 + + * \xi)^\beta d\xi * @param [in] polyDeg polynomial degree * @param [in] nodes polynomial interpolation nodes */ -Eigen::MatrixXd mMatrix(const unsigned int polyDeg, const Eigen::VectorXd nodes, const double alpha, const double beta) { +Eigen::MatrixXd mMatrix(const unsigned int polyDeg, const Eigen::VectorXd nodes, const double alpha, const double beta) +{ return invMMatrix(polyDeg, nodes, alpha, beta).inverse(); } /** @@ -408,14 +451,16 @@ Eigen::MatrixXd mMatrix(const unsigned int polyDeg, const Eigen::VectorXd nodes, * @param [in] b Jacobi polynomial parameter * @param [in] nodes polynomial interpolation nodes */ -MatrixXd jacDerVandermondeMatrix(const unsigned int polyDeg, const double a, const double b, const VectorXd nodes) { +MatrixXd jacDerVandermondeMatrix(const unsigned int polyDeg, const double a, const double b, const VectorXd nodes) +{ MatrixXd derVan = MatrixXd::Zero(polyDeg + 1, polyDeg + 1); - derVan.block(0, 1, polyDeg + 1, polyDeg) = jacVandermondeMatrix(polyDeg, nodes, a + 1.0, b + 1.0).block(0, 0, polyDeg + 1, polyDeg); + derVan.block(0, 1, polyDeg + 1, polyDeg) = + jacVandermondeMatrix(polyDeg, nodes, a + 1.0, b + 1.0).block(0, 0, polyDeg + 1, polyDeg); for (int hm = 1; hm < polyDeg + 1; hm++) derVan.block(0, hm, polyDeg + 1, 1) *= jacPDerivativePreFactor(hm, 1, a, b); - //derVan.block(0, hm, polyDeg + 1, 1) *= std::sqrt(hm * (hm + a + b + 1)); // todo + // derVan.block(0, hm, polyDeg + 1, 1) *= std::sqrt(hm * (hm + a + b + 1)); // todo return derVan; } @@ -426,11 +471,14 @@ MatrixXd jacDerVandermondeMatrix(const unsigned int polyDeg, const double a, con * @param [in] b Jacobi polynomial parameter * @param [in] nodes polynomial interpolation nodes */ -MatrixXd secondOrderStiffnessMatrix(const unsigned int polyDeg, const double alpha, const double beta, const VectorXd nodes) { - return derivativeMatrix(polyDeg, nodes).transpose() * mMatrix(polyDeg, nodes, alpha, beta) * derivativeMatrix(polyDeg, nodes); +MatrixXd secondOrderStiffnessMatrix(const unsigned int polyDeg, const double alpha, const double beta, + const VectorXd nodes) +{ + return derivativeMatrix(polyDeg, nodes).transpose() * mMatrix(polyDeg, nodes, alpha, beta) * + derivativeMatrix(polyDeg, nodes); } } // namespace dgtoolbox } // namespace parts } // namespace model -} // namespace cadet \ No newline at end of file +} // namespace cadet diff --git a/src/libcadet/model/parts/DGToolbox.hpp b/src/libcadet/model/parts/DGToolbox.hpp index a776e50b9..fcb1dab54 100644 --- a/src/libcadet/model/parts/DGToolbox.hpp +++ b/src/libcadet/model/parts/DGToolbox.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -40,7 +40,8 @@ namespace dgtoolbox * @param [in, out] invWeights Legendre Gauss quadrature weights * @param [in] invertWeights specifies if weights should be inverted */ -void lglNodesWeights(const unsigned int polyDeg, Eigen::VectorXd& nodes, Eigen::VectorXd& invWeights, bool invertWeights = true); +void lglNodesWeights(const unsigned int polyDeg, Eigen::VectorXd& nodes, Eigen::VectorXd& invWeights, + bool invertWeights = true); /** * @brief computes the Legendre polynomial and its derivative * @param [in] polyDeg polynomial degree @@ -70,31 +71,36 @@ Eigen::VectorXd barycentricWeights(const unsigned int polyDeg, const Eigen::Vect Eigen::MatrixXd derivativeMatrix(const unsigned int polyDeg, const Eigen::VectorXd nodes); /** * @brief calculates the inverse mass matrix via transformation to orthonormal Jacobi (modal) basis - * @detail the mass matrix used to compute integrals of the form \int_E \ell_i(\xi) \ell_j(\xi) (1 - \xi)^\alpha (1 + \xi)^\beta d\xi + * @detail the mass matrix used to compute integrals of the form \int_E \ell_i(\xi) \ell_j(\xi) (1 - \xi)^\alpha (1 + + * \xi)^\beta d\xi * @param [in] polyDeg polynomial degree * @param [in] nodes polynomial interpolation nodes */ -Eigen::MatrixXd invMMatrix(const unsigned int polyDeg, const Eigen::VectorXd nodes, const double alpha = 0.0, const double beta = 0.0); +Eigen::MatrixXd invMMatrix(const unsigned int polyDeg, const Eigen::VectorXd nodes, const double alpha = 0.0, + const double beta = 0.0); /** * @brief calculates the mass matrix via transformation to orthonormal Jacobi (modal) basis - * @detail mass matrix used to compute integrals of the form \int_E \ell_i(\xi) \ell_j(\xi) (1 - \xi)^\alpha (1 + \xi)^\beta d\xi + * @detail mass matrix used to compute integrals of the form \int_E \ell_i(\xi) \ell_j(\xi) (1 - \xi)^\alpha (1 + + * \xi)^\beta d\xi * @param [in] polyDeg polynomial degree * @param [in] nodes polynomial interpolation nodes */ Eigen::MatrixXd mMatrix(const unsigned int polyDeg, const Eigen::VectorXd nodes, const double alpha, const double beta); /** * @brief calculates a specific second order nodal stiffness matrix - * @detail for integrals including terms of the form (1 - \xi)^\alpha (1 + \xi)^\beta. Computation via transformation to the respective Jacobi polynomial + * @detail for integrals including terms of the form (1 - \xi)^\alpha (1 + \xi)^\beta. Computation via transformation to + * the respective Jacobi polynomial * @param [in] polyDeg polynomial degree * @param [in] a Jacobi polynomial parameter * @param [in] b Jacobi polynomial parameter * @param [in] nodes polynomial interpolation nodes */ -Eigen::MatrixXd secondOrderStiffnessMatrix(const unsigned int polyDeg, const double alpha, const double beta, const Eigen::VectorXd nodes); +Eigen::MatrixXd secondOrderStiffnessMatrix(const unsigned int polyDeg, const double alpha, const double beta, + const Eigen::VectorXd nodes); } // namespace dgtoolbox } // namespace parts } // namespace model } // namespace cadet -#endif // LIBCADET_DGTOOLBOX_HPP_ \ No newline at end of file +#endif // LIBCADET_DGTOOLBOX_HPP_ diff --git a/src/libcadet/model/parts/MultiChannelConvectionDispersionOperator.cpp b/src/libcadet/model/parts/MultiChannelConvectionDispersionOperator.cpp index d18381846..6f63b5c87 100644 --- a/src/libcadet/model/parts/MultiChannelConvectionDispersionOperator.cpp +++ b/src/libcadet/model/parts/MultiChannelConvectionDispersionOperator.cpp @@ -23,10 +23,10 @@ #include "model/ParameterDependence.hpp" #ifdef SUPERLU_FOUND - #include "linalg/SuperLUSparseMatrix.hpp" +#include "linalg/SuperLUSparseMatrix.hpp" #endif #ifdef UMFPACK_FOUND - #include "linalg/UMFPackSparseMatrix.hpp" +#include "linalg/UMFPackSparseMatrix.hpp" #endif #include "linalg/BandMatrix.hpp" @@ -40,7 +40,10 @@ namespace { -cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvider& paramProvider, std::unordered_map& parameters, std::vector& values, const std::string& name, unsigned int nComp, unsigned int nChannel, cadet::UnitOpIdx uoi) +cadet::model::MultiplexMode readAndRegisterMultiplexParam( + cadet::IParameterProvider& paramProvider, std::unordered_map& parameters, + std::vector& values, const std::string& name, unsigned int nComp, unsigned int nChannel, + cadet::UnitOpIdx uoi) { cadet::model::MultiplexMode mode = cadet::model::MultiplexMode::Independent; readParameterMatrix(values, paramProvider, name, nComp * nChannel, 1); @@ -52,25 +55,31 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi { mode = cadet::model::MultiplexMode::Independent; if (values.size() > 1) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be 1)"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be 1)"); } else if (modeConfig == 1) { mode = cadet::model::MultiplexMode::Radial; if (values.size() != nChannel) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nChannel) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be " + std::to_string(nChannel) + + ")"); } else if (modeConfig == 2) { mode = cadet::model::MultiplexMode::Component; if (values.size() != nComp) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nComp) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be " + std::to_string(nComp) + ")"); } else if (modeConfig == 3) { mode = cadet::model::MultiplexMode::ComponentRadial; if (values.size() != nComp * nChannel) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nComp * nChannel) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be " + + std::to_string(nComp * nChannel) + ")"); } else if (modeConfig == 4) { @@ -81,7 +90,9 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi { mode = cadet::model::MultiplexMode::RadialSection; if (values.size() % nChannel != 0) - throw cadet::InvalidParameterException("Number of elements in field " + name + " is not a positive multiple of NCHANNEL (" + std::to_string(nChannel) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + + " is not a positive multiple of NCHANNEL (" + + std::to_string(nChannel) + ")"); nSec = values.size() / nChannel; } @@ -89,7 +100,9 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi { mode = cadet::model::MultiplexMode::ComponentSection; if (values.size() % nComp != 0) - throw cadet::InvalidParameterException("Number of elements in field " + name + " is not a positive multiple of NCOMP (" + std::to_string(nComp) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + + " is not a positive multiple of NCOMP (" + + std::to_string(nComp) + ")"); nSec = values.size() / nComp; } @@ -97,7 +110,9 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi { mode = cadet::model::MultiplexMode::ComponentRadialSection; if (values.size() % (nComp * nChannel) != 0) - throw cadet::InvalidParameterException("Number of elements in field " + name + " is not a positive multiple of NCOMP * NCHANNEL (" + std::to_string(nComp * nChannel) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + + " is not a positive multiple of NCOMP * NCHANNEL (" + + std::to_string(nComp * nChannel) + ")"); nSec = values.size() / (nComp * nChannel); } @@ -128,7 +143,8 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi nSec = values.size() / (nComp * nChannel); } else - throw cadet::InvalidParameterException("Could not infer multiplex mode of field " + name + ", set " + name + "_MULTIPLEX or change number of elements"); + throw cadet::InvalidParameterException("Could not infer multiplex mode of field " + name + ", set " + name + + "_MULTIPLEX or change number of elements"); // Do not infer cadet::model::MultiplexMode::Section in case of no matches (might hide specification errors) } @@ -136,321 +152,342 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi const cadet::StringHash nameHash = cadet::hashStringRuntime(name); switch (mode) { - case cadet::model::MultiplexMode::Independent: - case cadet::model::MultiplexMode::Section: - { - std::vector p(nComp * nChannel * nSec); - for (unsigned int s = 0; s < nSec; ++s) - std::fill(p.begin() + s * nChannel * nComp, p.begin() + (s+1) * nChannel * nComp, values[s]); - - values = std::move(p); - - for (unsigned int s = 0; s < nSec; ++s) - parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, (mode == cadet::model::MultiplexMode::Independent) ? cadet::SectionIndep : s)] = &values[s * nChannel * nComp]; - } - break; - case cadet::model::MultiplexMode::Component: - case cadet::model::MultiplexMode::ComponentSection: - { - std::vector p(nComp * nChannel * nSec); - for (unsigned int s = 0; s < nSec; ++s) - { - for (unsigned int i = 0; i < nComp; ++i) - std::copy(values.begin() + s * nComp, values.begin() + (s+1) * nComp, p.begin() + i * nComp + s * nComp * nChannel); - } + case cadet::model::MultiplexMode::Independent: + case cadet::model::MultiplexMode::Section: { + std::vector p(nComp * nChannel * nSec); + for (unsigned int s = 0; s < nSec; ++s) + std::fill(p.begin() + s * nChannel * nComp, p.begin() + (s + 1) * nChannel * nComp, values[s]); + + values = std::move(p); + + for (unsigned int s = 0; s < nSec; ++s) + parameters[cadet::makeParamId( + nameHash, uoi, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, + (mode == cadet::model::MultiplexMode::Independent) ? cadet::SectionIndep : s)] = + &values[s * nChannel * nComp]; + } + break; + case cadet::model::MultiplexMode::Component: + case cadet::model::MultiplexMode::ComponentSection: { + std::vector p(nComp * nChannel * nSec); + for (unsigned int s = 0; s < nSec; ++s) + { + for (unsigned int i = 0; i < nComp; ++i) + std::copy(values.begin() + s * nComp, values.begin() + (s + 1) * nComp, + p.begin() + i * nComp + s * nComp * nChannel); + } - values = std::move(p); + values = std::move(p); - for (unsigned int s = 0; s < nSec; ++s) - { - for (unsigned int i = 0; i < nComp; ++i) - parameters[cadet::makeParamId(nameHash, uoi, i, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, (mode == cadet::model::MultiplexMode::Component) ? cadet::SectionIndep : s)] = &values[s * nChannel * nComp + i]; - } - } - break; - case cadet::model::MultiplexMode::Radial: - case cadet::model::MultiplexMode::RadialSection: - { - std::vector p(nComp * nChannel * nSec); - for (unsigned int i = 0; i < nChannel * nSec; ++i) - std::fill(p.begin() + i * nComp, p.begin() + (i+1) * nComp, values[i]); + for (unsigned int s = 0; s < nSec; ++s) + { + for (unsigned int i = 0; i < nComp; ++i) + parameters[cadet::makeParamId( + nameHash, uoi, i, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, + (mode == cadet::model::MultiplexMode::Component) ? cadet::SectionIndep : s)] = + &values[s * nChannel * nComp + i]; + } + } + break; + case cadet::model::MultiplexMode::Radial: + case cadet::model::MultiplexMode::RadialSection: { + std::vector p(nComp * nChannel * nSec); + for (unsigned int i = 0; i < nChannel * nSec; ++i) + std::fill(p.begin() + i * nComp, p.begin() + (i + 1) * nComp, values[i]); - values = std::move(p); + values = std::move(p); - for (unsigned int s = 0; s < nSec; ++s) - { - for (unsigned int i = 0; i < nChannel; ++i) - parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, i, cadet::BoundStateIndep, cadet::ReactionIndep, (mode == cadet::model::MultiplexMode::Radial) ? cadet::SectionIndep : s)] = &values[s * nChannel * nComp + i * nComp]; - } - } - break; - case cadet::model::MultiplexMode::ComponentRadial: - case cadet::model::MultiplexMode::ComponentRadialSection: - cadet::registerParam3DArray(parameters, values, [=](bool multi, unsigned int sec, unsigned int compartment, unsigned int comp) { return cadet::makeParamId(nameHash, uoi, comp, compartment, cadet::BoundStateIndep, cadet::ReactionIndep, multi ? sec : cadet::SectionIndep); }, nComp, nChannel); - break; - case cadet::model::MultiplexMode::Axial: - case cadet::model::MultiplexMode::AxialRadial: - case cadet::model::MultiplexMode::Type: - case cadet::model::MultiplexMode::ComponentType: - case cadet::model::MultiplexMode::ComponentSectionType: - cadet_assert(false); - break; + for (unsigned int s = 0; s < nSec; ++s) + { + for (unsigned int i = 0; i < nChannel; ++i) + parameters[cadet::makeParamId( + nameHash, uoi, cadet::CompIndep, i, cadet::BoundStateIndep, cadet::ReactionIndep, + (mode == cadet::model::MultiplexMode::Radial) ? cadet::SectionIndep : s)] = + &values[s * nChannel * nComp + i * nComp]; + } + } + break; + case cadet::model::MultiplexMode::ComponentRadial: + case cadet::model::MultiplexMode::ComponentRadialSection: + cadet::registerParam3DArray( + parameters, values, + [=](bool multi, unsigned int sec, unsigned int compartment, unsigned int comp) { + return cadet::makeParamId(nameHash, uoi, comp, compartment, cadet::BoundStateIndep, + cadet::ReactionIndep, multi ? sec : cadet::SectionIndep); + }, + nComp, nChannel); + break; + case cadet::model::MultiplexMode::Axial: + case cadet::model::MultiplexMode::AxialRadial: + case cadet::model::MultiplexMode::Type: + case cadet::model::MultiplexMode::ComponentType: + case cadet::model::MultiplexMode::ComponentSectionType: + cadet_assert(false); + break; } return mode; } -bool multiplexParameterValue(const cadet::ParameterId& pId, cadet::StringHash nameHash, cadet::model::MultiplexMode mode, std::vector& data, unsigned int nComp, unsigned int nChannel, double value, std::unordered_set const* sensParams) +bool multiplexParameterValue(const cadet::ParameterId& pId, cadet::StringHash nameHash, + cadet::model::MultiplexMode mode, std::vector& data, unsigned int nComp, + unsigned int nChannel, double value, std::unordered_set const* sensParams) { if (pId.name != nameHash) return false; switch (mode) { - case cadet::model::MultiplexMode::Independent: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + case cadet::model::MultiplexMode::Independent: { + if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[0])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[0])) + return false; - for (std::size_t i = 0; i < data.size(); ++i) - data[i].setValue(value); + for (std::size_t i = 0; i < data.size(); ++i) + data[i].setValue(value); - return true; - } - case cadet::model::MultiplexMode::Section: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Section: { + if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.section * nComp * nChannel])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.section * nComp * nChannel])) + return false; - for (unsigned int i = 0; i < nComp * nChannel; ++i) - data[i + pId.section * nComp * nChannel].setValue(value); + for (unsigned int i = 0; i < nComp * nChannel; ++i) + data[i + pId.section * nComp * nChannel].setValue(value); - return true; - } - case cadet::model::MultiplexMode::Component: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Component: { + if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.component])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.component])) + return false; - for (unsigned int i = 0; i < nChannel; ++i) - data[i * nComp + pId.component].setValue(value); + for (unsigned int i = 0; i < nChannel; ++i) + data[i * nComp + pId.component].setValue(value); - return true; - } - case cadet::model::MultiplexMode::ComponentSection: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::ComponentSection: { + if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.component + pId.section * nComp * nChannel])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.component + pId.section * nComp * nChannel])) + return false; - for (unsigned int i = 0; i < nChannel; ++i) - data[i * nComp + pId.component + pId.section * nComp * nChannel].setValue(value); + for (unsigned int i = 0; i < nChannel; ++i) + data[i * nComp + pId.component + pId.section * nComp * nChannel].setValue(value); - return true; - } - case cadet::model::MultiplexMode::Radial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Radial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.particleType * nComp])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.particleType * nComp])) + return false; - for (unsigned int i = 0; i < nComp; ++i) - data[i + pId.particleType * nComp].setValue(value); + for (unsigned int i = 0; i < nComp; ++i) + data[i + pId.particleType * nComp].setValue(value); - return true; - } - case cadet::model::MultiplexMode::RadialSection: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::RadialSection: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.particleType * nComp + pId.section * nComp * nChannel])) - return false; + if (sensParams && + !cadet::contains(*sensParams, &data[pId.particleType * nComp + pId.section * nComp * nChannel])) + return false; - for (unsigned int i = 0; i < nComp; ++i) - data[i + pId.particleType * nComp + pId.section * nComp * nChannel].setValue(value); + for (unsigned int i = 0; i < nComp; ++i) + data[i + pId.particleType * nComp + pId.section * nComp * nChannel].setValue(value); - return true; - } - case cadet::model::MultiplexMode::ComponentRadial: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::ComponentRadial: { + if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.component + pId.particleType * nComp])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.component + pId.particleType * nComp])) + return false; - data[pId.component + pId.particleType * nComp].setValue(value); + data[pId.component + pId.particleType * nComp].setValue(value); - return true; - } - case cadet::model::MultiplexMode::ComponentRadialSection: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::ComponentRadialSection: { + if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.component + pId.particleType * nComp + pId.section * nComp * nChannel])) - return false; + if (sensParams && + !cadet::contains(*sensParams, + &data[pId.component + pId.particleType * nComp + pId.section * nComp * nChannel])) + return false; - data[pId.component + pId.particleType * nComp + pId.section * nComp * nChannel].setValue(value); + data[pId.component + pId.particleType * nComp + pId.section * nComp * nChannel].setValue(value); - return true; - } - case cadet::model::MultiplexMode::Axial: - case cadet::model::MultiplexMode::AxialRadial: - case cadet::model::MultiplexMode::Type: - case cadet::model::MultiplexMode::ComponentType: - case cadet::model::MultiplexMode::ComponentSectionType: - cadet_assert(false); - break; + return true; + } + case cadet::model::MultiplexMode::Axial: + case cadet::model::MultiplexMode::AxialRadial: + case cadet::model::MultiplexMode::Type: + case cadet::model::MultiplexMode::ComponentType: + case cadet::model::MultiplexMode::ComponentSectionType: + cadet_assert(false); + break; } return false; } -bool multiplexParameterAD(const cadet::ParameterId& pId, cadet::StringHash nameHash, cadet::model::MultiplexMode mode, std::vector& data, unsigned int nComp, unsigned int nChannel, unsigned int adDirection, double adValue, std::unordered_set& sensParams) +bool multiplexParameterAD(const cadet::ParameterId& pId, cadet::StringHash nameHash, cadet::model::MultiplexMode mode, + std::vector& data, unsigned int nComp, unsigned int nChannel, + unsigned int adDirection, double adValue, std::unordered_set& sensParams) { if (pId.name != nameHash) return false; switch (mode) { - case cadet::model::MultiplexMode::Independent: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + case cadet::model::MultiplexMode::Independent: { + if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - sensParams.insert(&data[0]); + sensParams.insert(&data[0]); - for (std::size_t i = 0; i < data.size(); ++i) - data[i].setADValue(adDirection, adValue); + for (std::size_t i = 0; i < data.size(); ++i) + data[i].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::Section: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Section: { + if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.section * nComp * nChannel]); + sensParams.insert(&data[pId.section * nComp * nChannel]); - for (unsigned int i = 0; i < nComp * nChannel; ++i) - data[i + pId.section * nComp * nChannel].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nComp * nChannel; ++i) + data[i + pId.section * nComp * nChannel].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::Component: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Component: { + if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.component]); + sensParams.insert(&data[pId.component]); - for (unsigned int i = 0; i < nChannel; ++i) - data[i * nComp + pId.component].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nChannel; ++i) + data[i * nComp + pId.component].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::ComponentSection: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::ComponentSection: { + if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.component + pId.section * nComp * nChannel]); + sensParams.insert(&data[pId.component + pId.section * nComp * nChannel]); - for (unsigned int i = 0; i < nChannel; ++i) - data[i * nComp + pId.component + pId.section * nComp * nChannel].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nChannel; ++i) + data[i * nComp + pId.component + pId.section * nComp * nChannel].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::Radial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Radial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.particleType * nComp]); + sensParams.insert(&data[pId.particleType * nComp]); - for (unsigned int i = 0; i < nComp; ++i) - data[i + pId.particleType * nComp].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nComp; ++i) + data[i + pId.particleType * nComp].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::RadialSection: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::RadialSection: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.particleType * nComp + pId.section * nComp * nChannel]); + sensParams.insert(&data[pId.particleType * nComp + pId.section * nComp * nChannel]); - for (unsigned int i = 0; i < nComp; ++i) - data[i + pId.particleType * nComp + pId.section * nComp * nChannel].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nComp; ++i) + data[i + pId.particleType * nComp + pId.section * nComp * nChannel].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::ComponentRadial: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::ComponentRadial: { + if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.component + pId.particleType * nComp]); + sensParams.insert(&data[pId.component + pId.particleType * nComp]); - data[pId.component + pId.particleType * nComp].setADValue(adDirection, adValue); + data[pId.component + pId.particleType * nComp].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::ComponentRadialSection: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::ComponentRadialSection: { + if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.component + pId.particleType * nComp + pId.section * nComp * nChannel]); + sensParams.insert(&data[pId.component + pId.particleType * nComp + pId.section * nComp * nChannel]); - data[pId.component + pId.particleType * nComp + pId.section * nComp * nChannel].setADValue(adDirection, adValue); + data[pId.component + pId.particleType * nComp + pId.section * nComp * nChannel].setADValue(adDirection, + adValue); - return true; - } - case cadet::model::MultiplexMode::Axial: - case cadet::model::MultiplexMode::AxialRadial: - case cadet::model::MultiplexMode::Type: - case cadet::model::MultiplexMode::ComponentType: - case cadet::model::MultiplexMode::ComponentSectionType: - cadet_assert(false); - break; + return true; + } + case cadet::model::MultiplexMode::Axial: + case cadet::model::MultiplexMode::AxialRadial: + case cadet::model::MultiplexMode::Type: + case cadet::model::MultiplexMode::ComponentType: + case cadet::model::MultiplexMode::ComponentSectionType: + cadet_assert(false); + break; } return false; } -} // namespace +} // namespace namespace cadet { @@ -464,26 +501,34 @@ namespace parts class MultiChannelConvectionDispersionOperator::LinearSolver { public: + virtual ~LinearSolver() CADET_NOEXCEPT + { + } - virtual ~LinearSolver() CADET_NOEXCEPT { } - - virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, unsigned int nChannel, const Weno& weno) = 0; + virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, + unsigned int nChannel, const Weno& weno) = 0; virtual void setSparsityPattern(const cadet::linalg::SparsityPattern& pattern) = 0; virtual void assembleDiscretizedJacobian(double alpha) = 0; virtual bool factorize() = 0; - virtual bool solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, double outerTol) const = 0; + virtual bool solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, + double outerTol) const = 0; }; int matrixMultiplierMultiChannelCDO(void* userData, double const* x, double* z); -class MultiChannelConvectionDispersionOperator::GmresSolver : public MultiChannelConvectionDispersionOperator::LinearSolver +class MultiChannelConvectionDispersionOperator::GmresSolver + : public MultiChannelConvectionDispersionOperator::LinearSolver { public: + GmresSolver(linalg::CompressedSparseMatrix const* jacC) : _jacC(jacC) + { + } + virtual ~GmresSolver() CADET_NOEXCEPT + { + } - GmresSolver(linalg::CompressedSparseMatrix const* jacC) : _jacC(jacC) { } - virtual ~GmresSolver() CADET_NOEXCEPT { } - - virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, unsigned int nChannel, const Weno& weno) + virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, + unsigned int nChannel, const Weno& weno) { _gmres.initialize(nCol * nComp * nChannel, 0, linalg::toOrthogonalization(1), 0); _gmres.matrixVectorMultiplier(&matrixMultiplierMultiChannelCDO, this); @@ -492,14 +537,19 @@ class MultiChannelConvectionDispersionOperator::GmresSolver : public MultiChanne return true; } - virtual void setSparsityPattern(const linalg::SparsityPattern& pattern) { } + virtual void setSparsityPattern(const linalg::SparsityPattern& pattern) + { + } virtual void assembleDiscretizedJacobian(double alpha) { _alpha = alpha; } - virtual bool factorize() { return true; } + virtual bool factorize() + { + return true; + } virtual bool solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, double outerTol) const { @@ -515,7 +565,7 @@ class MultiChannelConvectionDispersionOperator::GmresSolver : public MultiChanne protected: linalg::CompressedSparseMatrix const* const _jacC; double _alpha; - mutable linalg::Gmres _gmres; //!< GMRES algorithm for the Schur-complement in linearSolve() + mutable linalg::Gmres _gmres; //!< GMRES algorithm for the Schur-complement in linearSolve() mutable std::vector _cache; //!< GMRES cache for result int matrixVectorMultiply(double const* x, double* z) const @@ -531,70 +581,81 @@ class MultiChannelConvectionDispersionOperator::GmresSolver : public MultiChanne int matrixMultiplierMultiChannelCDO(void* userData, double const* x, double* z) { - MultiChannelConvectionDispersionOperator::GmresSolver* const cdo = static_cast(userData); + MultiChannelConvectionDispersionOperator::GmresSolver* const cdo = + static_cast(userData); return cdo->matrixVectorMultiply(x, z); } #if defined(UMFPACK_FOUND) || defined(SUPERLU_FOUND) - template - class MultiChannelConvectionDispersionOperator::SparseDirectSolver : public MultiChannelConvectionDispersionOperator::LinearSolver +template +class MultiChannelConvectionDispersionOperator::SparseDirectSolver + : public MultiChannelConvectionDispersionOperator::LinearSolver +{ +public: + SparseDirectSolver(linalg::CompressedSparseMatrix const* jacC) : _jacC(jacC) { - public: - - SparseDirectSolver(linalg::CompressedSparseMatrix const* jacC) : _jacC(jacC) { } - virtual ~SparseDirectSolver() CADET_NOEXCEPT { } + } + virtual ~SparseDirectSolver() CADET_NOEXCEPT + { + } - virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, unsigned int nChannel, const Weno& weno) - { - return true; - } + virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, + unsigned int nChannel, const Weno& weno) + { + return true; + } - virtual void setSparsityPattern(const linalg::SparsityPattern& pattern) - { - _jacCdisc.assignPattern(pattern); - _jacCdisc.prepare(); - } + virtual void setSparsityPattern(const linalg::SparsityPattern& pattern) + { + _jacCdisc.assignPattern(pattern); + _jacCdisc.prepare(); + } - virtual void assembleDiscretizedJacobian(double alpha) - { - // Copy normal matrix over to factorizable matrix - _jacCdisc.copyFromSamePattern(*_jacC); + virtual void assembleDiscretizedJacobian(double alpha) + { + // Copy normal matrix over to factorizable matrix + _jacCdisc.copyFromSamePattern(*_jacC); - for (int i = 0; i < _jacC->rows(); ++i) - _jacCdisc.centered(i, 0) += alpha; - } + for (int i = 0; i < _jacC->rows(); ++i) + _jacCdisc.centered(i, 0) += alpha; + } - virtual bool factorize() - { - return _jacCdisc.factorize(); - } + virtual bool factorize() + { + return _jacCdisc.factorize(); + } - virtual bool solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, double outerTol) const - { - return _jacCdisc.solve(rhs); - } + virtual bool solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, double outerTol) const + { + return _jacCdisc.solve(rhs); + } - protected: - linalg::CompressedSparseMatrix const* const _jacC; - sparse_t _jacCdisc; - }; +protected: + linalg::CompressedSparseMatrix const* const _jacC; + sparse_t _jacCdisc; +}; #endif -class MultiChannelConvectionDispersionOperator::DenseDirectSolver : public MultiChannelConvectionDispersionOperator::LinearSolver +class MultiChannelConvectionDispersionOperator::DenseDirectSolver + : public MultiChannelConvectionDispersionOperator::LinearSolver { public: + DenseDirectSolver(linalg::CompressedSparseMatrix const* jacC) : _jacC(jacC) + { + } + virtual ~DenseDirectSolver() CADET_NOEXCEPT + { + } - DenseDirectSolver(linalg::CompressedSparseMatrix const* jacC) : _jacC(jacC) { } - virtual ~DenseDirectSolver() CADET_NOEXCEPT { } - - virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, unsigned int nChannel, const Weno& weno) + virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, + unsigned int nChannel, const Weno& weno) { // Note that we have to increase the lower bandwidth by 1 because the WENO stencil is applied to the - // right cell face (lower + 1 + upper) and to the left cell face (shift the stencil by -1 because influx of cell i - // is outflux of cell i-1) - // We also have to make sure that there's at least one sub and super diagonal for the dispersion term + // right cell face (lower + 1 + upper) and to the left cell face (shift the stencil by -1 because influx of cell + // i is outflux of cell i-1) We also have to make sure that there's at least one sub and super diagonal for the + // dispersion term const unsigned int lb = std::max(weno.lowerBandwidth() + 1u, 1u) * nComp * nChannel; // We have to make sure that there's at least one sub and super diagonal for the dispersion term @@ -609,7 +670,9 @@ class MultiChannelConvectionDispersionOperator::DenseDirectSolver : public Multi return true; } - virtual void setSparsityPattern(const linalg::SparsityPattern& pattern) { } + virtual void setSparsityPattern(const linalg::SparsityPattern& pattern) + { + } virtual void assembleDiscretizedJacobian(double alpha) { @@ -650,12 +713,12 @@ class MultiChannelConvectionDispersionOperator::DenseDirectSolver : public Multi linalg::FactorizableBandMatrix _jacCdisc; }; - /** * @brief Creates a MultiChannelConvectionDispersionOperator */ -MultiChannelConvectionDispersionOperator::MultiChannelConvectionDispersionOperator() : _dir(0), _stencilMemory(sizeof(active) * Weno::maxStencilSize()), - _wenoDerivatives(new double[Weno::maxStencilSize()]), _weno(), _linearSolver(nullptr) +MultiChannelConvectionDispersionOperator::MultiChannelConvectionDispersionOperator() + : _dir(0), _stencilMemory(sizeof(active) * Weno::maxStencilSize()), + _wenoDerivatives(new double[Weno::maxStencilSize()]), _weno(), _linearSolver(nullptr) { } @@ -674,7 +737,11 @@ MultiChannelConvectionDispersionOperator::~MultiChannelConvectionDispersionOpera * @param [in] dynamicReactions Determines whether the sparsity pattern accounts for dynamic reactions * @return @c true if configuration went fine, @c false otherwise */ -bool MultiChannelConvectionDispersionOperator::configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, unsigned int nCol, unsigned int nChannel, bool dynamicReactions) +bool MultiChannelConvectionDispersionOperator::configureModelDiscretization(IParameterProvider& paramProvider, + const IConfigHelper& helper, + unsigned int nComp, unsigned int nCol, + unsigned int nChannel, + bool dynamicReactions) { _nComp = nComp; _nCol = nCol; @@ -721,7 +788,8 @@ bool MultiChannelConvectionDispersionOperator::configureModelDiscretization(IPar #else _linearSolver = new DenseDirectSolver(&_jacC); #endif -// LOG(Info) << "Default to dense banded linear solver due to invalid or missing LINEAR_SOLVER_BULK setting"; + // LOG(Info) << "Default to dense banded linear solver due to invalid or missing LINEAR_SOLVER_BULK + // setting"; } paramProvider.popScope(); @@ -740,7 +808,8 @@ bool MultiChannelConvectionDispersionOperator::configureModelDiscretization(IPar * @param [out] parameters Map in which local parameters are inserted * @return @c true if configuration went fine, @c false otherwise */ -bool MultiChannelConvectionDispersionOperator::configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, std::unordered_map& parameters) +bool MultiChannelConvectionDispersionOperator::configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, + std::unordered_map& parameters) { // Read geometry parameters _colLength = paramProvider.getDouble("COL_LENGTH"); @@ -773,11 +842,16 @@ bool MultiChannelConvectionDispersionOperator::configure(UnitOpIdx unitOpIdx, IP _singleVelocity = false; if (!_singleVelocity && (_velocity.size() % _nChannel != 0)) - throw InvalidParameterException("Number of elements in field VELOCITY is not a positive multiple of NCHANNEL (" + std::to_string(_nChannel) + ")"); + throw InvalidParameterException( + "Number of elements in field VELOCITY is not a positive multiple of NCHANNEL (" + + std::to_string(_nChannel) + ")"); if ((mode == 0) && (_velocity.size() != 1)) - throw InvalidParameterException("Number of elements in field VELOCITY inconsistent with VELOCITY_MULTIPLEX (should be 1)"); + throw InvalidParameterException( + "Number of elements in field VELOCITY inconsistent with VELOCITY_MULTIPLEX (should be 1)"); if ((mode == 1) && (_velocity.size() != _nChannel)) - throw InvalidParameterException("Number of elements in field VELOCITY inconsistent with VELOCITY_MULTIPLEX (should be " + std::to_string(_nChannel) + ")"); + throw InvalidParameterException( + "Number of elements in field VELOCITY inconsistent with VELOCITY_MULTIPLEX (should be " + + std::to_string(_nChannel) + ")"); } else { @@ -809,24 +883,40 @@ bool MultiChannelConvectionDispersionOperator::configure(UnitOpIdx unitOpIdx, IP { // Register only the first item in each section for (std::size_t i = 0; i < _velocity.size() / _nChannel; ++i) - parameters[makeParamId(hashString("VELOCITY"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, i)] = &_velocity[i * _nChannel]; + parameters[makeParamId(hashString("VELOCITY"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, i)] = &_velocity[i * _nChannel]; } else { // We have only one parameter - parameters[makeParamId(hashString("VELOCITY"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_velocity[0]; + parameters[makeParamId(hashString("VELOCITY"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_velocity[0]; } } else - registerParam2DArray(parameters, _velocity, [=](bool multi, unsigned int sec, unsigned int compartment) { return makeParamId(hashString("VELOCITY"), unitOpIdx, CompIndep, compartment, BoundStateIndep, ReactionIndep, multi ? sec : SectionIndep); }, _nChannel); + registerParam2DArray( + parameters, _velocity, + [=](bool multi, unsigned int sec, unsigned int compartment) { + return makeParamId(hashString("VELOCITY"), unitOpIdx, CompIndep, compartment, BoundStateIndep, + ReactionIndep, multi ? sec : SectionIndep); + }, + _nChannel); _dir = std::vector(_nChannel, 1); - _axialDispersionMode = readAndRegisterMultiplexParam(paramProvider, parameters, _axialDispersion, "COL_DISPERSION", _nComp, _nChannel, unitOpIdx); + _axialDispersionMode = readAndRegisterMultiplexParam(paramProvider, parameters, _axialDispersion, "COL_DISPERSION", + _nComp, _nChannel, unitOpIdx); // Add parameters to map - parameters[makeParamId(hashString("COL_LENGTH"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_colLength; - registerParam3DArray(parameters, _exchangeMatrix, [=](bool multi, unsigned int channelSrc, unsigned int channelDest, unsigned comp) { return makeParamId(hashString("EXCHANGE_MATRIX"), unitOpIdx, multi ? comp : CompIndep, channelDest, channelSrc, ReactionIndep, SectionIndep); }, _nComp, _nChannel); + parameters[makeParamId(hashString("COL_LENGTH"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep)] = &_colLength; + registerParam3DArray( + parameters, _exchangeMatrix, + [=](bool multi, unsigned int channelSrc, unsigned int channelDest, unsigned comp) { + return makeParamId(hashString("EXCHANGE_MATRIX"), unitOpIdx, multi ? comp : CompIndep, channelDest, + channelSrc, ReactionIndep, SectionIndep); + }, + _nComp, _nChannel); setSparsityPattern(); @@ -863,7 +953,6 @@ bool MultiChannelConvectionDispersionOperator::notifyDiscontinuousSectionTransit } } - // Change the sparsity pattern if necessary if ((secIdx == 0) || hasChanged) setSparsityPattern(); @@ -903,7 +992,8 @@ void MultiChannelConvectionDispersionOperator::prepareADvectors(const AdJacobian * @param [in] in Total volumetric inlet flow rate * @param [in] out Total volumetric outlet flow rate */ -void MultiChannelConvectionDispersionOperator::setFlowRates(int compartment, const active& in, const active& out) CADET_NOEXCEPT +void MultiChannelConvectionDispersionOperator::setFlowRates(int compartment, const active& in, + const active& out) CADET_NOEXCEPT { _curVelocity[compartment] = _dir[compartment] * in / (_crossSections[compartment]); } @@ -920,12 +1010,12 @@ double MultiChannelConvectionDispersionOperator::inletFactor(unsigned int idxSec return -std::abs(static_cast(_curVelocity[idxRad])) / h; } -const active& MultiChannelConvectionDispersionOperator::axialDispersion(unsigned int idxSec, int idxRad, int idxComp) const CADET_NOEXCEPT +const active& MultiChannelConvectionDispersionOperator::axialDispersion(unsigned int idxSec, int idxRad, + int idxComp) const CADET_NOEXCEPT { return *(getSectionDependentSlice(_axialDispersion, _nChannel * _nComp, idxSec) + idxRad * _nComp + idxComp); } - /** * @brief Computes the residual of the transport equations * @param [in] t Current time point @@ -936,7 +1026,9 @@ const active& MultiChannelConvectionDispersionOperator::axialDispersion(unsigned * @param [in] wantJac Determines whether the Jacobian is computed or not * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ -int MultiChannelConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, bool wantJac, WithoutParamSensitivity) +int MultiChannelConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, double* res, bool wantJac, + WithoutParamSensitivity) { if (wantJac) return residualImpl(model, t, secIdx, y, yDot, res); @@ -944,7 +1036,9 @@ int MultiChannelConvectionDispersionOperator::residual(const IModel& model, doub return residualImpl(model, t, secIdx, y, yDot, res); } -int MultiChannelConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithoutParamSensitivity) +int MultiChannelConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, + active const* y, double const* yDot, active* res, bool wantJac, + WithoutParamSensitivity) { if (wantJac) return residualImpl(model, t, secIdx, y, yDot, res); @@ -952,7 +1046,9 @@ int MultiChannelConvectionDispersionOperator::residual(const IModel& model, doub return residualImpl(model, t, secIdx, y, yDot, res); } -int MultiChannelConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity) +int MultiChannelConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, active* res, bool wantJac, + WithParamSensitivity) { if (wantJac) return residualImpl(model, t, secIdx, y, yDot, res); @@ -960,7 +1056,9 @@ int MultiChannelConvectionDispersionOperator::residual(const IModel& model, doub return residualImpl(model, t, secIdx, y, yDot, res); } -int MultiChannelConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity) +int MultiChannelConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, + active const* y, double const* yDot, active* res, bool wantJac, + WithParamSensitivity) { if (wantJac) return residualImpl(model, t, secIdx, y, yDot, res); @@ -969,7 +1067,8 @@ int MultiChannelConvectionDispersionOperator::residual(const IModel& model, doub } template -int MultiChannelConvectionDispersionOperator::residualImpl(const IModel& model, double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res) +int MultiChannelConvectionDispersionOperator::residualImpl(const IModel& model, double t, unsigned int secIdx, + StateType const* y, double const* yDot, ResidualType* res) { if (wantJac) { @@ -991,19 +1090,20 @@ int MultiChannelConvectionDispersionOperator::residualImpl(const IModel& model, &_weno, &_stencilMemory, _wenoEpsilon, - static_cast(_nComp * _nChannel), // Stride between two cells + static_cast(_nComp * _nChannel), // Stride between two cells _nComp, _nCol, - _nComp * i, // Offset to the first component of the inlet DOFs in the local state vector - _nComp * (_nChannel + i), // Offset to the first component of the first bulk cell in the local state vector + _nComp * i, // Offset to the first component of the inlet DOFs in the local state vector + _nComp * (_nChannel + i), // Offset to the first component of the first bulk cell in the local state vector _dispersionDep, - model - }; + model}; if (wantJac) - convdisp::residualKernelAxial(SimulationTime{t, secIdx}, y, yDot, res, _jacC.row(i * _nComp), fp); + convdisp::residualKernelAxial( + SimulationTime{t, secIdx}, y, yDot, res, _jacC.row(i * _nComp), fp); else - convdisp::residualKernelAxial(SimulationTime{t, secIdx}, y, yDot, res, _jacC.row(i * _nComp), fp); + convdisp::residualKernelAxial( + SimulationTime{t, secIdx}, y, yDot, res, _jacC.row(i * _nComp), fp); } // Handle inter-channel transport @@ -1024,41 +1124,44 @@ int MultiChannelConvectionDispersionOperator::residualImpl(const IModel& model, ResidualType* const resColRadOrigBlock = resColBlock + offsetToRadOrigBlock; StateType const* const yColRadOrigBlock = yColBlock + offsetToRadOrigBlock; - for (unsigned int rad_dest = 0; rad_dest < _nChannel; ++rad_dest) - { + for (unsigned int rad_dest = 0; rad_dest < _nChannel; ++rad_dest) + { if (rad_orig == rad_dest) continue; - const unsigned int offsetToRadDestBlock = rad_dest * _nComp; - const unsigned int offsetColRadDestBlock = offsetColBlock + offsetToRadDestBlock; - ResidualType* const resColRadDestBlock = resColBlock + offsetToRadDestBlock; - // StateType const* const yColRadDestBlock = yColBlock + offsetToRadDestBlock; - - for (unsigned int comp = 0; comp < _nComp; ++comp) - { - const unsigned int offsetCur_orig = offsetColRadOrigBlock + comp; - const unsigned int offsetCur_dest = offsetColRadDestBlock + comp; - StateType const* const yCur_orig = yColRadOrigBlock + comp; - // StateType const* const yCur_dest = yColRadDestBlock + comp; - ResidualType* const resCur_orig = resColRadOrigBlock + comp; - ResidualType* const resCur_dest = resColRadDestBlock + comp; - - const ParamType exchange_orig_dest_comp = static_cast(_exchangeMatrix[rad_orig * _nChannel * _nComp + rad_dest * _nComp + comp]); + const unsigned int offsetToRadDestBlock = rad_dest * _nComp; + const unsigned int offsetColRadDestBlock = offsetColBlock + offsetToRadDestBlock; + ResidualType* const resColRadDestBlock = resColBlock + offsetToRadDestBlock; + // StateType const* const yColRadDestBlock = yColBlock + offsetToRadDestBlock; + + for (unsigned int comp = 0; comp < _nComp; ++comp) + { + const unsigned int offsetCur_orig = offsetColRadOrigBlock + comp; + const unsigned int offsetCur_dest = offsetColRadDestBlock + comp; + StateType const* const yCur_orig = yColRadOrigBlock + comp; + // StateType const* const yCur_dest = yColRadDestBlock + comp; + ResidualType* const resCur_orig = resColRadOrigBlock + comp; + ResidualType* const resCur_dest = resColRadDestBlock + comp; + + const ParamType exchange_orig_dest_comp = static_cast( + _exchangeMatrix[rad_orig * _nChannel * _nComp + rad_dest * _nComp + comp]); if (cadet_likely(exchange_orig_dest_comp > 0.0)) { *resCur_orig += exchange_orig_dest_comp * yCur_orig[0]; - *resCur_dest -= exchange_orig_dest_comp * yCur_orig[0] * static_cast(_crossSections[rad_orig]) / static_cast(_crossSections[rad_dest]); + *resCur_dest -= exchange_orig_dest_comp * yCur_orig[0] * + static_cast(_crossSections[rad_orig]) / + static_cast(_crossSections[rad_dest]); if (wantJac) { _jacC.centered(offsetCur_orig, 0) += static_cast(exchange_orig_dest_comp); - _jacC.centered(offsetCur_dest, static_cast(offsetCur_orig) - static_cast(offsetCur_dest)) -= static_cast(exchange_orig_dest_comp); + _jacC.centered(offsetCur_dest, + static_cast(offsetCur_orig) - static_cast(offsetCur_dest)) -= + static_cast(exchange_orig_dest_comp); } } - } - - } - + } + } } } @@ -1078,7 +1181,8 @@ void MultiChannelConvectionDispersionOperator::setSparsityPattern() // Handle convection, axial dispersion (WENO) for (unsigned int i = 0; i < _nChannel; ++i) - cadet::model::parts::convdisp::sparsityPatternAxial(pattern.row(i * _nComp), _nComp, _nCol, _nComp * _nChannel, static_cast(_curVelocity[i]), _weno); + cadet::model::parts::convdisp::sparsityPatternAxial(pattern.row(i * _nComp), _nComp, _nCol, _nComp * _nChannel, + static_cast(_curVelocity[i]), _weno); if (_nChannel > 1) { @@ -1088,11 +1192,11 @@ void MultiChannelConvectionDispersionOperator::setSparsityPattern() const unsigned int idxColBlock = col * _nChannel * _nComp; // Connecting from all compartments (orig) to all compartments (dest) - for (unsigned int rad_orig = 0; rad_orig < _nChannel ; ++rad_orig) + for (unsigned int rad_orig = 0; rad_orig < _nChannel; ++rad_orig) { const unsigned int idxColRadBlock_orig = idxColBlock + rad_orig * _nComp; - for (unsigned int rad_dest = 0; rad_dest < _nChannel ; ++rad_dest) + for (unsigned int rad_dest = 0; rad_dest < _nChannel; ++rad_dest) { // Don't connect compartments with themselves if (rad_orig == rad_dest) @@ -1102,7 +1206,8 @@ void MultiChannelConvectionDispersionOperator::setSparsityPattern() for (unsigned int comp = 0; comp < _nComp; ++comp) { - const active& exchange_orig_dest_comp = _exchangeMatrix[rad_orig * _nChannel * _nComp + rad_dest * _nComp + comp]; + const active& exchange_orig_dest_comp = + _exchangeMatrix[rad_orig * _nChannel * _nComp + rad_dest * _nComp + comp]; if (exchange_orig_dest_comp == 0.0) continue; @@ -1142,17 +1247,20 @@ void MultiChannelConvectionDispersionOperator::setSparsityPattern() } /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ + * with a given vector * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). * * Note that this function only performs multiplication with the Jacobian of the (axial) transport equations. - * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit operation's arrays. + * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit + * operation's arrays. * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] sDot Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ * @param [out] ret Vector @f$ z @f$ which stores the result of the operation */ -void MultiChannelConvectionDispersionOperator::multiplyWithDerivativeJacobian(const SimulationTime& simTime, double const* sDot, double* ret) const +void MultiChannelConvectionDispersionOperator::multiplyWithDerivativeJacobian(const SimulationTime& simTime, + double const* sDot, double* ret) const { double* localRet = ret + _nComp * _nChannel; double const* localSdot = sDot + _nComp * _nChannel; @@ -1162,11 +1270,13 @@ void MultiChannelConvectionDispersionOperator::multiplyWithDerivativeJacobian(co /** * @brief Extracts the system Jacobian from band compressed AD seed vectors - * @details The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit operation's arrays. + * @details The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit + * operation's arrays. * @param [in] adRes Residual vector of AD datatypes with band compressed seed vectors * @param [in] adDirOffset Number of AD directions used for non-Jacobian purposes (e.g., parameter sensitivities) */ -void MultiChannelConvectionDispersionOperator::extractJacobianFromAD(active const* const adRes, unsigned int adDirOffset) +void MultiChannelConvectionDispersionOperator::extractJacobianFromAD(active const* const adRes, + unsigned int adDirOffset) { // todo: implement compressed AD @@ -1180,16 +1290,13 @@ void MultiChannelConvectionDispersionOperator::extractJacobianFromAD(active cons /** * @brief Assembles the axial transport Jacobian @f$ J_0 @f$ of the time-discretized equations - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The system Jacobian of the original equations, - * \f[ \frac{\partial F}{\partial y}, \f] - * is already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible - * for adding - * \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] - * to the system Jacobian, which yields the Jacobian of the time-discretized equations - * \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] - * when a BDF method is used. The time integrator needs to solve this equation for @f$ y_0 @f$, which requires - * the solution of the linear system mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + * b \f] has to be solved. The system Jacobian of the original equations, \f[ \frac{\partial F}{\partial y}, \f] is + * already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible for + * adding \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] to the system Jacobian, which yields the Jacobian of the + * time-discretized equations \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] when a BDF method is used. + * The time integrator needs to solve this equation for @f$ y_0 @f$, which requires the solution of the linear system + * mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). * * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) */ @@ -1219,7 +1326,8 @@ bool MultiChannelConvectionDispersionOperator::assembleAndFactorizeDiscretizedJa * @param [in,out] rhs On entry, right hand side of the equation system. On exit, solution of the system. * @return @c true if the system was solved correctly, otherwise @c false */ -bool MultiChannelConvectionDispersionOperator::solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, double outerTol) const +bool MultiChannelConvectionDispersionOperator::solveDiscretizedJacobian(double* rhs, double const* weight, + double const* init, double outerTol) const { return _linearSolver->solveDiscretizedJacobian(rhs, weight, init, outerTol); } @@ -1232,7 +1340,8 @@ bool MultiChannelConvectionDispersionOperator::solveDiscretizedJacobian(double* * @param [in,out] rhs On entry, right hand side. On exit, solution of the system. * @return @c true if the system was solved correctly, @c false otherwise */ -bool MultiChannelConvectionDispersionOperator::solveTimeDerivativeSystem(const SimulationTime& simTime, double* const rhs) +bool MultiChannelConvectionDispersionOperator::solveTimeDerivativeSystem(const SimulationTime& simTime, + double* const rhs) { return true; } @@ -1240,7 +1349,8 @@ bool MultiChannelConvectionDispersionOperator::solveTimeDerivativeSystem(const S bool MultiChannelConvectionDispersionOperator::setParameter(const ParameterId& pId, double value) { - if (_singleVelocity && (pId.name == hashString("VELOCITY")) && (pId.component == CompIndep) && (pId.boundState == BoundStateIndep) && (pId.reaction == ReactionIndep)) + if (_singleVelocity && (pId.name == hashString("VELOCITY")) && (pId.component == CompIndep) && + (pId.boundState == BoundStateIndep) && (pId.reaction == ReactionIndep)) { if (_velocity.size() > _nChannel) { @@ -1262,17 +1372,20 @@ bool MultiChannelConvectionDispersionOperator::setParameter(const ParameterId& p } } - const bool ad = multiplexParameterValue(pId, hashString("COL_DISPERSION"), _axialDispersionMode, _axialDispersion, _nComp, _nChannel, value, nullptr); + const bool ad = multiplexParameterValue(pId, hashString("COL_DISPERSION"), _axialDispersionMode, _axialDispersion, + _nComp, _nChannel, value, nullptr); if (ad) return true; return false; } -bool MultiChannelConvectionDispersionOperator::setSensitiveParameterValue(const std::unordered_set& sensParams, const ParameterId& pId, double value) +bool MultiChannelConvectionDispersionOperator::setSensitiveParameterValue(const std::unordered_set& sensParams, + const ParameterId& pId, double value) { - if (_singleVelocity && (pId.name == hashString("VELOCITY")) && (pId.component == CompIndep) && (pId.boundState == BoundStateIndep) && (pId.reaction == ReactionIndep)) + if (_singleVelocity && (pId.name == hashString("VELOCITY")) && (pId.component == CompIndep) && + (pId.boundState == BoundStateIndep) && (pId.reaction == ReactionIndep)) { if (_velocity.size() > _nChannel) { @@ -1294,17 +1407,21 @@ bool MultiChannelConvectionDispersionOperator::setSensitiveParameterValue(const } } - const bool ad = multiplexParameterValue(pId, hashString("COL_DISPERSION"), _axialDispersionMode, _axialDispersion, _nComp, _nChannel, value, &sensParams); + const bool ad = multiplexParameterValue(pId, hashString("COL_DISPERSION"), _axialDispersionMode, _axialDispersion, + _nComp, _nChannel, value, &sensParams); if (ad) return true; return false; } -bool MultiChannelConvectionDispersionOperator::setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, unsigned int adDirection, double adValue) +bool MultiChannelConvectionDispersionOperator::setSensitiveParameter(std::unordered_set& sensParams, + const ParameterId& pId, unsigned int adDirection, + double adValue) { - if (_singleVelocity && (pId.name == hashString("VELOCITY")) && (pId.component == CompIndep) && (pId.boundState == BoundStateIndep) && (pId.reaction == ReactionIndep)) + if (_singleVelocity && (pId.name == hashString("VELOCITY")) && (pId.component == CompIndep) && + (pId.boundState == BoundStateIndep) && (pId.reaction == ReactionIndep)) { if (_velocity.size() > _nChannel) { @@ -1328,15 +1445,16 @@ bool MultiChannelConvectionDispersionOperator::setSensitiveParameter(std::unorde } } - const bool ad = multiplexParameterAD(pId, hashString("COL_DISPERSION"), _axialDispersionMode, _axialDispersion, _nComp, _nChannel, adDirection, adValue, sensParams); + const bool ad = multiplexParameterAD(pId, hashString("COL_DISPERSION"), _axialDispersionMode, _axialDispersion, + _nComp, _nChannel, adDirection, adValue, sensParams); if (ad) return true; return false; } -} // namespace parts +} // namespace parts -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/parts/MultiChannelConvectionDispersionOperator.hpp b/src/libcadet/model/parts/MultiChannelConvectionDispersionOperator.hpp index 501a9e330..ad2351e0c 100644 --- a/src/libcadet/model/parts/MultiChannelConvectionDispersionOperator.hpp +++ b/src/libcadet/model/parts/MultiChannelConvectionDispersionOperator.hpp @@ -48,8 +48,8 @@ namespace parts * @details Implements the equation * * @f[\begin{align} - \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i}(\rho) \frac{\partial^2 c_i}{\partial z^2} \\ -\end{align} @f] + \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i}(\rho) \frac{\partial^2 +c_i}{\partial z^2} \\ \end{align} @f] * with Danckwerts boundary conditions on the axial boundary (see @cite Danckwerts1953) @f[ \begin{align} u c_{\text{in},i}(t) &= u c_i(t,0,\rho) - D_{\text{ax},i}(\rho) \frac{\partial c_i}{\partial z}(t,0,\rho) \\ @@ -60,24 +60,32 @@ u c_{\text{in},i}(t) &= u c_i(t,0,\rho) - D_{\text{ax},i}(\rho) \frac{\partial c class MultiChannelConvectionDispersionOperator { public: - MultiChannelConvectionDispersionOperator(); ~MultiChannelConvectionDispersionOperator() CADET_NOEXCEPT; void setFlowRates(int compartment, const active& in, const active& out) CADET_NOEXCEPT; void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT; - bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, unsigned int nCol, unsigned int nRad, bool dynamicReactions); - bool configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, std::unordered_map& parameters); + bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, + unsigned int nComp, unsigned int nCol, unsigned int nRad, bool dynamicReactions); + bool configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, + std::unordered_map& parameters); bool notifyDiscontinuousSectionTransition(double t, unsigned int secIdx); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, bool wantJac, WithoutParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithoutParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, + bool wantJac, WithoutParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, + bool wantJac, WithoutParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, + bool wantJac, WithParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, + bool wantJac, WithParamSensitivity); void prepareADvectors(const AdJacobianParams& adJac) const; - unsigned int numAdDirsForJacobian() const CADET_NOEXCEPT { return _nComp * _nCol * _nChannel; } // todo compressed AD Jacobian (currently AD is dense for MCT) + unsigned int numAdDirsForJacobian() const CADET_NOEXCEPT + { + return _nComp * _nCol * _nChannel; + } // todo compressed AD Jacobian (currently AD is dense for MCT) void extractJacobianFromAD(active const* const adRes, unsigned int adDirOffset); bool solveTimeDerivativeSystem(const SimulationTime& simTime, double* const rhs); @@ -87,23 +95,44 @@ class MultiChannelConvectionDispersionOperator bool solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, double outerTol) const; bool setParameter(const ParameterId& pId, double value); - bool setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, unsigned int adDirection, double adValue); + bool setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, + unsigned int adDirection, double adValue); bool setSensitiveParameterValue(const std::unordered_set& sensParams, const ParameterId& id, double value); - inline const active& columnLength() const CADET_NOEXCEPT { return _colLength; } - inline const active& currentVelocity(int idx) const CADET_NOEXCEPT { return _curVelocity[idx]; } - inline const active& crossSection(int idx) const CADET_NOEXCEPT { return _crossSections[idx]; } - inline active const* crossSections() const CADET_NOEXCEPT { return _crossSections.data(); } - inline bool isCurrentFlowForward(int idx) const CADET_NOEXCEPT { return _curVelocity[idx] >= 0.0; } + inline const active& columnLength() const CADET_NOEXCEPT + { + return _colLength; + } + inline const active& currentVelocity(int idx) const CADET_NOEXCEPT + { + return _curVelocity[idx]; + } + inline const active& crossSection(int idx) const CADET_NOEXCEPT + { + return _crossSections[idx]; + } + inline active const* crossSections() const CADET_NOEXCEPT + { + return _crossSections.data(); + } + inline bool isCurrentFlowForward(int idx) const CADET_NOEXCEPT + { + return _curVelocity[idx] >= 0.0; + } const active& axialDispersion(unsigned int idxSec, int idxRad, int idxComp) const CADET_NOEXCEPT; const active& radialDispersion(unsigned int idxSec, int idxRad, int idxComp) const CADET_NOEXCEPT; double inletFactor(unsigned int idxSec, int idxRad) const CADET_NOEXCEPT; - inline linalg::CompressedSparseMatrix& jacobian() CADET_NOEXCEPT { return _jacC; } - inline const linalg::CompressedSparseMatrix& jacobian() const CADET_NOEXCEPT { return _jacC; } + inline linalg::CompressedSparseMatrix& jacobian() CADET_NOEXCEPT + { + return _jacC; + } + inline const linalg::CompressedSparseMatrix& jacobian() const CADET_NOEXCEPT + { + return _jacC; + } protected: - class LinearSolver; class GmresSolver; template class SparseDirectSolver; @@ -114,7 +143,8 @@ class MultiChannelConvectionDispersionOperator void assembleDiscretizedJacobian(double alpha); template - int residualImpl(const IModel& model, double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res); + int residualImpl(const IModel& model, double t, unsigned int secIdx, StateType const* y, double const* yDot, + ResidualType* res); void setSparsityPattern(); @@ -141,38 +171,39 @@ class MultiChannelConvectionDispersionOperator UserDefined }; - unsigned int _nComp; //!< Number of components - unsigned int _nCol; //!< Number of axial cells - unsigned int _nChannel; //!< Number of channels - bool _hasDynamicReactions; //!< Determines whether the model has dynamic reactions (only relevant for sparsity pattern) + unsigned int _nComp; //!< Number of components + unsigned int _nCol; //!< Number of axial cells + unsigned int _nChannel; //!< Number of channels + bool _hasDynamicReactions; //!< Determines whether the model has dynamic reactions (only relevant for sparsity + //!< pattern) - active _colLength; //!< Column length \f$ L \f$ + active _colLength; //!< Column length \f$ L \f$ std::vector _crossSections; //!< Cross section area of each compartment - std::vector _axialDispersion; //!< Axial dispersion coefficient \f$ D_{\text{ax}} \f$ - MultiplexMode _axialDispersionMode; //!< Multiplex mode of the axial dispersion + std::vector _axialDispersion; //!< Axial dispersion coefficient \f$ D_{\text{ax}} \f$ + MultiplexMode _axialDispersionMode; //!< Multiplex mode of the axial dispersion std::vector _radialDispersion; //!< Radial dispersion coefficient \f$ D_{\rho} \f$ - MultiplexMode _radialDispersionMode; //!< Multiplex mode of the radial dispersion - std::vector _velocity; //!< Interstitial velocity parameter - std::vector _curVelocity; //!< Current interstitial velocity \f$ u \f$ - std::vector _dir; //!< Current flow direction - bool _singleVelocity; //!< Determines whether only one velocity for all compartments is given + MultiplexMode _radialDispersionMode; //!< Multiplex mode of the radial dispersion + std::vector _velocity; //!< Interstitial velocity parameter + std::vector _curVelocity; //!< Current interstitial velocity \f$ u \f$ + std::vector _dir; //!< Current flow direction + bool _singleVelocity; //!< Determines whether only one velocity for all compartments is given - std::vector _exchangeMatrix; //!< Matrix of exchange coeffs for the linear inter-channel transport + std::vector _exchangeMatrix; //!< Matrix of exchange coeffs for the linear inter-channel transport IParameterParameterDependence* _dispersionDep; ArrayPool _stencilMemory; //!< Provides memory for the stencil double* _wenoDerivatives; //!< Holds derivatives of the WENO scheme - Weno _weno; //!< The WENO scheme implementation - double _wenoEpsilon; //!< The @f$ \varepsilon @f$ of the WENO scheme (prevents division by zero) + Weno _weno; //!< The WENO scheme implementation + double _wenoEpsilon; //!< The @f$ \varepsilon @f$ of the WENO scheme (prevents division by zero) linalg::CompressedSparseMatrix _jacC; //!< Jacobian - LinearSolver* _linearSolver; //!< Solves linear system with time discretized Jacobian + LinearSolver* _linearSolver; //!< Solves linear system with time discretized Jacobian }; } // namespace parts } // namespace model } // namespace cadet -#endif // LIBCADET_MULTICHANNELTRANSPORTOPERATOR_HPP_ \ No newline at end of file +#endif // LIBCADET_MULTICHANNELTRANSPORTOPERATOR_HPP_ diff --git a/src/libcadet/model/parts/RadialConvectionDispersionKernel.cpp b/src/libcadet/model/parts/RadialConvectionDispersionKernel.cpp index a6e667d0f..49665778d 100644 --- a/src/libcadet/model/parts/RadialConvectionDispersionKernel.cpp +++ b/src/libcadet/model/parts/RadialConvectionDispersionKernel.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Implements the kernel of the radial convection dispersion transport operator. */ @@ -34,16 +34,22 @@ namespace convdisp namespace impl { - class DummyStencil +class DummyStencil +{ +public: + DummyStencil() + { + } + inline double operator[](const int idx) const { - public: - DummyStencil() { } - inline double operator[](const int idx) const { return 0.0; } - }; + return 0.0; + } +}; } // namespace impl -void sparsityPatternRadial(linalg::SparsityPatternRowIterator itBegin, unsigned int nComp, unsigned int nCol, int strideCell, double u, Weno& weno) +void sparsityPatternRadial(linalg::SparsityPatternRowIterator itBegin, unsigned int nComp, unsigned int nCol, + int strideCell, double u, Weno& weno) { impl::DummyStencil stencil; @@ -87,7 +93,8 @@ void sparsityPatternRadial(linalg::SparsityPatternRowIterator itBegin, unsigned { for (int i = 0; i < 2 * wenoOrder - 1; ++i) // Note that we have an offset of -1 here (compared to the right cell face below), since - // the reconstructed value depends on the previous stencil (which has now been moved by one cell) + // the reconstructed value depends on the previous stencil (which has now been moved by one + // cell) jac.centered((i - wenoOrder) * strideCell); } @@ -144,7 +151,8 @@ void sparsityPatternRadial(linalg::SparsityPatternRowIterator itBegin, unsigned { for (int i = 0; i < 2 * wenoOrder - 1; ++i) // Note that we have an offset of +1 here (compared to the left cell face below), since - // the reconstructed value depends on the previous stencil (which has now been moved by one cell) + // the reconstructed value depends on the previous stencil (which has now been moved by one + // cell) jac.centered((wenoOrder - i) * strideCell); } diff --git a/src/libcadet/model/parts/RadialConvectionDispersionKernel.hpp b/src/libcadet/model/parts/RadialConvectionDispersionKernel.hpp index 14cea1f83..186eed8f1 100644 --- a/src/libcadet/model/parts/RadialConvectionDispersionKernel.hpp +++ b/src/libcadet/model/parts/RadialConvectionDispersionKernel.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Implements the kernel of the radial convection dispersion transport operator. */ @@ -41,367 +41,427 @@ namespace parts namespace convdisp { -template -struct RadialFlowParameters +template struct RadialFlowParameters { T u; active const* d_rad; active const* cellCenters; //!< Midpoints of the cells - active const* cellSizes; //!< Cell sizes - active const* cellBounds; //!< Cell boundaries - ArrayPool* stencilMemory; //!< Provides memory for the stencil + active const* cellSizes; //!< Cell sizes + active const* cellBounds; //!< Cell boundaries + ArrayPool* stencilMemory; //!< Provides memory for the stencil int strideCell; unsigned int nComp; unsigned int nCol; unsigned int offsetToInlet; //!< Offset to the first component of the inlet DOFs in the local state vector - unsigned int offsetToBulk; //!< Offset to the first component of the first bulk cell in the local state vector + unsigned int offsetToBulk; //!< Offset to the first component of the first bulk cell in the local state vector IParameterParameterDependence* parDep; const IModel& model; }; - namespace impl { - template - int residualForwardsRadialFlow(const SimulationTime& simTime, StateType const* y, double const* yDot, ResidualType* res, RowIteratorType jacBegin, const RadialFlowParameters& p) +template +int residualForwardsRadialFlow(const SimulationTime& simTime, StateType const* y, double const* yDot, ResidualType* res, + RowIteratorType jacBegin, const RadialFlowParameters& p) +{ + // The stencil caches parts of the state vector for better spatial coherence + typedef CachingStencil StencilType; + // StencilType stencil(std::max(p.weno->stencilSize(), 3u), *p.stencilMemory, std::max(p.weno->order() - 1, + // 1)); + StencilType stencil(std::max(1u, 3u), *p.stencilMemory, std::max(1 - 1, 1)); + + // The RowIterator is always centered on the main diagonal. + // This means that jac[0] is the main diagonal, jac[-1] is the first lower diagonal, + // and jac[1] is the first upper diagonal. We can also access the rows from left to + // right beginning with the last lower diagonal moving towards the main diagonal and + // continuing to the last upper diagonal by using the native() method. + RowIteratorType jac; + + ResidualType* const resBulk = wantRes ? res + p.offsetToBulk : nullptr; + StateType const* const yBulk = y + p.offsetToBulk; + + for (unsigned int comp = 0; comp < p.nComp; ++comp) { - // The stencil caches parts of the state vector for better spatial coherence - typedef CachingStencil StencilType; -// StencilType stencil(std::max(p.weno->stencilSize(), 3u), *p.stencilMemory, std::max(p.weno->order() - 1, 1)); - StencilType stencil(std::max(1u, 3u), *p.stencilMemory, std::max(1 - 1, 1)); - - // The RowIterator is always centered on the main diagonal. - // This means that jac[0] is the main diagonal, jac[-1] is the first lower diagonal, - // and jac[1] is the first upper diagonal. We can also access the rows from left to - // right beginning with the last lower diagonal moving towards the main diagonal and - // continuing to the last upper diagonal by using the native() method. - RowIteratorType jac; - - ResidualType* const resBulk = wantRes ? res + p.offsetToBulk : nullptr; - StateType const* const yBulk = y + p.offsetToBulk; - - for (unsigned int comp = 0; comp < p.nComp; ++comp) - { - if (wantJac) - jac = jacBegin + comp; + if (wantJac) + jac = jacBegin + comp; - ResidualType* const resBulkComp = wantRes ? resBulk + comp : nullptr; - StateType const* const yBulkComp = yBulk + comp; + ResidualType* const resBulkComp = wantRes ? resBulk + comp : nullptr; + StateType const* const yBulkComp = yBulk + comp; - // Add time derivative to each cell - if (yDot && wantRes) - { - double const* const yDotBulkComp = yDot + p.offsetToBulk + comp; - for (unsigned int col = 0; col < p.nCol; ++col) - resBulkComp[col * p.strideCell] = yDotBulkComp[col * p.strideCell]; - } - else if (wantRes) - { - for (unsigned int col = 0; col < p.nCol; ++col) - resBulkComp[col * p.strideCell] = 0.0; - } - - // Fill stencil (left side with zeros, right side with states) - for (int i = -std::max(1, 2) + 1; i < 0; ++i) - stencil[i] = 0.0; - for (int i = 0; i < std::max(1, 2); ++i) - stencil[i] = yBulkComp[i * p.strideCell]; - - // Reset WENO output - StateType vm(0.0); // reconstructed value -// if (wantJac) -// std::fill(p.wenoDerivatives, p.wenoDerivatives + p.weno->stencilSize(), 0.0); - - int wenoOrder = 1; - const ParamType d_rad = static_cast(p.d_rad[comp]); - - // Iterate over all cells + // Add time derivative to each cell + if (yDot && wantRes) + { + double const* const yDotBulkComp = yDot + p.offsetToBulk + comp; for (unsigned int col = 0; col < p.nCol; ++col) - { - const ParamType denom = static_cast(p.cellCenters[col]) * static_cast(p.cellSizes[col]); - - // ------------------- Dispersion ------------------- + resBulkComp[col * p.strideCell] = yDotBulkComp[col * p.strideCell]; + } + else if (wantRes) + { + for (unsigned int col = 0; col < p.nCol; ++col) + resBulkComp[col * p.strideCell] = 0.0; + } - // Right side, leave out if we're in the last cell (boundary condition) - if (cadet_likely(col < p.nCol - 1)) - { - const double relCoord = (static_cast(p.cellBounds[col+1]) - static_cast(p.cellBounds[0])) / (static_cast(p.cellBounds[p.nCol - 1]) - static_cast(p.cellBounds[0])); - const ParamType d_rad_right = d_rad * p.parDep->getValue(p.model, ColumnPosition{relCoord, 0.0, 0.0}, comp, ParTypeIndep, BoundStateIndep, static_cast(p.u) / static_cast(p.cellBounds[col+1])); - if(wantRes) - resBulkComp[col * p.strideCell] -= d_rad_right * static_cast(p.cellBounds[col+1]) / denom * (stencil[1] - stencil[0]) / (static_cast(p.cellCenters[col+1]) - static_cast(p.cellCenters[col])); - // Jacobian entries - if (wantJac) - { - const double val = static_cast(d_rad_right) * static_cast(p.cellBounds[col+1]) / static_cast(denom) / (static_cast(p.cellCenters[col+1]) - static_cast(p.cellCenters[col])); - jac[0] += val; - jac[p.strideCell] -= val; - } - } + // Fill stencil (left side with zeros, right side with states) + for (int i = -std::max(1, 2) + 1; i < 0; ++i) + stencil[i] = 0.0; + for (int i = 0; i < std::max(1, 2); ++i) + stencil[i] = yBulkComp[i * p.strideCell]; - // Left side, leave out if we're in the first cell (boundary condition) - if (cadet_likely(col > 0)) - { - const double relCoord = (static_cast(p.cellBounds[col]) - static_cast(p.cellBounds[0])) / (static_cast(p.cellBounds[p.nCol - 1]) - static_cast(p.cellBounds[0])); - const ParamType d_rad_left = d_rad * p.parDep->getValue(p.model, ColumnPosition{ relCoord, 0.0, 0.0 }, comp, ParTypeIndep, BoundStateIndep, static_cast(p.u) / static_cast(p.cellBounds[col])); - if(wantRes) - resBulkComp[col * p.strideCell] -= d_rad_left * static_cast(p.cellBounds[col]) / denom * (stencil[-1] - stencil[0]) / (static_cast(p.cellCenters[col]) - static_cast(p.cellCenters[col-1])); - // Jacobian entries - if (wantJac) - { - const double val = static_cast(d_rad_left) * static_cast(p.cellBounds[col]) / static_cast(denom) / (static_cast(p.cellCenters[col]) - static_cast(p.cellCenters[col-1])); - jac[0] += val; - jac[-p.strideCell] -= val; - } - } + // Reset WENO output + StateType vm( + 0.0); // reconstructed value + // if (wantJac) + // std::fill(p.wenoDerivatives, p.wenoDerivatives + p.weno->stencilSize(), 0.0); - // ------------------- Convection ------------------- + int wenoOrder = 1; + const ParamType d_rad = static_cast(p.d_rad[comp]); - // Add convection through this cell's left face - if (cadet_likely(col > 0)) - { - // Remember that vm still contains the reconstructed value of the previous - // cell's *right* face, which is identical to this cell's *left* face! - if(wantRes) - resBulkComp[col * p.strideCell] -= p.u / denom * vm; + // Iterate over all cells + for (unsigned int col = 0; col < p.nCol; ++col) + { + const ParamType denom = + static_cast(p.cellCenters[col]) * static_cast(p.cellSizes[col]); - // Jacobian entries - if (wantJac) - { - for (int i = 0; i < 2 * wenoOrder - 1; ++i) - // Note that we have an offset of -1 here (compared to the right cell face below), since - // the reconstructed value depends on the previous stencil (which has now been moved by one cell) - jac[(i - wenoOrder) * p.strideCell] -= static_cast(p.u) / static_cast(denom); - } - } - else if (wantRes) - { - // In the first cell we need to apply the boundary condition: inflow concentration - resBulkComp[col * p.strideCell] -= p.u / denom * y[p.offsetToInlet + comp]; - } + // ------------------- Dispersion ------------------- - // Reconstruct concentration on this cell's right face + // Right side, leave out if we're in the last cell (boundary condition) + if (cadet_likely(col < p.nCol - 1)) + { + const double relCoord = + (static_cast(p.cellBounds[col + 1]) - static_cast(p.cellBounds[0])) / + (static_cast(p.cellBounds[p.nCol - 1]) - static_cast(p.cellBounds[0])); + const ParamType d_rad_right = + d_rad * + p.parDep->getValue(p.model, ColumnPosition{relCoord, 0.0, 0.0}, comp, ParTypeIndep, BoundStateIndep, + static_cast(p.u) / static_cast(p.cellBounds[col + 1])); + if (wantRes) + resBulkComp[col * p.strideCell] -= + d_rad_right * static_cast(p.cellBounds[col + 1]) / denom * + (stencil[1] - stencil[0]) / + (static_cast(p.cellCenters[col + 1]) - static_cast(p.cellCenters[col])); + // Jacobian entries if (wantJac) { - wenoOrder = 1; - vm = stencil[0]; - // wenoOrder = p.weno->template reconstruct(p.wenoEpsilon, col, p.nCol, stencil, vm, p.wenoDerivatives); - } - else - { - wenoOrder = 1; - vm = stencil[0]; - // wenoOrder = p.weno->template reconstruct(p.wenoEpsilon, col, p.nCol, stencil, vm); + const double val = + static_cast(d_rad_right) * static_cast(p.cellBounds[col + 1]) / + static_cast(denom) / + (static_cast(p.cellCenters[col + 1]) - static_cast(p.cellCenters[col])); + jac[0] += val; + jac[p.strideCell] -= val; } + } - // Right side - if(wantRes) - resBulkComp[col * p.strideCell] += p.u / denom * vm; + // Left side, leave out if we're in the first cell (boundary condition) + if (cadet_likely(col > 0)) + { + const double relCoord = + (static_cast(p.cellBounds[col]) - static_cast(p.cellBounds[0])) / + (static_cast(p.cellBounds[p.nCol - 1]) - static_cast(p.cellBounds[0])); + const ParamType d_rad_left = + d_rad * p.parDep->getValue(p.model, ColumnPosition{relCoord, 0.0, 0.0}, comp, ParTypeIndep, + BoundStateIndep, + static_cast(p.u) / static_cast(p.cellBounds[col])); + if (wantRes) + resBulkComp[col * p.strideCell] -= + d_rad_left * static_cast(p.cellBounds[col]) / denom * (stencil[-1] - stencil[0]) / + (static_cast(p.cellCenters[col]) - static_cast(p.cellCenters[col - 1])); // Jacobian entries if (wantJac) { - for (int i = 0; i < 2 * wenoOrder - 1; ++i) - jac[(i - wenoOrder + 1) * p.strideCell] += static_cast(p.u) / static_cast(denom); + const double val = + static_cast(d_rad_left) * static_cast(p.cellBounds[col]) / + static_cast(denom) / + (static_cast(p.cellCenters[col]) - static_cast(p.cellCenters[col - 1])); + jac[0] += val; + jac[-p.strideCell] -= val; } + } + + // ------------------- Convection ------------------- - // Update stencil - const unsigned int shift = std::max(1, 2); - if (cadet_likely(col + shift < p.nCol)) - stencil.advance(yBulkComp[(col + shift) * p.strideCell]); - else - stencil.advance(0.0); + // Add convection through this cell's left face + if (cadet_likely(col > 0)) + { + // Remember that vm still contains the reconstructed value of the previous + // cell's *right* face, which is identical to this cell's *left* face! + if (wantRes) + resBulkComp[col * p.strideCell] -= p.u / denom * vm; + // Jacobian entries if (wantJac) { - if (cadet_likely(col < p.nCol - 1)) - jac += p.strideCell; + for (int i = 0; i < 2 * wenoOrder - 1; ++i) + // Note that we have an offset of -1 here (compared to the right cell face below), since + // the reconstructed value depends on the previous stencil (which has now been moved by one + // cell) + jac[(i - wenoOrder) * p.strideCell] -= static_cast(p.u) / static_cast(denom); } } - } - - // Film diffusion with flux into beads is added in residualFlux() function - - return 0; - } + else if (wantRes) + { + // In the first cell we need to apply the boundary condition: inflow concentration + resBulkComp[col * p.strideCell] -= p.u / denom * y[p.offsetToInlet + comp]; + } - template - int residualBackwardsRadialFlow(const SimulationTime& simTime, StateType const* y, double const* yDot, ResidualType* res, RowIteratorType jacBegin, const RadialFlowParameters& p) - { - // The stencil caches parts of the state vector for better spatial coherence - typedef CachingStencil StencilType; - // StencilType stencil(std::max(p.weno->stencilSize(), 3u), *p.stencilMemory, std::max(p.weno->order() - 1, 1)); - StencilType stencil(std::max(1u, 3u), *p.stencilMemory, std::max(1 - 1, 1)); - - // The RowIterator is always centered on the main diagonal. - // This means that jac[0] is the main diagonal, jac[-1] is the first lower diagonal, - // and jac[1] is the first upper diagonal. We can also access the rows from left to - // right beginning with the last lower diagonal moving towards the main diagonal and - // continuing to the last upper diagonal by using the native() method. - RowIteratorType jac; - - ResidualType* const resBulk = wantRes ? res + p.offsetToBulk : nullptr; - StateType const* const yBulk = y + p.offsetToBulk; - - for (unsigned int comp = 0; comp < p.nComp; ++comp) - { + // Reconstruct concentration on this cell's right face if (wantJac) - jac = jacBegin + p.strideCell * (p.nCol - 1) + comp; - - ResidualType* const resBulkComp = wantRes ? resBulk + comp : nullptr; - StateType const* const yBulkComp = yBulk + comp; + { + wenoOrder = 1; + vm = stencil[0]; + // wenoOrder = p.weno->template reconstruct(p.wenoEpsilon, col, p.nCol, stencil, + // vm, p.wenoDerivatives); + } + else + { + wenoOrder = 1; + vm = stencil[0]; + // wenoOrder = p.weno->template reconstruct(p.wenoEpsilon, col, p.nCol, stencil, + // vm); + } - // Add time derivative to each cell - if (yDot && wantRes) + // Right side + if (wantRes) + resBulkComp[col * p.strideCell] += p.u / denom * vm; + // Jacobian entries + if (wantJac) { - double const* const yDotBulkComp = yDot + p.offsetToBulk + comp; - for (unsigned int col = 0; col < p.nCol; ++col) - resBulkComp[col * p.strideCell] = yDotBulkComp[col * p.strideCell]; + for (int i = 0; i < 2 * wenoOrder - 1; ++i) + jac[(i - wenoOrder + 1) * p.strideCell] += static_cast(p.u) / static_cast(denom); } - else if (wantRes) + + // Update stencil + const unsigned int shift = std::max(1, 2); + if (cadet_likely(col + shift < p.nCol)) + stencil.advance(yBulkComp[(col + shift) * p.strideCell]); + else + stencil.advance(0.0); + + if (wantJac) { - for (unsigned int col = 0; col < p.nCol; ++col) - resBulkComp[col * p.strideCell] = 0.0; + if (cadet_likely(col < p.nCol - 1)) + jac += p.strideCell; } + } + } - // Fill stencil (left side with zeros, right side with states) - for (int i = -std::max(1, 2) + 1; i < 0; ++i) - stencil[i] = 0.0; - for (int i = 0; i < std::max(1, 2); ++i) - stencil[i] = yBulkComp[(p.nCol - static_cast(i) - 1) * p.strideCell]; + // Film diffusion with flux into beads is added in residualFlux() function - // Reset WENO output - StateType vm(0.0); // reconstructed value - // if (wantJac) - // std::fill(p.wenoDerivatives, p.wenoDerivatives + p.weno->stencilSize(), 0.0); + return 0; +} - int wenoOrder = 1; - const ParamType d_rad = static_cast(p.d_rad[comp]); +template +int residualBackwardsRadialFlow(const SimulationTime& simTime, StateType const* y, double const* yDot, + ResidualType* res, RowIteratorType jacBegin, const RadialFlowParameters& p) +{ + // The stencil caches parts of the state vector for better spatial coherence + typedef CachingStencil StencilType; + // StencilType stencil(std::max(p.weno->stencilSize(), 3u), *p.stencilMemory, std::max(p.weno->order() - 1, + // 1)); + StencilType stencil(std::max(1u, 3u), *p.stencilMemory, std::max(1 - 1, 1)); + + // The RowIterator is always centered on the main diagonal. + // This means that jac[0] is the main diagonal, jac[-1] is the first lower diagonal, + // and jac[1] is the first upper diagonal. We can also access the rows from left to + // right beginning with the last lower diagonal moving towards the main diagonal and + // continuing to the last upper diagonal by using the native() method. + RowIteratorType jac; + + ResidualType* const resBulk = wantRes ? res + p.offsetToBulk : nullptr; + StateType const* const yBulk = y + p.offsetToBulk; + + for (unsigned int comp = 0; comp < p.nComp; ++comp) + { + if (wantJac) + jac = jacBegin + p.strideCell * (p.nCol - 1) + comp; - // Iterate over all cells (backwards) - // Note that col wraps around to unsigned int's maximum value after 0 - for (unsigned int col = p.nCol - 1; col < p.nCol; --col) - { - const ParamType denom = static_cast(p.cellCenters[col]) * static_cast(p.cellSizes[col]); + ResidualType* const resBulkComp = wantRes ? resBulk + comp : nullptr; + StateType const* const yBulkComp = yBulk + comp; - // ------------------- Dispersion ------------------- + // Add time derivative to each cell + if (yDot && wantRes) + { + double const* const yDotBulkComp = yDot + p.offsetToBulk + comp; + for (unsigned int col = 0; col < p.nCol; ++col) + resBulkComp[col * p.strideCell] = yDotBulkComp[col * p.strideCell]; + } + else if (wantRes) + { + for (unsigned int col = 0; col < p.nCol; ++col) + resBulkComp[col * p.strideCell] = 0.0; + } - // Right side, leave out if we're in the first cell (boundary condition) - if (cadet_likely(col < p.nCol - 1)) - { - const double relCoord = (static_cast(p.cellBounds[col + 1]) - static_cast(p.cellBounds[0])) / (static_cast(p.cellBounds[p.nCol - 1]) - static_cast(p.cellBounds[0])); - const ParamType d_rad_right = d_rad * p.parDep->getValue(p.model, ColumnPosition{ relCoord, 0.0, 0.0 }, comp, ParTypeIndep, BoundStateIndep, static_cast(p.u) / static_cast(p.cellBounds[col + 1])); - if (wantRes) - resBulkComp[col * p.strideCell] -= d_rad_right * static_cast(p.cellBounds[col + 1]) / denom * (stencil[-1] - stencil[0]) / (static_cast(p.cellCenters[col + 1]) - static_cast(p.cellCenters[col])); - // Jacobian entries - if (wantJac) - { - const double val = static_cast(d_rad_right) * static_cast(p.cellBounds[col + 1]) / static_cast(denom) / (static_cast(p.cellCenters[col + 1]) - static_cast(p.cellCenters[col])); - jac[0] += val; - jac[p.strideCell] -= val; - } - } + // Fill stencil (left side with zeros, right side with states) + for (int i = -std::max(1, 2) + 1; i < 0; ++i) + stencil[i] = 0.0; + for (int i = 0; i < std::max(1, 2); ++i) + stencil[i] = yBulkComp[(p.nCol - static_cast(i) - 1) * p.strideCell]; - // Left side, leave out if we're in the last cell (boundary condition) - if (cadet_likely(col > 0)) - { - const double relCoord = (static_cast(p.cellBounds[col]) - static_cast(p.cellBounds[0])) / (static_cast(p.cellBounds[p.nCol - 1]) - static_cast(p.cellBounds[0])); - const ParamType d_rad_left = d_rad * p.parDep->getValue(p.model, ColumnPosition{ relCoord, 0.0, 0.0 }, comp, ParTypeIndep, BoundStateIndep, static_cast(p.u) / static_cast(p.cellBounds[col])); - if (wantRes) - resBulkComp[col * p.strideCell] -= d_rad_left * static_cast(p.cellBounds[col]) / denom * (stencil[1] - stencil[0]) / (static_cast(p.cellCenters[col - 1]) - static_cast(p.cellCenters[col])); - // Jacobian entries - if (wantJac) - { - const double val = static_cast(d_rad_left) * static_cast(p.cellBounds[col]) / static_cast(denom) / (static_cast(p.cellCenters[col - 1]) - static_cast(p.cellCenters[col])); - jac[0] += val; - jac[-p.strideCell] -= val; - } - } + // Reset WENO output + StateType vm(0.0); // reconstructed value + // if (wantJac) + // std::fill(p.wenoDerivatives, p.wenoDerivatives + p.weno->stencilSize(), 0.0); - // ------------------- Convection ------------------- + int wenoOrder = 1; + const ParamType d_rad = static_cast(p.d_rad[comp]); - // Add convection through this cell's right face - if (cadet_likely(col < p.nCol - 1)) - { - // Remember that vm still contains the reconstructed value of the previous - // cell's *left* face, which is identical to this cell's *right* face! - if (wantRes) - resBulkComp[col * p.strideCell] += p.u / denom * vm; - - // Jacobian entries - if (wantJac) - { - for (int i = 0; i < 2 * wenoOrder - 1; ++i) - // Note that we have an offset of +1 here (compared to the left cell face below), since - // the reconstructed value depends on the previous stencil (which has now been moved by one cell) - jac[(wenoOrder - i) * p.strideCell] += static_cast(p.u) / static_cast(denom); - } - } - else if (wantRes) - { - // In the last cell (z = L) we need to apply the boundary condition: inflow concentration - resBulkComp[col * p.strideCell] += p.u / denom * y[p.offsetToInlet + comp]; - } + // Iterate over all cells (backwards) + // Note that col wraps around to unsigned int's maximum value after 0 + for (unsigned int col = p.nCol - 1; col < p.nCol; --col) + { + const ParamType denom = + static_cast(p.cellCenters[col]) * static_cast(p.cellSizes[col]); - // Reconstruct concentration on this cell's left face + // ------------------- Dispersion ------------------- + + // Right side, leave out if we're in the first cell (boundary condition) + if (cadet_likely(col < p.nCol - 1)) + { + const double relCoord = + (static_cast(p.cellBounds[col + 1]) - static_cast(p.cellBounds[0])) / + (static_cast(p.cellBounds[p.nCol - 1]) - static_cast(p.cellBounds[0])); + const ParamType d_rad_right = + d_rad * + p.parDep->getValue(p.model, ColumnPosition{relCoord, 0.0, 0.0}, comp, ParTypeIndep, BoundStateIndep, + static_cast(p.u) / static_cast(p.cellBounds[col + 1])); + if (wantRes) + resBulkComp[col * p.strideCell] -= + d_rad_right * static_cast(p.cellBounds[col + 1]) / denom * + (stencil[-1] - stencil[0]) / + (static_cast(p.cellCenters[col + 1]) - static_cast(p.cellCenters[col])); + // Jacobian entries if (wantJac) { - wenoOrder = 1; - vm = stencil[0]; - // wenoOrder = p.weno->template reconstruct(p.wenoEpsilon, col, p.nCol, stencil, vm, p.wenoDerivatives); - } - else - { - wenoOrder = 1; - vm = stencil[0]; - // wenoOrder = p.weno->template reconstruct(p.wenoEpsilon, col, p.nCol, stencil, vm); + const double val = + static_cast(d_rad_right) * static_cast(p.cellBounds[col + 1]) / + static_cast(denom) / + (static_cast(p.cellCenters[col + 1]) - static_cast(p.cellCenters[col])); + jac[0] += val; + jac[p.strideCell] -= val; } + } - // Left face + // Left side, leave out if we're in the last cell (boundary condition) + if (cadet_likely(col > 0)) + { + const double relCoord = + (static_cast(p.cellBounds[col]) - static_cast(p.cellBounds[0])) / + (static_cast(p.cellBounds[p.nCol - 1]) - static_cast(p.cellBounds[0])); + const ParamType d_rad_left = + d_rad * p.parDep->getValue(p.model, ColumnPosition{relCoord, 0.0, 0.0}, comp, ParTypeIndep, + BoundStateIndep, + static_cast(p.u) / static_cast(p.cellBounds[col])); if (wantRes) - resBulkComp[col * p.strideCell] -= p.u / denom * vm; + resBulkComp[col * p.strideCell] -= + d_rad_left * static_cast(p.cellBounds[col]) / denom * (stencil[1] - stencil[0]) / + (static_cast(p.cellCenters[col - 1]) - static_cast(p.cellCenters[col])); // Jacobian entries if (wantJac) { - for (int i = 0; i < 2 * wenoOrder - 1; ++i) - jac[(wenoOrder - i - 1) * p.strideCell] -= static_cast(p.u) / static_cast(denom); + const double val = + static_cast(d_rad_left) * static_cast(p.cellBounds[col]) / + static_cast(denom) / + (static_cast(p.cellCenters[col - 1]) - static_cast(p.cellCenters[col])); + jac[0] += val; + jac[-p.strideCell] -= val; } + } - // Update stencil (be careful because of wrap-around, might cause reading memory very far away [although never used]) - const unsigned int shift = std::max(1, 2); - if (cadet_likely(col - shift < p.nCol)) - stencil.advance(yBulkComp[(col - shift) * p.strideCell]); - else - stencil.advance(0.0); + // ------------------- Convection ------------------- + // Add convection through this cell's right face + if (cadet_likely(col < p.nCol - 1)) + { + // Remember that vm still contains the reconstructed value of the previous + // cell's *left* face, which is identical to this cell's *right* face! + if (wantRes) + resBulkComp[col * p.strideCell] += p.u / denom * vm; + + // Jacobian entries if (wantJac) { - if (cadet_likely(col > 0)) - jac -= p.strideCell; + for (int i = 0; i < 2 * wenoOrder - 1; ++i) + // Note that we have an offset of +1 here (compared to the left cell face below), since + // the reconstructed value depends on the previous stencil (which has now been moved by one + // cell) + jac[(wenoOrder - i) * p.strideCell] += static_cast(p.u) / static_cast(denom); } } - } + else if (wantRes) + { + // In the last cell (z = L) we need to apply the boundary condition: inflow concentration + resBulkComp[col * p.strideCell] += p.u / denom * y[p.offsetToInlet + comp]; + } - // Film diffusion with flux into beads is added in residualFlux() function + // Reconstruct concentration on this cell's left face + if (wantJac) + { + wenoOrder = 1; + vm = stencil[0]; + // wenoOrder = p.weno->template reconstruct(p.wenoEpsilon, col, + // p.nCol, stencil, vm, p.wenoDerivatives); + } + else + { + wenoOrder = 1; + vm = stencil[0]; + // wenoOrder = p.weno->template reconstruct(p.wenoEpsilon, col, + // p.nCol, stencil, vm); + } + + // Left face + if (wantRes) + resBulkComp[col * p.strideCell] -= p.u / denom * vm; + // Jacobian entries + if (wantJac) + { + for (int i = 0; i < 2 * wenoOrder - 1; ++i) + jac[(wenoOrder - i - 1) * p.strideCell] -= static_cast(p.u) / static_cast(denom); + } - return 0; + // Update stencil (be careful because of wrap-around, might cause reading memory very far away [although + // never used]) + const unsigned int shift = std::max(1, 2); + if (cadet_likely(col - shift < p.nCol)) + stencil.advance(yBulkComp[(col - shift) * p.strideCell]); + else + stencil.advance(0.0); + + if (wantJac) + { + if (cadet_likely(col > 0)) + jac -= p.strideCell; + } + } } -} // namespace impl + // Film diffusion with flux into beads is added in residualFlux() function + + return 0; +} +} // namespace impl -template -int residualKernelRadial(const SimulationTime& simTime, StateType const* y, double const* yDot, ResidualType* res, RowIteratorType jacBegin, const RadialFlowParameters& p) +template +int residualKernelRadial(const SimulationTime& simTime, StateType const* y, double const* yDot, ResidualType* res, + RowIteratorType jacBegin, const RadialFlowParameters& p) { if (p.u >= 0.0) - return impl::residualForwardsRadialFlow(simTime, y, yDot, res, jacBegin, p); + return impl::residualForwardsRadialFlow( + simTime, y, yDot, res, jacBegin, p); else - return impl::residualBackwardsRadialFlow(simTime, y, yDot, res, jacBegin, p); + return impl::residualBackwardsRadialFlow( + simTime, y, yDot, res, jacBegin, p); } -void sparsityPatternRadial(linalg::SparsityPatternRowIterator itBegin, unsigned int nComp, unsigned int nCol, int strideCell, double u, Weno& weno); +void sparsityPatternRadial(linalg::SparsityPatternRowIterator itBegin, unsigned int nComp, unsigned int nCol, + int strideCell, double u, Weno& weno); } // namespace convdisp } // namespace parts } // namespace model } // namespace cadet -#endif // LIBCADET_RADIALCONVECTIONDISPERSIONKERNEL_HPP_ +#endif // LIBCADET_RADIALCONVECTIONDISPERSIONKERNEL_HPP_ diff --git a/src/libcadet/model/parts/TwoDimensionalConvectionDispersionOperator.cpp b/src/libcadet/model/parts/TwoDimensionalConvectionDispersionOperator.cpp index 613b958b6..8e95d5fb2 100644 --- a/src/libcadet/model/parts/TwoDimensionalConvectionDispersionOperator.cpp +++ b/src/libcadet/model/parts/TwoDimensionalConvectionDispersionOperator.cpp @@ -23,12 +23,11 @@ #include "model/ParameterDependence.hpp" #include "ConfigurationHelper.hpp" - #ifdef SUPERLU_FOUND - #include "linalg/SuperLUSparseMatrix.hpp" +#include "linalg/SuperLUSparseMatrix.hpp" #endif #ifdef UMFPACK_FOUND - #include "linalg/UMFPackSparseMatrix.hpp" +#include "linalg/UMFPackSparseMatrix.hpp" #endif #include "linalg/BandMatrix.hpp" @@ -42,7 +41,10 @@ namespace { -cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvider& paramProvider, std::unordered_map& parameters, std::vector& values, const std::string& name, unsigned int nComp, unsigned int nRad, cadet::UnitOpIdx uoi) +cadet::model::MultiplexMode readAndRegisterMultiplexParam( + cadet::IParameterProvider& paramProvider, std::unordered_map& parameters, + std::vector& values, const std::string& name, unsigned int nComp, unsigned int nRad, + cadet::UnitOpIdx uoi) { cadet::model::MultiplexMode mode = cadet::model::MultiplexMode::Independent; readParameterMatrix(values, paramProvider, name, nComp * nRad, 1); @@ -54,25 +56,30 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi { mode = cadet::model::MultiplexMode::Independent; if (values.size() > 1) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be 1)"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be 1)"); } else if (modeConfig == 1) { mode = cadet::model::MultiplexMode::Radial; if (values.size() != nRad) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nRad) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be " + std::to_string(nRad) + ")"); } else if (modeConfig == 2) { mode = cadet::model::MultiplexMode::Component; if (values.size() != nComp) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nComp) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be " + std::to_string(nComp) + ")"); } else if (modeConfig == 3) { mode = cadet::model::MultiplexMode::ComponentRadial; if (values.size() != nComp * nRad) - throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + name + "_MULTIPLEX (should be " + std::to_string(nComp * nRad) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + " inconsistent with " + + name + "_MULTIPLEX (should be " + std::to_string(nComp * nRad) + + ")"); } else if (modeConfig == 4) { @@ -83,7 +90,9 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi { mode = cadet::model::MultiplexMode::RadialSection; if (values.size() % nRad != 0) - throw cadet::InvalidParameterException("Number of elements in field " + name + " is not a positive multiple of NRAD (" + std::to_string(nRad) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + + " is not a positive multiple of NRAD (" + std::to_string(nRad) + + ")"); nSec = values.size() / nRad; } @@ -91,7 +100,9 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi { mode = cadet::model::MultiplexMode::ComponentSection; if (values.size() % nComp != 0) - throw cadet::InvalidParameterException("Number of elements in field " + name + " is not a positive multiple of NCOMP (" + std::to_string(nComp) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + + " is not a positive multiple of NCOMP (" + + std::to_string(nComp) + ")"); nSec = values.size() / nComp; } @@ -99,7 +110,9 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi { mode = cadet::model::MultiplexMode::ComponentRadialSection; if (values.size() % (nComp * nRad) != 0) - throw cadet::InvalidParameterException("Number of elements in field " + name + " is not a positive multiple of NCOMP * NRAD (" + std::to_string(nComp * nRad) + ")"); + throw cadet::InvalidParameterException("Number of elements in field " + name + + " is not a positive multiple of NCOMP * NRAD (" + + std::to_string(nComp * nRad) + ")"); nSec = values.size() / (nComp * nRad); } @@ -130,7 +143,8 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi nSec = values.size() / (nComp * nRad); } else - throw cadet::InvalidParameterException("Could not infer multiplex mode of field " + name + ", set " + name + "_MULTIPLEX or change number of elements"); + throw cadet::InvalidParameterException("Could not infer multiplex mode of field " + name + ", set " + name + + "_MULTIPLEX or change number of elements"); // Do not infer cadet::model::MultiplexMode::Section in case of no matches (might hide specification errors) } @@ -138,321 +152,339 @@ cadet::model::MultiplexMode readAndRegisterMultiplexParam(cadet::IParameterProvi const cadet::StringHash nameHash = cadet::hashStringRuntime(name); switch (mode) { - case cadet::model::MultiplexMode::Independent: - case cadet::model::MultiplexMode::Section: - { - std::vector p(nComp * nRad * nSec); - for (unsigned int s = 0; s < nSec; ++s) - std::fill(p.begin() + s * nRad * nComp, p.begin() + (s+1) * nRad * nComp, values[s]); - - values = std::move(p); - - for (unsigned int s = 0; s < nSec; ++s) - parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, (mode == cadet::model::MultiplexMode::Independent) ? cadet::SectionIndep : s)] = &values[s * nRad * nComp]; - } - break; - case cadet::model::MultiplexMode::Component: - case cadet::model::MultiplexMode::ComponentSection: - { - std::vector p(nComp * nRad * nSec); - for (unsigned int s = 0; s < nSec; ++s) - { - for (unsigned int i = 0; i < nComp; ++i) - std::copy(values.begin() + s * nComp, values.begin() + (s+1) * nComp, p.begin() + i * nComp + s * nComp * nRad); - } + case cadet::model::MultiplexMode::Independent: + case cadet::model::MultiplexMode::Section: { + std::vector p(nComp * nRad * nSec); + for (unsigned int s = 0; s < nSec; ++s) + std::fill(p.begin() + s * nRad * nComp, p.begin() + (s + 1) * nRad * nComp, values[s]); + + values = std::move(p); + + for (unsigned int s = 0; s < nSec; ++s) + parameters[cadet::makeParamId( + nameHash, uoi, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, + (mode == cadet::model::MultiplexMode::Independent) ? cadet::SectionIndep : s)] = + &values[s * nRad * nComp]; + } + break; + case cadet::model::MultiplexMode::Component: + case cadet::model::MultiplexMode::ComponentSection: { + std::vector p(nComp * nRad * nSec); + for (unsigned int s = 0; s < nSec; ++s) + { + for (unsigned int i = 0; i < nComp; ++i) + std::copy(values.begin() + s * nComp, values.begin() + (s + 1) * nComp, + p.begin() + i * nComp + s * nComp * nRad); + } - values = std::move(p); + values = std::move(p); - for (unsigned int s = 0; s < nSec; ++s) - { - for (unsigned int i = 0; i < nComp; ++i) - parameters[cadet::makeParamId(nameHash, uoi, i, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, (mode == cadet::model::MultiplexMode::Component) ? cadet::SectionIndep : s)] = &values[s * nRad * nComp + i]; - } - } - break; - case cadet::model::MultiplexMode::Radial: - case cadet::model::MultiplexMode::RadialSection: - { - std::vector p(nComp * nRad * nSec); - for (unsigned int i = 0; i < nRad * nSec; ++i) - std::fill(p.begin() + i * nComp, p.begin() + (i+1) * nComp, values[i]); + for (unsigned int s = 0; s < nSec; ++s) + { + for (unsigned int i = 0; i < nComp; ++i) + parameters[cadet::makeParamId( + nameHash, uoi, i, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, + (mode == cadet::model::MultiplexMode::Component) ? cadet::SectionIndep : s)] = + &values[s * nRad * nComp + i]; + } + } + break; + case cadet::model::MultiplexMode::Radial: + case cadet::model::MultiplexMode::RadialSection: { + std::vector p(nComp * nRad * nSec); + for (unsigned int i = 0; i < nRad * nSec; ++i) + std::fill(p.begin() + i * nComp, p.begin() + (i + 1) * nComp, values[i]); - values = std::move(p); + values = std::move(p); - for (unsigned int s = 0; s < nSec; ++s) - { - for (unsigned int i = 0; i < nRad; ++i) - parameters[cadet::makeParamId(nameHash, uoi, cadet::CompIndep, i, cadet::BoundStateIndep, cadet::ReactionIndep, (mode == cadet::model::MultiplexMode::Radial) ? cadet::SectionIndep : s)] = &values[s * nRad * nComp + i * nComp]; - } - } - break; - case cadet::model::MultiplexMode::ComponentRadial: - case cadet::model::MultiplexMode::ComponentRadialSection: - cadet::registerParam3DArray(parameters, values, [=](bool multi, unsigned int sec, unsigned int compartment, unsigned int comp) { return cadet::makeParamId(nameHash, uoi, comp, compartment, cadet::BoundStateIndep, cadet::ReactionIndep, multi ? sec : cadet::SectionIndep); }, nComp, nRad); - break; - case cadet::model::MultiplexMode::Axial: - case cadet::model::MultiplexMode::AxialRadial: - case cadet::model::MultiplexMode::Type: - case cadet::model::MultiplexMode::ComponentType: - case cadet::model::MultiplexMode::ComponentSectionType: - cadet_assert(false); - break; + for (unsigned int s = 0; s < nSec; ++s) + { + for (unsigned int i = 0; i < nRad; ++i) + parameters[cadet::makeParamId( + nameHash, uoi, cadet::CompIndep, i, cadet::BoundStateIndep, cadet::ReactionIndep, + (mode == cadet::model::MultiplexMode::Radial) ? cadet::SectionIndep : s)] = + &values[s * nRad * nComp + i * nComp]; + } + } + break; + case cadet::model::MultiplexMode::ComponentRadial: + case cadet::model::MultiplexMode::ComponentRadialSection: + cadet::registerParam3DArray( + parameters, values, + [=](bool multi, unsigned int sec, unsigned int compartment, unsigned int comp) { + return cadet::makeParamId(nameHash, uoi, comp, compartment, cadet::BoundStateIndep, + cadet::ReactionIndep, multi ? sec : cadet::SectionIndep); + }, + nComp, nRad); + break; + case cadet::model::MultiplexMode::Axial: + case cadet::model::MultiplexMode::AxialRadial: + case cadet::model::MultiplexMode::Type: + case cadet::model::MultiplexMode::ComponentType: + case cadet::model::MultiplexMode::ComponentSectionType: + cadet_assert(false); + break; } return mode; } -bool multiplexParameterValue(const cadet::ParameterId& pId, cadet::StringHash nameHash, cadet::model::MultiplexMode mode, std::vector& data, unsigned int nComp, unsigned int nRad, double value, std::unordered_set const* sensParams) +bool multiplexParameterValue(const cadet::ParameterId& pId, cadet::StringHash nameHash, + cadet::model::MultiplexMode mode, std::vector& data, unsigned int nComp, + unsigned int nRad, double value, std::unordered_set const* sensParams) { if (pId.name != nameHash) return false; switch (mode) { - case cadet::model::MultiplexMode::Independent: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + case cadet::model::MultiplexMode::Independent: { + if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[0])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[0])) + return false; - for (std::size_t i = 0; i < data.size(); ++i) - data[i].setValue(value); + for (std::size_t i = 0; i < data.size(); ++i) + data[i].setValue(value); - return true; - } - case cadet::model::MultiplexMode::Section: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Section: { + if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.section * nComp * nRad])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.section * nComp * nRad])) + return false; - for (unsigned int i = 0; i < nComp * nRad; ++i) - data[i + pId.section * nComp * nRad].setValue(value); + for (unsigned int i = 0; i < nComp * nRad; ++i) + data[i + pId.section * nComp * nRad].setValue(value); - return true; - } - case cadet::model::MultiplexMode::Component: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Component: { + if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.component])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.component])) + return false; - for (unsigned int i = 0; i < nRad; ++i) - data[i * nComp + pId.component].setValue(value); + for (unsigned int i = 0; i < nRad; ++i) + data[i * nComp + pId.component].setValue(value); - return true; - } - case cadet::model::MultiplexMode::ComponentSection: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::ComponentSection: { + if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.component + pId.section * nComp * nRad])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.component + pId.section * nComp * nRad])) + return false; - for (unsigned int i = 0; i < nRad; ++i) - data[i * nComp + pId.component + pId.section * nComp * nRad].setValue(value); + for (unsigned int i = 0; i < nRad; ++i) + data[i * nComp + pId.component + pId.section * nComp * nRad].setValue(value); - return true; - } - case cadet::model::MultiplexMode::Radial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Radial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.particleType * nComp])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.particleType * nComp])) + return false; - for (unsigned int i = 0; i < nComp; ++i) - data[i + pId.particleType * nComp].setValue(value); + for (unsigned int i = 0; i < nComp; ++i) + data[i + pId.particleType * nComp].setValue(value); - return true; - } - case cadet::model::MultiplexMode::RadialSection: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::RadialSection: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.particleType * nComp + pId.section * nComp * nRad])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.particleType * nComp + pId.section * nComp * nRad])) + return false; - for (unsigned int i = 0; i < nComp; ++i) - data[i + pId.particleType * nComp + pId.section * nComp * nRad].setValue(value); + for (unsigned int i = 0; i < nComp; ++i) + data[i + pId.particleType * nComp + pId.section * nComp * nRad].setValue(value); - return true; - } - case cadet::model::MultiplexMode::ComponentRadial: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::ComponentRadial: { + if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.component + pId.particleType * nComp])) - return false; + if (sensParams && !cadet::contains(*sensParams, &data[pId.component + pId.particleType * nComp])) + return false; - data[pId.component + pId.particleType * nComp].setValue(value); + data[pId.component + pId.particleType * nComp].setValue(value); - return true; - } - case cadet::model::MultiplexMode::ComponentRadialSection: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::ComponentRadialSection: { + if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - if (sensParams && !cadet::contains(*sensParams, &data[pId.component + pId.particleType * nComp + pId.section * nComp * nRad])) - return false; + if (sensParams && + !cadet::contains(*sensParams, &data[pId.component + pId.particleType * nComp + pId.section * nComp * nRad])) + return false; - data[pId.component + pId.particleType * nComp + pId.section * nComp * nRad].setValue(value); + data[pId.component + pId.particleType * nComp + pId.section * nComp * nRad].setValue(value); - return true; - } - case cadet::model::MultiplexMode::Axial: - case cadet::model::MultiplexMode::AxialRadial: - case cadet::model::MultiplexMode::Type: - case cadet::model::MultiplexMode::ComponentType: - case cadet::model::MultiplexMode::ComponentSectionType: - cadet_assert(false); - break; + return true; + } + case cadet::model::MultiplexMode::Axial: + case cadet::model::MultiplexMode::AxialRadial: + case cadet::model::MultiplexMode::Type: + case cadet::model::MultiplexMode::ComponentType: + case cadet::model::MultiplexMode::ComponentSectionType: + cadet_assert(false); + break; } return false; } -bool multiplexParameterAD(const cadet::ParameterId& pId, cadet::StringHash nameHash, cadet::model::MultiplexMode mode, std::vector& data, unsigned int nComp, unsigned int nRad, unsigned int adDirection, double adValue, std::unordered_set& sensParams) +bool multiplexParameterAD(const cadet::ParameterId& pId, cadet::StringHash nameHash, cadet::model::MultiplexMode mode, + std::vector& data, unsigned int nComp, unsigned int nRad, + unsigned int adDirection, double adValue, std::unordered_set& sensParams) { if (pId.name != nameHash) return false; switch (mode) { - case cadet::model::MultiplexMode::Independent: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + case cadet::model::MultiplexMode::Independent: { + if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - sensParams.insert(&data[0]); + sensParams.insert(&data[0]); - for (std::size_t i = 0; i < data.size(); ++i) - data[i].setADValue(adDirection, adValue); + for (std::size_t i = 0; i < data.size(); ++i) + data[i].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::Section: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Section: { + if ((pId.component != cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.section * nComp * nRad]); + sensParams.insert(&data[pId.section * nComp * nRad]); - for (unsigned int i = 0; i < nComp * nRad; ++i) - data[i + pId.section * nComp * nRad].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nComp * nRad; ++i) + data[i + pId.section * nComp * nRad].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::Component: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Component: { + if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.component]); + sensParams.insert(&data[pId.component]); - for (unsigned int i = 0; i < nRad; ++i) - data[i * nComp + pId.component].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nRad; ++i) + data[i * nComp + pId.component].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::ComponentSection: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::ComponentSection: { + if ((pId.component == cadet::CompIndep) || (pId.particleType != cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.component + pId.section * nComp * nRad]); + sensParams.insert(&data[pId.component + pId.section * nComp * nRad]); - for (unsigned int i = 0; i < nRad; ++i) - data[i * nComp + pId.component + pId.section * nComp * nRad].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nRad; ++i) + data[i * nComp + pId.component + pId.section * nComp * nRad].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::Radial: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::Radial: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.particleType * nComp]); + sensParams.insert(&data[pId.particleType * nComp]); - for (unsigned int i = 0; i < nComp; ++i) - data[i + pId.particleType * nComp].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nComp; ++i) + data[i + pId.particleType * nComp].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::RadialSection: - { - if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::RadialSection: { + if ((pId.component != cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.particleType * nComp + pId.section * nComp * nRad]); + sensParams.insert(&data[pId.particleType * nComp + pId.section * nComp * nRad]); - for (unsigned int i = 0; i < nComp; ++i) - data[i + pId.particleType * nComp + pId.section * nComp * nRad].setADValue(adDirection, adValue); + for (unsigned int i = 0; i < nComp; ++i) + data[i + pId.particleType * nComp + pId.section * nComp * nRad].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::ComponentRadial: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section != cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::ComponentRadial: { + if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section != cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.component + pId.particleType * nComp]); + sensParams.insert(&data[pId.component + pId.particleType * nComp]); - data[pId.component + pId.particleType * nComp].setADValue(adDirection, adValue); + data[pId.component + pId.particleType * nComp].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::ComponentRadialSection: - { - if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || (pId.boundState != cadet::BoundStateIndep) - || (pId.reaction != cadet::ReactionIndep) || (pId.section == cadet::SectionIndep)) - return false; + return true; + } + case cadet::model::MultiplexMode::ComponentRadialSection: { + if ((pId.component == cadet::CompIndep) || (pId.particleType == cadet::ParTypeIndep) || + (pId.boundState != cadet::BoundStateIndep) || (pId.reaction != cadet::ReactionIndep) || + (pId.section == cadet::SectionIndep)) + return false; - sensParams.insert(&data[pId.component + pId.particleType * nComp + pId.section * nComp * nRad]); + sensParams.insert(&data[pId.component + pId.particleType * nComp + pId.section * nComp * nRad]); - data[pId.component + pId.particleType * nComp + pId.section * nComp * nRad].setADValue(adDirection, adValue); + data[pId.component + pId.particleType * nComp + pId.section * nComp * nRad].setADValue(adDirection, adValue); - return true; - } - case cadet::model::MultiplexMode::Axial: - case cadet::model::MultiplexMode::AxialRadial: - case cadet::model::MultiplexMode::Type: - case cadet::model::MultiplexMode::ComponentType: - case cadet::model::MultiplexMode::ComponentSectionType: - cadet_assert(false); - break; + return true; + } + case cadet::model::MultiplexMode::Axial: + case cadet::model::MultiplexMode::AxialRadial: + case cadet::model::MultiplexMode::Type: + case cadet::model::MultiplexMode::ComponentType: + case cadet::model::MultiplexMode::ComponentSectionType: + cadet_assert(false); + break; } return false; } -} // namespace +} // namespace namespace cadet { @@ -466,26 +498,34 @@ namespace parts class TwoDimensionalConvectionDispersionOperator::LinearSolver { public: + virtual ~LinearSolver() CADET_NOEXCEPT + { + } - virtual ~LinearSolver() CADET_NOEXCEPT { } - - virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, unsigned int nRad, const Weno& weno) = 0; + virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, unsigned int nRad, + const Weno& weno) = 0; virtual void setSparsityPattern(const cadet::linalg::SparsityPattern& pattern) = 0; virtual void assembleDiscretizedJacobian(double alpha) = 0; virtual bool factorize() = 0; - virtual bool solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, double outerTol) const = 0; + virtual bool solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, + double outerTol) const = 0; }; int schurComplementMultiplier2DCDO(void* userData, double const* x, double* z); -class TwoDimensionalConvectionDispersionOperator::GmresSolver : public TwoDimensionalConvectionDispersionOperator::LinearSolver +class TwoDimensionalConvectionDispersionOperator::GmresSolver + : public TwoDimensionalConvectionDispersionOperator::LinearSolver { public: + GmresSolver(linalg::CompressedSparseMatrix const* jacC) : _jacC(jacC) + { + } + virtual ~GmresSolver() CADET_NOEXCEPT + { + } - GmresSolver(linalg::CompressedSparseMatrix const* jacC) : _jacC(jacC) { } - virtual ~GmresSolver() CADET_NOEXCEPT { } - - virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, unsigned int nRad, const Weno& weno) + virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, unsigned int nRad, + const Weno& weno) { _gmres.initialize(nCol * nComp * nRad, 0, linalg::toOrthogonalization(1), 0); _gmres.matrixVectorMultiplier(&schurComplementMultiplier2DCDO, this); @@ -494,16 +534,21 @@ class TwoDimensionalConvectionDispersionOperator::GmresSolver : public TwoDimens return true; } - virtual void setSparsityPattern(const linalg::SparsityPattern& pattern) { } + virtual void setSparsityPattern(const linalg::SparsityPattern& pattern) + { + } virtual void assembleDiscretizedJacobian(double alpha) { _alpha = alpha; } - virtual bool factorize() { return true; } + virtual bool factorize() + { + return true; + } - virtual bool solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, double outerTol) const + virtual bool solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, double outerTol) const { if (init) std::copy(init, init + _cache.size(), _cache.begin()); @@ -517,7 +562,7 @@ class TwoDimensionalConvectionDispersionOperator::GmresSolver : public TwoDimens protected: linalg::CompressedSparseMatrix const* const _jacC; double _alpha; - mutable linalg::Gmres _gmres; //!< GMRES algorithm for the Schur-complement in linearSolve() + mutable linalg::Gmres _gmres; //!< GMRES algorithm for the Schur-complement in linearSolve() mutable std::vector _cache; //!< GMRES cache for result int schurComplementMatrixVector(double const* x, double* z) const @@ -533,70 +578,81 @@ class TwoDimensionalConvectionDispersionOperator::GmresSolver : public TwoDimens int schurComplementMultiplier2DCDO(void* userData, double const* x, double* z) { - TwoDimensionalConvectionDispersionOperator::GmresSolver* const cdo = static_cast(userData); + TwoDimensionalConvectionDispersionOperator::GmresSolver* const cdo = + static_cast(userData); return cdo->schurComplementMatrixVector(x, z); } -#if defined(UMFPACK_FOUND) || defined(SUPERLU_FOUND) +#if defined(UMFPACK_FOUND) || defined(SUPERLU_FOUND) - template - class TwoDimensionalConvectionDispersionOperator::SparseDirectSolver : public TwoDimensionalConvectionDispersionOperator::LinearSolver +template +class TwoDimensionalConvectionDispersionOperator::SparseDirectSolver + : public TwoDimensionalConvectionDispersionOperator::LinearSolver +{ +public: + SparseDirectSolver(linalg::CompressedSparseMatrix const* jacC) : _jacC(jacC) { - public: - - SparseDirectSolver(linalg::CompressedSparseMatrix const* jacC) : _jacC(jacC) { } - virtual ~SparseDirectSolver() CADET_NOEXCEPT { } + } + virtual ~SparseDirectSolver() CADET_NOEXCEPT + { + } - virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, unsigned int nRad, const Weno& weno) - { - return true; - } + virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, unsigned int nRad, + const Weno& weno) + { + return true; + } - virtual void setSparsityPattern(const linalg::SparsityPattern& pattern) - { - _jacCdisc.assignPattern(pattern); - _jacCdisc.prepare(); - } + virtual void setSparsityPattern(const linalg::SparsityPattern& pattern) + { + _jacCdisc.assignPattern(pattern); + _jacCdisc.prepare(); + } - virtual void assembleDiscretizedJacobian(double alpha) - { - // Copy normal matrix over to factorizable matrix - _jacCdisc.copyFromSamePattern(*_jacC); + virtual void assembleDiscretizedJacobian(double alpha) + { + // Copy normal matrix over to factorizable matrix + _jacCdisc.copyFromSamePattern(*_jacC); - for (int i = 0; i < _jacC->rows(); ++i) - _jacCdisc.centered(i, 0) += alpha; - } + for (int i = 0; i < _jacC->rows(); ++i) + _jacCdisc.centered(i, 0) += alpha; + } - virtual bool factorize() - { - return _jacCdisc.factorize(); - } + virtual bool factorize() + { + return _jacCdisc.factorize(); + } - virtual bool solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, double outerTol) const - { - return _jacCdisc.solve(rhs); - } + virtual bool solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, double outerTol) const + { + return _jacCdisc.solve(rhs); + } - protected: - linalg::CompressedSparseMatrix const* const _jacC; - sparse_t _jacCdisc; - }; +protected: + linalg::CompressedSparseMatrix const* const _jacC; + sparse_t _jacCdisc; +}; #endif -class TwoDimensionalConvectionDispersionOperator::DenseDirectSolver : public TwoDimensionalConvectionDispersionOperator::LinearSolver +class TwoDimensionalConvectionDispersionOperator::DenseDirectSolver + : public TwoDimensionalConvectionDispersionOperator::LinearSolver { public: + DenseDirectSolver(linalg::CompressedSparseMatrix const* jacC) : _jacC(jacC) + { + } + virtual ~DenseDirectSolver() CADET_NOEXCEPT + { + } - DenseDirectSolver(linalg::CompressedSparseMatrix const* jacC) : _jacC(jacC) { } - virtual ~DenseDirectSolver() CADET_NOEXCEPT { } - - virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, unsigned int nRad, const Weno& weno) + virtual bool initialize(IParameterProvider& paramProvider, unsigned int nComp, unsigned int nCol, unsigned int nRad, + const Weno& weno) { // Note that we have to increase the lower bandwidth by 1 because the WENO stencil is applied to the - // right cell face (lower + 1 + upper) and to the left cell face (shift the stencil by -1 because influx of cell i - // is outflux of cell i-1) - // We also have to make sure that there's at least one sub and super diagonal for the dispersion term + // right cell face (lower + 1 + upper) and to the left cell face (shift the stencil by -1 because influx of cell + // i is outflux of cell i-1) We also have to make sure that there's at least one sub and super diagonal for the + // dispersion term const unsigned int lb = std::max(weno.lowerBandwidth() + 1u, 1u) * nComp * nRad; // We have to make sure that there's at least one sub and super diagonal for the dispersion term @@ -611,7 +667,9 @@ class TwoDimensionalConvectionDispersionOperator::DenseDirectSolver : public Two return true; } - virtual void setSparsityPattern(const linalg::SparsityPattern& pattern) { } + virtual void setSparsityPattern(const linalg::SparsityPattern& pattern) + { + } virtual void assembleDiscretizedJacobian(double alpha) { @@ -632,7 +690,7 @@ class TwoDimensionalConvectionDispersionOperator::DenseDirectSolver : public Two jac[diag] = vals[c]; } - // Add time derivative + // Add time derivative jac[0] += alpha; } } @@ -642,7 +700,7 @@ class TwoDimensionalConvectionDispersionOperator::DenseDirectSolver : public Two return _jacCdisc.factorize(); } - virtual bool solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, double outerTol) const + virtual bool solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, double outerTol) const { return _jacCdisc.solve(rhs); } @@ -652,12 +710,12 @@ class TwoDimensionalConvectionDispersionOperator::DenseDirectSolver : public Two linalg::FactorizableBandMatrix _jacCdisc; }; - /** * @brief Creates a TwoDimensionalConvectionDispersionOperator */ -TwoDimensionalConvectionDispersionOperator::TwoDimensionalConvectionDispersionOperator() : _colPorosities(0), _dir(0), _stencilMemory(sizeof(active) * Weno::maxStencilSize()), - _wenoDerivatives(new double[Weno::maxStencilSize()]), _weno(), _linearSolver(nullptr), _dispersionDep(nullptr) +TwoDimensionalConvectionDispersionOperator::TwoDimensionalConvectionDispersionOperator() + : _colPorosities(0), _dir(0), _stencilMemory(sizeof(active) * Weno::maxStencilSize()), + _wenoDerivatives(new double[Weno::maxStencilSize()]), _weno(), _linearSolver(nullptr), _dispersionDep(nullptr) { } @@ -677,7 +735,10 @@ TwoDimensionalConvectionDispersionOperator::~TwoDimensionalConvectionDispersionO * @param [in] dynamicReactions Determines whether the sparsity pattern accounts for dynamic reactions * @return @c true if configuration went fine, @c false otherwise */ -bool TwoDimensionalConvectionDispersionOperator::configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, unsigned int nCol, unsigned int nRad, bool dynamicReactions) +bool TwoDimensionalConvectionDispersionOperator::configureModelDiscretization(IParameterProvider& paramProvider, + const IConfigHelper& helper, + unsigned int nComp, unsigned int nCol, + unsigned int nRad, bool dynamicReactions) { _nComp = nComp; _nCol = nCol; @@ -723,17 +784,18 @@ bool TwoDimensionalConvectionDispersionOperator::configureModelDiscretization(IP _linearSolver = new SparseDirectSolver(&_jacC); #elif defined(SUPERLU_FOUND) _linearSolver = new SparseDirectSolver(&_jacC); -#else +#else _linearSolver = new DenseDirectSolver(&_jacC); #endif -// LOG(Info) << "Default to dense banded linear solver due to invalid or missing LINEAR_SOLVER_BULK setting"; + // LOG(Info) << "Default to dense banded linear solver due to invalid or missing LINEAR_SOLVER_BULK + // setting"; } paramProvider.popScope(); _radialEdges.resize(nRad + 1); _radialCenters.resize(nRad); -// _radialCentroids.resize(nRad); + // _radialCentroids.resize(nRad); _crossSections.resize(nRad); _curVelocity.resize(nRad); @@ -750,27 +812,29 @@ bool TwoDimensionalConvectionDispersionOperator::configureModelDiscretization(IP * @param [out] parameters Map in which local parameters are inserted * @return @c true if configuration went fine, @c false otherwise */ -bool TwoDimensionalConvectionDispersionOperator::configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, std::unordered_map& parameters) +bool TwoDimensionalConvectionDispersionOperator::configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, + std::unordered_map& parameters) { // Read geometry parameters _colLength = paramProvider.getDouble("COL_LENGTH"); if (paramProvider.exists("COL_RADIUS")) - { - _colRadius = paramProvider.getDouble("COL_RADIUS"); - } - else if(paramProvider.exists("CROSS_SECTION_AREA")) - { - const double cross_section_area = paramProvider.getDouble("CROSS_SECTION_AREA"); - const double pi = 3.1415926535897932384626434; - _colRadius = std::sqrt(cross_section_area / pi); - } + { + _colRadius = paramProvider.getDouble("COL_RADIUS"); + } + else if (paramProvider.exists("CROSS_SECTION_AREA")) + { + const double cross_section_area = paramProvider.getDouble("CROSS_SECTION_AREA"); + const double pi = 3.1415926535897932384626434; + _colRadius = std::sqrt(cross_section_area / pi); + } else throw InvalidParameterException("Either COL_RADIUS or CROSS_SECTION_AREA must be provided"); readScalarParameterOrArray(_colPorosities, paramProvider, "COL_POROSITY", 1); if ((_colPorosities.size() != 1) && (_colPorosities.size() != _nRad)) - throw InvalidParameterException("Number of elements in field COL_POROSITY is neither 1 nor NRAD (" + std::to_string(_nRad) + ")"); + throw InvalidParameterException("Number of elements in field COL_POROSITY is neither 1 nor NRAD (" + + std::to_string(_nRad) + ")"); _singlePorosity = (_colPorosities.size() == 1); if (_singlePorosity) @@ -787,9 +851,13 @@ bool TwoDimensionalConvectionDispersionOperator::configure(UnitOpIdx unitOpIdx, readScalarParameterOrArray(_radialEdges, paramProvider, "RADIAL_COMPARTMENTS", 1); if (_radialEdges.size() < _nRad + 1) - throw InvalidParameterException("Number of elements in field RADIAL_COMPARTMENTS is less than NRAD + 1 (" + std::to_string(_nRad + 1) + ")"); + throw InvalidParameterException("Number of elements in field RADIAL_COMPARTMENTS is less than NRAD + 1 (" + + std::to_string(_nRad + 1) + ")"); - registerParam1DArray(parameters, _radialEdges, [=](bool multi, unsigned int i) { return makeParamId(hashString("RADIAL_COMPARTMENTS"), unitOpIdx, CompIndep, i, BoundStateIndep, ReactionIndep, SectionIndep); }); + registerParam1DArray(parameters, _radialEdges, [=](bool multi, unsigned int i) { + return makeParamId(hashString("RADIAL_COMPARTMENTS"), unitOpIdx, CompIndep, i, BoundStateIndep, + ReactionIndep, SectionIndep); + }); } else _radialDiscretizationMode = RadialDiscretizationMode::Equidistant; @@ -822,11 +890,16 @@ bool TwoDimensionalConvectionDispersionOperator::configure(UnitOpIdx unitOpIdx, _singleVelocity = false; if (!_singleVelocity && (_velocity.size() % _nRad != 0)) - throw InvalidParameterException("Number of elements in field VELOCITY is not a positive multiple of NRAD (" + std::to_string(_nRad) + ")"); + throw InvalidParameterException( + "Number of elements in field VELOCITY is not a positive multiple of NRAD (" + + std::to_string(_nRad) + ")"); if ((mode == 0) && (_velocity.size() != 1)) - throw InvalidParameterException("Number of elements in field VELOCITY inconsistent with VELOCITY_MULTIPLEX (should be 1)"); + throw InvalidParameterException( + "Number of elements in field VELOCITY inconsistent with VELOCITY_MULTIPLEX (should be 1)"); if ((mode == 1) && (_velocity.size() != _nRad)) - throw InvalidParameterException("Number of elements in field VELOCITY inconsistent with VELOCITY_MULTIPLEX (should be " + std::to_string(_nRad) + ")"); + throw InvalidParameterException( + "Number of elements in field VELOCITY inconsistent with VELOCITY_MULTIPLEX (should be " + + std::to_string(_nRad) + ")"); } else { @@ -858,26 +931,41 @@ bool TwoDimensionalConvectionDispersionOperator::configure(UnitOpIdx unitOpIdx, { // Register only the first item in each section for (std::size_t i = 0; i < _velocity.size() / _nRad; ++i) - parameters[makeParamId(hashString("VELOCITY"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, i)] = &_velocity[i * _nRad]; + parameters[makeParamId(hashString("VELOCITY"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, i)] = &_velocity[i * _nRad]; } else { // We have only one parameter - parameters[makeParamId(hashString("VELOCITY"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_velocity[0]; + parameters[makeParamId(hashString("VELOCITY"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep)] = &_velocity[0]; } } else - registerParam2DArray(parameters, _velocity, [=](bool multi, unsigned int sec, unsigned int compartment) { return makeParamId(hashString("VELOCITY"), unitOpIdx, CompIndep, compartment, BoundStateIndep, ReactionIndep, multi ? sec : SectionIndep); }, _nRad); + registerParam2DArray( + parameters, _velocity, + [=](bool multi, unsigned int sec, unsigned int compartment) { + return makeParamId(hashString("VELOCITY"), unitOpIdx, CompIndep, compartment, BoundStateIndep, + ReactionIndep, multi ? sec : SectionIndep); + }, + _nRad); _dir = std::vector(_nRad, 1); - _axialDispersionMode = readAndRegisterMultiplexParam(paramProvider, parameters, _axialDispersion, "COL_DISPERSION", _nComp, _nRad, unitOpIdx); - _radialDispersionMode = readAndRegisterMultiplexParam(paramProvider, parameters, _radialDispersion, "COL_DISPERSION_RADIAL", _nComp, _nRad, unitOpIdx); + _axialDispersionMode = readAndRegisterMultiplexParam(paramProvider, parameters, _axialDispersion, "COL_DISPERSION", + _nComp, _nRad, unitOpIdx); + _radialDispersionMode = readAndRegisterMultiplexParam(paramProvider, parameters, _radialDispersion, + "COL_DISPERSION_RADIAL", _nComp, _nRad, unitOpIdx); // Add parameters to map - parameters[makeParamId(hashString("COL_LENGTH"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_colLength; - parameters[makeParamId(hashString("COL_RADIUS"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep)] = &_colRadius; - registerParam1DArray(parameters, _colPorosities, [=](bool multi, unsigned int i) { return makeParamId(hashString("COL_POROSITY"), unitOpIdx, CompIndep, multi ? i : ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep); }); + parameters[makeParamId(hashString("COL_LENGTH"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep)] = &_colLength; + parameters[makeParamId(hashString("COL_RADIUS"), unitOpIdx, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, + SectionIndep)] = &_colRadius; + registerParam1DArray(parameters, _colPorosities, [=](bool multi, unsigned int i) { + return makeParamId(hashString("COL_POROSITY"), unitOpIdx, CompIndep, multi ? i : ParTypeIndep, BoundStateIndep, + ReactionIndep, SectionIndep); + }); return true; } @@ -926,7 +1014,8 @@ bool TwoDimensionalConvectionDispersionOperator::notifyDiscontinuousSectionTrans * @param [in] in Total volumetric inlet flow rate * @param [in] out Total volumetric outlet flow rate */ -void TwoDimensionalConvectionDispersionOperator::setFlowRates(int compartment, const active& in, const active& out) CADET_NOEXCEPT +void TwoDimensionalConvectionDispersionOperator::setFlowRates(int compartment, const active& in, + const active& out) CADET_NOEXCEPT { _curVelocity[compartment] = _dir[compartment] * in / (_crossSections[compartment] * _colPorosities[compartment]); } @@ -943,12 +1032,14 @@ double TwoDimensionalConvectionDispersionOperator::inletFactor(unsigned int idxS return -std::abs(static_cast(_curVelocity[idxRad])) / h; } -const active& TwoDimensionalConvectionDispersionOperator::axialDispersion(unsigned int idxSec, int idxRad, int idxComp) const CADET_NOEXCEPT +const active& TwoDimensionalConvectionDispersionOperator::axialDispersion(unsigned int idxSec, int idxRad, + int idxComp) const CADET_NOEXCEPT { return *(getSectionDependentSlice(_axialDispersion, _nRad * _nComp, idxSec) + idxRad * _nComp + idxComp); } -const active& TwoDimensionalConvectionDispersionOperator::radialDispersion(unsigned int idxSec, int idxRad, int idxComp) const CADET_NOEXCEPT +const active& TwoDimensionalConvectionDispersionOperator::radialDispersion(unsigned int idxSec, int idxRad, + int idxComp) const CADET_NOEXCEPT { return *(getSectionDependentSlice(_radialDispersion, _nRad * _nComp, idxSec) + idxRad * _nComp + idxComp); } @@ -963,7 +1054,9 @@ const active& TwoDimensionalConvectionDispersionOperator::radialDispersion(unsig * @param [in] wantJac Determines whether the Jacobian is computed or not * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ -int TwoDimensionalConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, bool wantJac, WithoutParamSensitivity) +int TwoDimensionalConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, double* res, bool wantJac, + WithoutParamSensitivity) { if (wantJac) return residualImpl(model, t, secIdx, y, yDot, res); @@ -971,7 +1064,9 @@ int TwoDimensionalConvectionDispersionOperator::residual(const IModel& model, do return residualImpl(model, t, secIdx, y, yDot, res); } -int TwoDimensionalConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithoutParamSensitivity) +int TwoDimensionalConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, + active const* y, double const* yDot, active* res, bool wantJac, + WithoutParamSensitivity) { if (wantJac) return residualImpl(model, t, secIdx, y, yDot, res); @@ -979,7 +1074,9 @@ int TwoDimensionalConvectionDispersionOperator::residual(const IModel& model, do return residualImpl(model, t, secIdx, y, yDot, res); } -int TwoDimensionalConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity) +int TwoDimensionalConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, + double const* y, double const* yDot, active* res, bool wantJac, + WithParamSensitivity) { if (wantJac) return residualImpl(model, t, secIdx, y, yDot, res); @@ -987,7 +1084,9 @@ int TwoDimensionalConvectionDispersionOperator::residual(const IModel& model, do return residualImpl(model, t, secIdx, y, yDot, res); } -int TwoDimensionalConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity) +int TwoDimensionalConvectionDispersionOperator::residual(const IModel& model, double t, unsigned int secIdx, + active const* y, double const* yDot, active* res, bool wantJac, + WithParamSensitivity) { if (wantJac) return residualImpl(model, t, secIdx, y, yDot, res); @@ -996,7 +1095,8 @@ int TwoDimensionalConvectionDispersionOperator::residual(const IModel& model, do } template -int TwoDimensionalConvectionDispersionOperator::residualImpl(const IModel& model, double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res) +int TwoDimensionalConvectionDispersionOperator::residualImpl(const IModel& model, double t, unsigned int secIdx, + StateType const* y, double const* yDot, ResidualType* res) { if (wantJac) { @@ -1018,19 +1118,20 @@ int TwoDimensionalConvectionDispersionOperator::residualImpl(const IModel& model &_weno, &_stencilMemory, _wenoEpsilon, - static_cast(_nComp * _nRad), // Stride between two cells + static_cast(_nComp * _nRad), // Stride between two cells _nComp, _nCol, - _nComp * i, // Offset to the first component of the inlet DOFs in the local state vector - _nComp * (_nRad + i), // Offset to the first component of the first bulk cell in the local state vector + _nComp * i, // Offset to the first component of the inlet DOFs in the local state vector + _nComp * (_nRad + i), // Offset to the first component of the first bulk cell in the local state vector _dispersionDep, - model - }; + model}; if (wantJac) - convdisp::residualKernelAxial(SimulationTime{t, secIdx}, y, yDot, res, _jacC.row(i * _nComp), fp); + convdisp::residualKernelAxial( + SimulationTime{t, secIdx}, y, yDot, res, _jacC.row(i * _nComp), fp); else - convdisp::residualKernelAxial(SimulationTime{t, secIdx}, y, yDot, res, _jacC.row(i * _nComp), fp); + convdisp::residualKernelAxial( + SimulationTime{t, secIdx}, y, yDot, res, _jacC.row(i * _nComp), fp); } // Handle radial dispersion @@ -1054,16 +1155,18 @@ int TwoDimensionalConvectionDispersionOperator::residualImpl(const IModel& model active const* const localD_rho = d_rho + rad * _nComp; const ParamType invEps = 1.0 / static_cast(_colPorosities[rad]); - const ParamType deltaRho = static_cast(_radialEdges[rad+1]) - static_cast(_radialEdges[rad]); + const ParamType deltaRho = + static_cast(_radialEdges[rad + 1]) - static_cast(_radialEdges[rad]); const ParamType rho = static_cast(_radialCenters[rad]); -// const ParamType rhoCentroid = static_cast(_radialCentroids[rad]); + // const ParamType rhoCentroid = static_cast(_radialCentroids[rad]); for (unsigned int comp = 0; comp < _nComp; ++comp) { const unsigned int offsetCur = offsetColRadBlock + comp; ResidualType* const resCur = resColRadBlock + comp; StateType const* const yCur = yColRadBlock + comp; - const ParamType curFluxCoeff = static_cast(_colPorosities[rad]) * static_cast(localD_rho[comp]); + const ParamType curFluxCoeff = + static_cast(_colPorosities[rad]) * static_cast(localD_rho[comp]); if (cadet_unlikely(curFluxCoeff <= 0.0)) continue; @@ -1071,16 +1174,29 @@ int TwoDimensionalConvectionDispersionOperator::residualImpl(const IModel& model if (cadet_likely(rad > 0)) { // Flow from inner cell - const ParamType centerDiffRho = rho - static_cast(_radialCenters[rad-1]); -// const ParamType centerDiffRho = rhoCentroid - static_cast(_radialCentroids[rad-1]); - const ParamType innerFluxCoeff = static_cast(_colPorosities[rad-1]) * static_cast(localD_rho[-static_cast(_nComp) + static_cast(comp)]); + const ParamType centerDiffRho = rho - static_cast(_radialCenters[rad - 1]); + // const ParamType centerDiffRho = rhoCentroid - + // static_cast(_radialCentroids[rad-1]); + const ParamType innerFluxCoeff = + static_cast(_colPorosities[rad - 1]) * + static_cast(localD_rho[-static_cast(_nComp) + static_cast(comp)]); if (cadet_likely(innerFluxCoeff > 0.0)) { -// const ParamType fluxCoeff = (centerDiffRho * curFluxCoeff * innerFluxCoeff) / (curFluxCoeff * (static_cast(_radialEdges[rad]) - static_cast(_radialCentroids[rad-1])) + innerFluxCoeff * (rhoCentroid - static_cast(_radialEdges[rad]))); - const ParamType fluxCoeff = (centerDiffRho * curFluxCoeff * innerFluxCoeff) / (curFluxCoeff * (static_cast(_radialEdges[rad]) - static_cast(_radialCenters[rad-1])) + innerFluxCoeff * (rho - static_cast(_radialEdges[rad]))); -// const ParamType fluxCoeff = static_cast(_colPorosities[0]) * static_cast(d_rho[0]); - const ParamType finalFactor = invEps * (static_cast(_radialEdges[rad]) * fluxCoeff / centerDiffRho) / (rho * deltaRho); + // const ParamType fluxCoeff = (centerDiffRho * curFluxCoeff * + // innerFluxCoeff) / (curFluxCoeff * (static_cast(_radialEdges[rad]) - + // static_cast(_radialCentroids[rad-1])) + innerFluxCoeff * (rhoCentroid - + // static_cast(_radialEdges[rad]))); + const ParamType fluxCoeff = + (centerDiffRho * curFluxCoeff * innerFluxCoeff) / + (curFluxCoeff * (static_cast(_radialEdges[rad]) - + static_cast(_radialCenters[rad - 1])) + + innerFluxCoeff * (rho - static_cast(_radialEdges[rad]))); + // const ParamType fluxCoeff = static_cast(_colPorosities[0]) * + // static_cast(d_rho[0]); + const ParamType finalFactor = + invEps * (static_cast(_radialEdges[rad]) * fluxCoeff / centerDiffRho) / + (rho * deltaRho); *resCur += finalFactor * (yCur[0] - yCur[-static_cast(_nComp)]); if (wantJac) @@ -1094,16 +1210,28 @@ int TwoDimensionalConvectionDispersionOperator::residualImpl(const IModel& model if (cadet_likely(rad < _nRad - 1)) { // Flow from outer cell - const ParamType centerDiffRho = static_cast(_radialCenters[rad+1]) - rho; -// const ParamType centerDiffRho = static_cast(_radialCentroids[rad+1]) - rhoCentroid; - const ParamType outerFluxCoeff = static_cast(_colPorosities[rad+1]) * static_cast(localD_rho[_nComp + comp]); + const ParamType centerDiffRho = static_cast(_radialCenters[rad + 1]) - rho; + // const ParamType centerDiffRho = static_cast(_radialCentroids[rad+1]) + //- rhoCentroid; + const ParamType outerFluxCoeff = static_cast(_colPorosities[rad + 1]) * + static_cast(localD_rho[_nComp + comp]); if (cadet_likely(outerFluxCoeff > 0.0)) { -// const ParamType fluxCoeff = (centerDiffRho * curFluxCoeff * outerFluxCoeff) / (curFluxCoeff * (static_cast(_radialCentroids[rad+1] - static_cast(_radialEdges[rad+1]))) + outerFluxCoeff * (static_cast(_radialEdges[rad+1]) - rhoCentroid)); - const ParamType fluxCoeff = (centerDiffRho * curFluxCoeff * outerFluxCoeff) / (curFluxCoeff * (static_cast(_radialCenters[rad+1] - static_cast(_radialEdges[rad+1]))) + outerFluxCoeff * (static_cast(_radialEdges[rad+1]) - rho)); -// const ParamType fluxCoeff = static_cast(_colPorosities[0]) * static_cast(d_rho[0]); - const ParamType finalFactor = invEps * (static_cast(_radialEdges[rad+1]) * fluxCoeff / centerDiffRho) / (rho * deltaRho); + // const ParamType fluxCoeff = (centerDiffRho * curFluxCoeff * + // outerFluxCoeff) / (curFluxCoeff * (static_cast(_radialCentroids[rad+1] - + // static_cast(_radialEdges[rad+1]))) + outerFluxCoeff * + //(static_cast(_radialEdges[rad+1]) - rhoCentroid)); + const ParamType fluxCoeff = + (centerDiffRho * curFluxCoeff * outerFluxCoeff) / + (curFluxCoeff * (static_cast(_radialCenters[rad + 1] - + static_cast(_radialEdges[rad + 1]))) + + outerFluxCoeff * (static_cast(_radialEdges[rad + 1]) - rho)); + // const ParamType fluxCoeff = static_cast(_colPorosities[0]) * + // static_cast(d_rho[0]); + const ParamType finalFactor = + invEps * (static_cast(_radialEdges[rad + 1]) * fluxCoeff / centerDiffRho) / + (rho * deltaRho); *resCur += finalFactor * (yCur[0] - yCur[_nComp]); if (wantJac) @@ -1133,7 +1261,8 @@ void TwoDimensionalConvectionDispersionOperator::setSparsityPattern() // Handle convection, axial dispersion (WENO) for (unsigned int i = 0; i < _nRad; ++i) - cadet::model::parts::convdisp::sparsityPatternAxial(pattern.row(i * _nComp), _nComp, _nCol, _nComp * _nRad, static_cast(_curVelocity[i]), _weno); + cadet::model::parts::convdisp::sparsityPatternAxial(pattern.row(i * _nComp), _nComp, _nCol, _nComp * _nRad, + static_cast(_curVelocity[i]), _weno); // Handle radial dispersion if (_nRad > 1) @@ -1193,17 +1322,20 @@ void TwoDimensionalConvectionDispersionOperator::setSparsityPattern() } /** - * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ with a given vector + * @brief Multiplies the time derivative Jacobian @f$ \frac{\partial F}{\partial \dot{y}}\left(t, y, \dot{y}\right) @f$ + * with a given vector * @details The operation @f$ z = \frac{\partial F}{\partial \dot{y}} x @f$ is performed. * The matrix-vector multiplication is performed matrix-free (i.e., no matrix is explicitly formed). - * + * * Note that this function only performs multiplication with the Jacobian of the (axial) transport equations. - * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit operation's arrays. + * The input vectors are assumed to point to the beginning (including inlet DOFs) of the respective unit + * operation's arrays. * @param [in] simTime Simulation time information (time point, section index, pre-factor of time derivatives) * @param [in] sDot Vector @f$ x @f$ that is transformed by the Jacobian @f$ \frac{\partial F}{\partial \dot{y}} @f$ * @param [out] ret Vector @f$ z @f$ which stores the result of the operation */ -void TwoDimensionalConvectionDispersionOperator::multiplyWithDerivativeJacobian(const SimulationTime& simTime, double const* sDot, double* ret) const +void TwoDimensionalConvectionDispersionOperator::multiplyWithDerivativeJacobian(const SimulationTime& simTime, + double const* sDot, double* ret) const { double* localRet = ret + _nComp * _nRad; double const* localSdot = sDot + _nComp * _nRad; @@ -1213,16 +1345,13 @@ void TwoDimensionalConvectionDispersionOperator::multiplyWithDerivativeJacobian( /** * @brief Assembles the axial transport Jacobian @f$ J_0 @f$ of the time-discretized equations - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The system Jacobian of the original equations, - * \f[ \frac{\partial F}{\partial y}, \f] - * is already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible - * for adding - * \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] - * to the system Jacobian, which yields the Jacobian of the time-discretized equations - * \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] - * when a BDF method is used. The time integrator needs to solve this equation for @f$ y_0 @f$, which requires - * the solution of the linear system mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = + * b \f] has to be solved. The system Jacobian of the original equations, \f[ \frac{\partial F}{\partial y}, \f] is + * already computed (by AD or manually in residualImpl() with @c wantJac = true). This function is responsible for + * adding \f[ \alpha \frac{\partial F}{\partial \dot{y}} \f] to the system Jacobian, which yields the Jacobian of the + * time-discretized equations \f[ F\left(t, y_0, \sum_{k=0}^N \alpha_k y_k \right) = 0 \f] when a BDF method is used. + * The time integrator needs to solve this equation for @f$ y_0 @f$, which requires the solution of the linear system + * mentioned above (@f$ \alpha_0 = \alpha @f$ given in @p alpha). * * @param [in] alpha Value of \f$ \alpha \f$ (arises from BDF time discretization) */ @@ -1248,11 +1377,12 @@ bool TwoDimensionalConvectionDispersionOperator::assembleAndFactorizeDiscretized * @details The (time discretized) Jacobian matrix has to be factorized before calling this function. * Note that the given right hand side vector @p rhs is not shifted by the inlet DOFs. That * is, it is assumed to point directly to the first axial DOF. - * + * * @param [in,out] rhs On entry, right hand side of the equation system. On exit, solution of the system. * @return @c true if the system was solved correctly, otherwise @c false */ -bool TwoDimensionalConvectionDispersionOperator::solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, double outerTol) const +bool TwoDimensionalConvectionDispersionOperator::solveDiscretizedJacobian(double* rhs, double const* weight, + double const* init, double outerTol) const { return _linearSolver->solveDiscretizedJacobian(rhs, weight, init, outerTol); } @@ -1265,7 +1395,8 @@ bool TwoDimensionalConvectionDispersionOperator::solveDiscretizedJacobian(double * @param [in,out] rhs On entry, right hand side. On exit, solution of the system. * @return @c true if the system was solved correctly, @c false otherwise */ -bool TwoDimensionalConvectionDispersionOperator::solveTimeDerivativeSystem(const SimulationTime& simTime, double* const rhs) +bool TwoDimensionalConvectionDispersionOperator::solveTimeDerivativeSystem(const SimulationTime& simTime, + double* const rhs) { return true; } @@ -1280,13 +1411,14 @@ void TwoDimensionalConvectionDispersionOperator::setEquidistantRadialDisc() { // Set last edge to _colRadius for exact geometry if (r == _nRad - 1) - _radialEdges[r+1] = _colRadius; + _radialEdges[r + 1] = _colRadius; else - _radialEdges[r+1] = h * (r + 1); + _radialEdges[r + 1] = h * (r + 1); - _crossSections[r] = pi * (_radialEdges[r+1] * _radialEdges[r+1] - _radialEdges[r] * _radialEdges[r]); - _radialCenters[r] = 0.5 * (_radialEdges[r+1] + _radialEdges[r]); -// _radialCentroids[r] = 2.0 / 3.0 * ( _radialEdges[r+1] * _radialEdges[r+1] / (_radialEdges[r+1] + _radialEdges[r]) + _radialEdges[r]); + _crossSections[r] = pi * (_radialEdges[r + 1] * _radialEdges[r + 1] - _radialEdges[r] * _radialEdges[r]); + _radialCenters[r] = 0.5 * (_radialEdges[r + 1] + _radialEdges[r]); + // _radialCentroids[r] = 2.0 / 3.0 * ( _radialEdges[r+1] * _radialEdges[r+1] / (_radialEdges[r+1] + + //_radialEdges[r]) + _radialEdges[r]); } } @@ -1300,13 +1432,14 @@ void TwoDimensionalConvectionDispersionOperator::setEquivolumeRadialDisc() { // Set last edge to _colRadius for exact geometry if (r == _nRad - 1) - _radialEdges[r+1] = _colRadius; + _radialEdges[r + 1] = _colRadius; else - _radialEdges[r+1] = sqrt(volPerCompartment + _radialEdges[r] * _radialEdges[r]); + _radialEdges[r + 1] = sqrt(volPerCompartment + _radialEdges[r] * _radialEdges[r]); _crossSections[r] = pi * volPerCompartment; - _radialCenters[r] = 0.5 * (_radialEdges[r+1] + _radialEdges[r]); -// _radialCentroids[r] = 2.0 / 3.0 * ( _radialEdges[r+1] * _radialEdges[r+1] / (_radialEdges[r+1] + _radialEdges[r]) + _radialEdges[r]); + _radialCenters[r] = 0.5 * (_radialEdges[r + 1] + _radialEdges[r]); + // _radialCentroids[r] = 2.0 / 3.0 * ( _radialEdges[r+1] * _radialEdges[r+1] / (_radialEdges[r+1] + + //_radialEdges[r]) + _radialEdges[r]); } } @@ -1315,9 +1448,10 @@ void TwoDimensionalConvectionDispersionOperator::setUserdefinedRadialDisc() const double pi = 3.1415926535897932384626434; for (unsigned int r = 0; r < _nRad; ++r) { - _crossSections[r] = pi * (_radialEdges[r+1] * _radialEdges[r+1] - _radialEdges[r] * _radialEdges[r]); - _radialCenters[r] = 0.5 * (_radialEdges[r+1] + _radialEdges[r]); -// _radialCentroids[r] = 2.0 / 3.0 * ( _radialEdges[r+1] * _radialEdges[r+1] / (_radialEdges[r+1] + _radialEdges[r]) + _radialEdges[r]); + _crossSections[r] = pi * (_radialEdges[r + 1] * _radialEdges[r + 1] - _radialEdges[r] * _radialEdges[r]); + _radialCenters[r] = 0.5 * (_radialEdges[r + 1] + _radialEdges[r]); + // _radialCentroids[r] = 2.0 / 3.0 * ( _radialEdges[r+1] * _radialEdges[r+1] / (_radialEdges[r+1] + + //_radialEdges[r]) + _radialEdges[r]); } } @@ -1333,15 +1467,17 @@ void TwoDimensionalConvectionDispersionOperator::updateRadialDisc() bool TwoDimensionalConvectionDispersionOperator::setParameter(const ParameterId& pId, double value) { - if (_singlePorosity && (pId.name == hashString("COL_POROSITY")) && (pId.component == CompIndep) && (pId.boundState == BoundStateIndep) - && (pId.reaction == ReactionIndep) && (pId.section == SectionIndep) && (pId.particleType == ParTypeIndep)) + if (_singlePorosity && (pId.name == hashString("COL_POROSITY")) && (pId.component == CompIndep) && + (pId.boundState == BoundStateIndep) && (pId.reaction == ReactionIndep) && (pId.section == SectionIndep) && + (pId.particleType == ParTypeIndep)) { for (unsigned int i = 0; i < _nRad; ++i) _colPorosities[i].setValue(value); return true; } - if (_singleVelocity && (pId.name == hashString("VELOCITY")) && (pId.component == CompIndep) && (pId.boundState == BoundStateIndep) && (pId.reaction == ReactionIndep)) + if (_singleVelocity && (pId.name == hashString("VELOCITY")) && (pId.component == CompIndep) && + (pId.boundState == BoundStateIndep) && (pId.reaction == ReactionIndep)) { if (_velocity.size() > _nRad) { @@ -1363,21 +1499,25 @@ bool TwoDimensionalConvectionDispersionOperator::setParameter(const ParameterId& } } - const bool ad = multiplexParameterValue(pId, hashString("COL_DISPERSION"), _axialDispersionMode, _axialDispersion, _nComp, _nRad, value, nullptr); + const bool ad = multiplexParameterValue(pId, hashString("COL_DISPERSION"), _axialDispersionMode, _axialDispersion, + _nComp, _nRad, value, nullptr); if (ad) return true; - const bool adr = multiplexParameterValue(pId, hashString("COL_DISPERSION_RADIAL"), _radialDispersionMode, _radialDispersion, _nComp, _nRad, value, nullptr); + const bool adr = multiplexParameterValue(pId, hashString("COL_DISPERSION_RADIAL"), _radialDispersionMode, + _radialDispersion, _nComp, _nRad, value, nullptr); if (adr) return true; return false; } -bool TwoDimensionalConvectionDispersionOperator::setSensitiveParameterValue(const std::unordered_set& sensParams, const ParameterId& pId, double value) +bool TwoDimensionalConvectionDispersionOperator::setSensitiveParameterValue( + const std::unordered_set& sensParams, const ParameterId& pId, double value) { - if (_singlePorosity && (pId.name == hashString("COL_POROSITY")) && (pId.component == CompIndep) && (pId.boundState == BoundStateIndep) - && (pId.reaction == ReactionIndep) && (pId.section == SectionIndep) && (pId.particleType == ParTypeIndep)) + if (_singlePorosity && (pId.name == hashString("COL_POROSITY")) && (pId.component == CompIndep) && + (pId.boundState == BoundStateIndep) && (pId.reaction == ReactionIndep) && (pId.section == SectionIndep) && + (pId.particleType == ParTypeIndep)) { if (contains(sensParams, &_colPorosities[0])) { @@ -1387,7 +1527,8 @@ bool TwoDimensionalConvectionDispersionOperator::setSensitiveParameterValue(cons } } - if (_singleVelocity && (pId.name == hashString("VELOCITY")) && (pId.component == CompIndep) && (pId.boundState == BoundStateIndep) && (pId.reaction == ReactionIndep)) + if (_singleVelocity && (pId.name == hashString("VELOCITY")) && (pId.component == CompIndep) && + (pId.boundState == BoundStateIndep) && (pId.reaction == ReactionIndep)) { if (_velocity.size() > _nRad) { @@ -1409,21 +1550,26 @@ bool TwoDimensionalConvectionDispersionOperator::setSensitiveParameterValue(cons } } - const bool ad = multiplexParameterValue(pId, hashString("COL_DISPERSION"), _axialDispersionMode, _axialDispersion, _nComp, _nRad, value, &sensParams); + const bool ad = multiplexParameterValue(pId, hashString("COL_DISPERSION"), _axialDispersionMode, _axialDispersion, + _nComp, _nRad, value, &sensParams); if (ad) return true; - const bool adr = multiplexParameterValue(pId, hashString("COL_DISPERSION_RADIAL"), _radialDispersionMode, _radialDispersion, _nComp, _nRad, value, &sensParams); + const bool adr = multiplexParameterValue(pId, hashString("COL_DISPERSION_RADIAL"), _radialDispersionMode, + _radialDispersion, _nComp, _nRad, value, &sensParams); if (adr) return true; return false; } -bool TwoDimensionalConvectionDispersionOperator::setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, unsigned int adDirection, double adValue) +bool TwoDimensionalConvectionDispersionOperator::setSensitiveParameter(std::unordered_set& sensParams, + const ParameterId& pId, unsigned int adDirection, + double adValue) { - if (_singlePorosity && (pId.name == hashString("COL_POROSITY")) && (pId.component == CompIndep) && (pId.boundState == BoundStateIndep) - && (pId.reaction == ReactionIndep) && (pId.section == SectionIndep) && (pId.particleType == ParTypeIndep)) + if (_singlePorosity && (pId.name == hashString("COL_POROSITY")) && (pId.component == CompIndep) && + (pId.boundState == BoundStateIndep) && (pId.reaction == ReactionIndep) && (pId.section == SectionIndep) && + (pId.particleType == ParTypeIndep)) { sensParams.insert(&_colPorosities[0]); for (unsigned int i = 0; i < _nRad; ++i) @@ -1432,7 +1578,8 @@ bool TwoDimensionalConvectionDispersionOperator::setSensitiveParameter(std::unor return true; } - if (_singleVelocity && (pId.name == hashString("VELOCITY")) && (pId.component == CompIndep) && (pId.boundState == BoundStateIndep) && (pId.reaction == ReactionIndep)) + if (_singleVelocity && (pId.name == hashString("VELOCITY")) && (pId.component == CompIndep) && + (pId.boundState == BoundStateIndep) && (pId.reaction == ReactionIndep)) { if (_velocity.size() > _nRad) { @@ -1456,19 +1603,21 @@ bool TwoDimensionalConvectionDispersionOperator::setSensitiveParameter(std::unor } } - const bool ad = multiplexParameterAD(pId, hashString("COL_DISPERSION"), _axialDispersionMode, _axialDispersion, _nComp, _nRad, adDirection, adValue, sensParams); + const bool ad = multiplexParameterAD(pId, hashString("COL_DISPERSION"), _axialDispersionMode, _axialDispersion, + _nComp, _nRad, adDirection, adValue, sensParams); if (ad) return true; - const bool adr = multiplexParameterAD(pId, hashString("COL_DISPERSION_RADIAL"), _radialDispersionMode, _radialDispersion, _nComp, _nRad, adDirection, adValue, sensParams); + const bool adr = multiplexParameterAD(pId, hashString("COL_DISPERSION_RADIAL"), _radialDispersionMode, + _radialDispersion, _nComp, _nRad, adDirection, adValue, sensParams); if (adr) return true; return false; } -} // namespace parts +} // namespace parts -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/parts/TwoDimensionalConvectionDispersionOperator.hpp b/src/libcadet/model/parts/TwoDimensionalConvectionDispersionOperator.hpp index d17581d5a..3784466fa 100644 --- a/src/libcadet/model/parts/TwoDimensionalConvectionDispersionOperator.hpp +++ b/src/libcadet/model/parts/TwoDimensionalConvectionDispersionOperator.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines the 2D convection dispersion transport operator. */ @@ -48,10 +48,11 @@ namespace parts /** * @brief 2D Convection dispersion transport operator * @details Implements the equation - * + * * @f[\begin{align} - \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i}(\rho) \frac{\partial^2 c_i}{\partial z^2} + \frac{1}{\rho} \frac{\partial}{\partial \rho} \left( \rho D_{\rho} \frac{\partial c_i}{\partial \rho} \right) \\ -\end{align} @f] + \frac{\partial c_i}{\partial t} &= - u \frac{\partial c_i}{\partial z} + D_{\text{ax},i}(\rho) \frac{\partial^2 +c_i}{\partial z^2} + \frac{1}{\rho} \frac{\partial}{\partial \rho} \left( \rho D_{\rho} \frac{\partial c_i}{\partial +\rho} \right) \\ \end{align} @f] * with Danckwerts boundary conditions on the axial boundary (see @cite Danckwerts1953) @f[ \begin{align} u c_{\text{in},i}(t) &= u c_i(t,0,\rho) - D_{\text{ax},i}(\rho) \frac{\partial c_i}{\partial z}(t,0,\rho) \\ @@ -62,26 +63,32 @@ u c_{\text{in},i}(t) &= u c_i(t,0,\rho) - D_{\text{ax},i}(\rho) \frac{\partial c \frac{\partial c_i}{\partial \rho}(t,z,0) &= 0 \\ \frac{\partial c_i}{\partial z}(t,z,R) &= 0 \end{align} @f] - * Methods are described in @cite VonLieres2010a (WENO, linear solver), and @cite Puttmann2013, @cite Puttmann2016 (forward sensitivities, AD, band compression) + * Methods are described in @cite VonLieres2010a (WENO, linear solver), and @cite Puttmann2013, @cite Puttmann2016 +(forward sensitivities, AD, band compression) */ class TwoDimensionalConvectionDispersionOperator { public: - TwoDimensionalConvectionDispersionOperator(); ~TwoDimensionalConvectionDispersionOperator() CADET_NOEXCEPT; void setFlowRates(int compartment, const active& in, const active& out) CADET_NOEXCEPT; void setFlowRates(active const* in, active const* out) CADET_NOEXCEPT; - bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, unsigned int nComp, unsigned int nCol, unsigned int nRad, bool dynamicReactions); - bool configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, std::unordered_map& parameters); + bool configureModelDiscretization(IParameterProvider& paramProvider, const IConfigHelper& helper, + unsigned int nComp, unsigned int nCol, unsigned int nRad, bool dynamicReactions); + bool configure(UnitOpIdx unitOpIdx, IParameterProvider& paramProvider, + std::unordered_map& parameters); bool notifyDiscontinuousSectionTransition(double t, unsigned int secIdx); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, bool wantJac, WithoutParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithoutParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity); - int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, bool wantJac, WithParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, double* res, + bool wantJac, WithoutParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, + bool wantJac, WithoutParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, active const* y, double const* yDot, active* res, + bool wantJac, WithParamSensitivity); + int residual(const IModel& model, double t, unsigned int secIdx, double const* y, double const* yDot, active* res, + bool wantJac, WithParamSensitivity); bool solveTimeDerivativeSystem(const SimulationTime& simTime, double* const rhs); void multiplyWithDerivativeJacobian(const SimulationTime& simTime, double const* sDot, double* ret) const; @@ -90,28 +97,61 @@ class TwoDimensionalConvectionDispersionOperator bool solveDiscretizedJacobian(double* rhs, double const* weight, double const* init, double outerTol) const; bool setParameter(const ParameterId& pId, double value); - bool setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, unsigned int adDirection, double adValue); + bool setSensitiveParameter(std::unordered_set& sensParams, const ParameterId& pId, + unsigned int adDirection, double adValue); bool setSensitiveParameterValue(const std::unordered_set& sensParams, const ParameterId& id, double value); - inline const active& columnLength() const CADET_NOEXCEPT { return _colLength; } - inline const active& columnRadius() const CADET_NOEXCEPT { return _colRadius; } - inline const active& currentVelocity(int idx) const CADET_NOEXCEPT { return _curVelocity[idx]; } - inline const active& columnPorosity(int idx) const CADET_NOEXCEPT { return _colPorosities[idx]; } - inline const active& crossSection(int idx) const CADET_NOEXCEPT { return _crossSections[idx]; } - inline active const* crossSections() const CADET_NOEXCEPT { return _crossSections.data(); } - inline active const* radialCenters() const CADET_NOEXCEPT { return _radialCenters.data(); } -// inline active const* radialCentroids() const CADET_NOEXCEPT { return _radialCentroids.data(); } - inline active const* radialEdges() const CADET_NOEXCEPT { return _radialEdges.data(); } - inline bool isCurrentFlowForward(int idx) const CADET_NOEXCEPT { return _curVelocity[idx] >= 0.0; } + inline const active& columnLength() const CADET_NOEXCEPT + { + return _colLength; + } + inline const active& columnRadius() const CADET_NOEXCEPT + { + return _colRadius; + } + inline const active& currentVelocity(int idx) const CADET_NOEXCEPT + { + return _curVelocity[idx]; + } + inline const active& columnPorosity(int idx) const CADET_NOEXCEPT + { + return _colPorosities[idx]; + } + inline const active& crossSection(int idx) const CADET_NOEXCEPT + { + return _crossSections[idx]; + } + inline active const* crossSections() const CADET_NOEXCEPT + { + return _crossSections.data(); + } + inline active const* radialCenters() const CADET_NOEXCEPT + { + return _radialCenters.data(); + } + // inline active const* radialCentroids() const CADET_NOEXCEPT { return _radialCentroids.data(); } + inline active const* radialEdges() const CADET_NOEXCEPT + { + return _radialEdges.data(); + } + inline bool isCurrentFlowForward(int idx) const CADET_NOEXCEPT + { + return _curVelocity[idx] >= 0.0; + } const active& axialDispersion(unsigned int idxSec, int idxRad, int idxComp) const CADET_NOEXCEPT; const active& radialDispersion(unsigned int idxSec, int idxRad, int idxComp) const CADET_NOEXCEPT; double inletFactor(unsigned int idxSec, int idxRad) const CADET_NOEXCEPT; - inline linalg::CompressedSparseMatrix& jacobian() CADET_NOEXCEPT { return _jacC; } - inline const linalg::CompressedSparseMatrix& jacobian() const CADET_NOEXCEPT { return _jacC; } + inline linalg::CompressedSparseMatrix& jacobian() CADET_NOEXCEPT + { + return _jacC; + } + inline const linalg::CompressedSparseMatrix& jacobian() const CADET_NOEXCEPT + { + return _jacC; + } protected: - class LinearSolver; class GmresSolver; template class SparseDirectSolver; @@ -122,7 +162,8 @@ class TwoDimensionalConvectionDispersionOperator void assembleDiscretizedJacobian(double alpha); template - int residualImpl(const IModel& model, double t, unsigned int secIdx, StateType const* y, double const* yDot, ResidualType* res); + int residualImpl(const IModel& model, double t, unsigned int secIdx, StateType const* y, double const* yDot, + ResidualType* res); void setSparsityPattern(); @@ -149,38 +190,39 @@ class TwoDimensionalConvectionDispersionOperator UserDefined }; - unsigned int _nComp; //!< Number of components - unsigned int _nCol; //!< Number of axial cells - unsigned int _nRad; //!< Number of radial cells - bool _hasDynamicReactions; //!< Determines whether the model has dynamic reactions (only relevant for sparsity pattern) + unsigned int _nComp; //!< Number of components + unsigned int _nCol; //!< Number of axial cells + unsigned int _nRad; //!< Number of radial cells + bool _hasDynamicReactions; //!< Determines whether the model has dynamic reactions (only relevant for sparsity + //!< pattern) - active _colLength; //!< Column length \f$ L \f$ - active _colRadius; //!< Column radius \f$ r_c \f$ - std::vector _radialEdges; //!< Boundaries of the radial compartments + active _colLength; //!< Column length \f$ L \f$ + active _colRadius; //!< Column radius \f$ r_c \f$ + std::vector _radialEdges; //!< Boundaries of the radial compartments std::vector _radialCenters; //!< Center of each radial compartment -// std::vector _radialCentroids; //!< Center of mass of each radial compartment + // std::vector _radialCentroids; //!< Center of mass of each radial compartment RadialDiscretizationMode _radialDiscretizationMode; - std::vector _crossSections; //!< Cross section area of each compartment + std::vector _crossSections; //!< Cross section area of each compartment std::vector _colPorosities; //!< Bulk porosity for each compartment - bool _singlePorosity; //!< Determines whether only one porosity for all compartments is given + bool _singlePorosity; //!< Determines whether only one porosity for all compartments is given - std::vector _axialDispersion; //!< Axial dispersion coefficient \f$ D_{\text{ax}} \f$ - MultiplexMode _axialDispersionMode; //!< Multiplex mode of the axial dispersion + std::vector _axialDispersion; //!< Axial dispersion coefficient \f$ D_{\text{ax}} \f$ + MultiplexMode _axialDispersionMode; //!< Multiplex mode of the axial dispersion std::vector _radialDispersion; //!< Radial dispersion coefficient \f$ D_{\rho} \f$ - MultiplexMode _radialDispersionMode; //!< Multiplex mode of the radial dispersion - std::vector _velocity; //!< Interstitial velocity parameter - std::vector _curVelocity; //!< Current interstitial velocity \f$ u \f$ - std::vector _dir; //!< Current flow direction - bool _singleVelocity; //!< Determines whether only one velocity for all compartments is given + MultiplexMode _radialDispersionMode; //!< Multiplex mode of the radial dispersion + std::vector _velocity; //!< Interstitial velocity parameter + std::vector _curVelocity; //!< Current interstitial velocity \f$ u \f$ + std::vector _dir; //!< Current flow direction + bool _singleVelocity; //!< Determines whether only one velocity for all compartments is given ArrayPool _stencilMemory; //!< Provides memory for the stencil double* _wenoDerivatives; //!< Holds derivatives of the WENO scheme - Weno _weno; //!< The WENO scheme implementation - double _wenoEpsilon; //!< The @f$ \varepsilon @f$ of the WENO scheme (prevents division by zero) + Weno _weno; //!< The WENO scheme implementation + double _wenoEpsilon; //!< The @f$ \varepsilon @f$ of the WENO scheme (prevents division by zero) linalg::CompressedSparseMatrix _jacC; //!< Jacobian - LinearSolver* _linearSolver; //!< Solves linear system with time discretized Jacobian + LinearSolver* _linearSolver; //!< Solves linear system with time discretized Jacobian IParameterParameterDependence* _dispersionDep; }; @@ -189,4 +231,4 @@ class TwoDimensionalConvectionDispersionOperator } // namespace model } // namespace cadet -#endif // LIBCADET_2DCONVECTIONDISPERSIONOPERATOR_HPP_ +#endif // LIBCADET_2DCONVECTIONDISPERSIONOPERATOR_HPP_ diff --git a/src/libcadet/model/reaction/DummyReaction.cpp b/src/libcadet/model/reaction/DummyReaction.cpp index 7b6374d28..3e6d346cc 100644 --- a/src/libcadet/model/reaction/DummyReaction.cpp +++ b/src/libcadet/model/reaction/DummyReaction.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -27,91 +27,204 @@ namespace model class DummyDynamicReaction : public IDynamicReactionModel { public: + virtual ~DummyDynamicReaction() CADET_NOEXCEPT + { + } - virtual ~DummyDynamicReaction() CADET_NOEXCEPT { } - - static const char* identifier() { return "NONE"; } - virtual const char* name() const CADET_NOEXCEPT { return "NONE"; } + static const char* identifier() + { + return "NONE"; + } + virtual const char* name() const CADET_NOEXCEPT + { + return "NONE"; + } - virtual bool requiresConfiguration() const CADET_NOEXCEPT { return false; } - virtual bool usesParamProviderInDiscretizationConfig() const CADET_NOEXCEPT { return false; } + virtual bool requiresConfiguration() const CADET_NOEXCEPT + { + return false; + } + virtual bool usesParamProviderInDiscretizationConfig() const CADET_NOEXCEPT + { + return false; + } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { return true; } - virtual bool configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) { return true; } + virtual bool configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) + { + return true; + } - virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) { } + virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) + { + } - virtual bool hasParameter(const ParameterId& pId) const { return false; } + virtual bool hasParameter(const ParameterId& pId) const + { + return false; + } - virtual std::unordered_map getAllParameterValues() const { return std::unordered_map(); } + virtual std::unordered_map getAllParameterValues() const + { + return std::unordered_map(); + } - virtual bool setParameter(const ParameterId& pId, int value) { return false; } - virtual bool setParameter(const ParameterId& pId, double value) { return false; } - virtual bool setParameter(const ParameterId& pId, bool value) { return false; } + virtual bool setParameter(const ParameterId& pId, int value) + { + return false; + } + virtual bool setParameter(const ParameterId& pId, double value) + { + return false; + } + virtual bool setParameter(const ParameterId& pId, bool value) + { + return false; + } - virtual active* getParameter(const ParameterId& pId) { return nullptr; } + virtual active* getParameter(const ParameterId& pId) + { + return nullptr; + } - virtual bool dependsOnTime() const CADET_NOEXCEPT { return false; } - virtual bool requiresWorkspace() const CADET_NOEXCEPT { return false; } - virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT + virtual bool dependsOnTime() const CADET_NOEXCEPT + { + return false; + } + virtual bool requiresWorkspace() const CADET_NOEXCEPT + { + return false; + } + virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT { return 0; } virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, - active* res, const active& factor, LinearBufferAllocator workSpace) const { return 0; } + active* res, const active& factor, LinearBufferAllocator workSpace) const + { + return 0; + } virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, - active* res, double factor, LinearBufferAllocator workSpace) const { return 0; } + active* res, double factor, LinearBufferAllocator workSpace) const + { + return 0; + } virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, - active* res, double factor, LinearBufferAllocator workSpace) const { return 0; } + active* res, double factor, LinearBufferAllocator workSpace) const + { + return 0; + } virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, - double* res, double factor, LinearBufferAllocator workSpace) const { return 0; } + double* res, double factor, LinearBufferAllocator workSpace) const + { + return 0; + } - virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double factor, linalg::BandMatrix::RowIterator jac, LinearBufferAllocator workSpace) const { } - virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double factor, linalg::DenseBandedRowIterator jac, LinearBufferAllocator workSpace) const { } - virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double factor, linalg::BandedSparseRowIterator jac, LinearBufferAllocator workSpace) const { } - #ifdef ENABLE_DG - virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double factor, linalg::BandedEigenSparseRowIterator jac, LinearBufferAllocator workSpace) const { } - #endif + virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + double factor, linalg::BandMatrix::RowIterator jac, + LinearBufferAllocator workSpace) const + { + } + virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + double factor, linalg::DenseBandedRowIterator jac, + LinearBufferAllocator workSpace) const + { + } + virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + double factor, linalg::BandedSparseRowIterator jac, + LinearBufferAllocator workSpace) const + { + } +#ifdef ENABLE_DG + virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, + double factor, linalg::BandedEigenSparseRowIterator jac, + LinearBufferAllocator workSpace) const + { + } +#endif - virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* yLiquid, active const* ySolid, - active* resLiquid, active* resSolid, double factor, LinearBufferAllocator workSpace) const { return 0; } + virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* yLiquid, + active const* ySolid, active* resLiquid, active* resSolid, double factor, + LinearBufferAllocator workSpace) const + { + return 0; + } - virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, - active* resLiquid, active* resSolid, double factor, LinearBufferAllocator workSpace) const { return 0; } + virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, + double const* ySolid, active* resLiquid, active* resSolid, double factor, + LinearBufferAllocator workSpace) const + { + return 0; + } - virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, - double* resLiquid, double* resSolid, double factor, LinearBufferAllocator workSpace) const { return 0; } + virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, + double const* ySolid, double* resLiquid, double* resSolid, double factor, + LinearBufferAllocator workSpace) const + { + return 0; + } - virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, double factor, linalg::BandMatrix::RowIterator jacLiquid, linalg::BandMatrix::RowIterator jacSolid, LinearBufferAllocator workSpace) const { } - virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, double factor, linalg::DenseBandedRowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, LinearBufferAllocator workSpace) const { } - virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, double factor, linalg::BandMatrix::RowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, LinearBufferAllocator workSpace) const { } - #ifdef ENABLE_DG - virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, double factor, linalg::BandedEigenSparseRowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, LinearBufferAllocator workSpace) const { } - #endif + virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yLiquid, double const* ySolid, double factor, + linalg::BandMatrix::RowIterator jacLiquid, + linalg::BandMatrix::RowIterator jacSolid, + LinearBufferAllocator workSpace) const + { + } + virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yLiquid, double const* ySolid, double factor, + linalg::DenseBandedRowIterator jacLiquid, + linalg::DenseBandedRowIterator jacSolid, + LinearBufferAllocator workSpace) const + { + } + virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yLiquid, double const* ySolid, double factor, + linalg::BandMatrix::RowIterator jacLiquid, + linalg::DenseBandedRowIterator jacSolid, + LinearBufferAllocator workSpace) const + { + } +#ifdef ENABLE_DG + virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, + double const* yLiquid, double const* ySolid, double factor, + linalg::BandedEigenSparseRowIterator jacLiquid, + linalg::DenseBandedRowIterator jacSolid, + LinearBufferAllocator workSpace) const + { + } +#endif - virtual unsigned int numReactionsLiquid() const CADET_NOEXCEPT { return 0; } - virtual unsigned int numReactionsCombined() const CADET_NOEXCEPT { return 0; } + virtual unsigned int numReactionsLiquid() const CADET_NOEXCEPT + { + return 0; + } + virtual unsigned int numReactionsCombined() const CADET_NOEXCEPT + { + return 0; + } protected: }; - namespace reaction { - void registerDummyReaction(std::unordered_map>& reactions) - { - reactions[DummyDynamicReaction::identifier()] = []() { return new DummyDynamicReaction(); }; - reactions["DUMMY"] = []() { return new DummyDynamicReaction(); }; - } -} // namespace reaction +void registerDummyReaction(std::unordered_map>& reactions) +{ + reactions[DummyDynamicReaction::identifier()] = []() { return new DummyDynamicReaction(); }; + reactions["DUMMY"] = []() { return new DummyDynamicReaction(); }; +} +} // namespace reaction } // namespace model } // namespace cadet diff --git a/src/libcadet/model/reaction/MassActionLawReaction.cpp b/src/libcadet/model/reaction/MassActionLawReaction.cpp index c4344231e..caba77ed0 100644 --- a/src/libcadet/model/reaction/MassActionLawReaction.cpp +++ b/src/libcadet/model/reaction/MassActionLawReaction.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -58,266 +58,305 @@ namespace cadet namespace model { -inline const char* MassActionLawParamHandler::identifier() CADET_NOEXCEPT { return "MASS_ACTION_LAW"; } +inline const char* MassActionLawParamHandler::identifier() CADET_NOEXCEPT +{ + return "MASS_ACTION_LAW"; +} -inline bool MassActionLawParamHandler::validateConfig(unsigned int nReactions, unsigned int nComp, unsigned int const* nBoundStates) +inline bool MassActionLawParamHandler::validateConfig(unsigned int nReactions, unsigned int nComp, + unsigned int const* nBoundStates) { return true; } -inline const char* ExtMassActionLawParamHandler::identifier() CADET_NOEXCEPT { return "EXT_MASS_ACTION_LAW"; } +inline const char* ExtMassActionLawParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_MASS_ACTION_LAW"; +} -inline bool ExtMassActionLawParamHandler::validateConfig(unsigned int nReactions, unsigned int nComp, unsigned int const* nBoundStates) +inline bool ExtMassActionLawParamHandler::validateConfig(unsigned int nReactions, unsigned int nComp, + unsigned int const* nBoundStates) { return true; } - namespace { - /** - * @brief Registers a matrix-valued parameter (row-major storage) with bound states as rows - * @details The matrix-valued parameter has as many rows as there are bound states in the system. - * @param [in,out] parameters Parameter map - * @param [in] unitOpIdx Unit operation id - * @param [in] parTypeIdx Particle type index - * @param [in] paramName Name of the parameter - * @param [in] mat Matrix to register - * @param [in] nComp Number of components - * @param [in] boundOffset Array with offsets to bound states of a specific component - */ - inline void registerBoundStateRowMatrix(std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, const std::string& paramName, cadet::linalg::ActiveDenseMatrix& mat, - unsigned int nComp, unsigned int const* boundOffset) - { - const cadet::StringHash hashName = cadet::hashStringRuntime(paramName); - cadet::registerParam2DArray(parameters, mat.data(), mat.elements(), [=](bool multi, unsigned int row, unsigned int col) - { - const unsigned int comp = std::lower_bound(boundOffset, boundOffset + nComp, row) - boundOffset; - const unsigned int bnd = row - boundOffset[comp]; - return cadet::makeParamId(hashName, unitOpIdx, comp, parTypeIdx, bnd, col, cadet::SectionIndep); - }, - mat.columns() - ); - } +/** + * @brief Registers a matrix-valued parameter (row-major storage) with bound states as rows + * @details The matrix-valued parameter has as many rows as there are bound states in the system. + * @param [in,out] parameters Parameter map + * @param [in] unitOpIdx Unit operation id + * @param [in] parTypeIdx Particle type index + * @param [in] paramName Name of the parameter + * @param [in] mat Matrix to register + * @param [in] nComp Number of components + * @param [in] boundOffset Array with offsets to bound states of a specific component + */ +inline void registerBoundStateRowMatrix(std::unordered_map& parameters, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx, const std::string& paramName, + cadet::linalg::ActiveDenseMatrix& mat, unsigned int nComp, + unsigned int const* boundOffset) +{ + const cadet::StringHash hashName = cadet::hashStringRuntime(paramName); + cadet::registerParam2DArray( + parameters, mat.data(), mat.elements(), + [=](bool multi, unsigned int row, unsigned int col) { + const unsigned int comp = std::lower_bound(boundOffset, boundOffset + nComp, row) - boundOffset; + const unsigned int bnd = row - boundOffset[comp]; + return cadet::makeParamId(hashName, unitOpIdx, comp, parTypeIdx, bnd, col, cadet::SectionIndep); + }, + mat.columns()); +} - /** - * @brief Registers a matrix-valued parameter (row-major storage) with components as rows - * @details The matrix-valued parameter has as many rows as there are components in the system. - * @param [in,out] parameters Parameter map - * @param [in] unitOpIdx Unit operation id - * @param [in] parTypeIdx Particle type index - * @param [in] paramName Name of the parameter - * @param [in] mat Matrix to register - */ - inline void registerCompRowMatrix(std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, const std::string& paramName, cadet::linalg::ActiveDenseMatrix& mat) - { - const cadet::StringHash hashName = cadet::hashStringRuntime(paramName); - cadet::registerParam2DArray(parameters, mat.data(), mat.elements(), [=](bool multi, unsigned int row, unsigned int col) - { - return cadet::makeParamId(hashName, unitOpIdx, row, parTypeIdx, cadet::BoundStateIndep, col, cadet::SectionIndep); - }, - mat.columns() - ); - } +/** + * @brief Registers a matrix-valued parameter (row-major storage) with components as rows + * @details The matrix-valued parameter has as many rows as there are components in the system. + * @param [in,out] parameters Parameter map + * @param [in] unitOpIdx Unit operation id + * @param [in] parTypeIdx Particle type index + * @param [in] paramName Name of the parameter + * @param [in] mat Matrix to register + */ +inline void registerCompRowMatrix(std::unordered_map& parameters, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx, const std::string& paramName, + cadet::linalg::ActiveDenseMatrix& mat) +{ + const cadet::StringHash hashName = cadet::hashStringRuntime(paramName); + cadet::registerParam2DArray( + parameters, mat.data(), mat.elements(), + [=](bool multi, unsigned int row, unsigned int col) { + return cadet::makeParamId(hashName, unitOpIdx, row, parTypeIdx, cadet::BoundStateIndep, col, + cadet::SectionIndep); + }, + mat.columns()); +} - /** - * @brief Reads reaction rate exponents and registers them - * @details Reads a matrix-valued parameter into the given pre-allocated matrix and registers the - * parameters in the map. If @p boundOffset is @c nullptr, the matrix has as many columns - * as there are components. Otherwise, the matrix has as many columns as there are bound states. - * @param [in] paramProvider Parameter provider - * @param [in,out] parameters Parameter map - * @param [in] unitOpIdx Unit operation id - * @param [in] parTypeIdx Particle type index - * @param [in] paramName Name of the parameter - * @param [out] mat Matrix that holds the values (pre-allocated) - * @param [in] nComp Number of components - * @param [in] boundOffset Array with offsets to bound states of a specific component, or @c nullptr - */ - inline void readAndRegisterExponents(cadet::IParameterProvider& paramProvider, std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, - const std::string& paramName, cadet::linalg::ActiveDenseMatrix& mat, unsigned int nComp, unsigned int const* boundOffset) +/** + * @brief Reads reaction rate exponents and registers them + * @details Reads a matrix-valued parameter into the given pre-allocated matrix and registers the + * parameters in the map. If @p boundOffset is @c nullptr, the matrix has as many columns + * as there are components. Otherwise, the matrix has as many columns as there are bound states. + * @param [in] paramProvider Parameter provider + * @param [in,out] parameters Parameter map + * @param [in] unitOpIdx Unit operation id + * @param [in] parTypeIdx Particle type index + * @param [in] paramName Name of the parameter + * @param [out] mat Matrix that holds the values (pre-allocated) + * @param [in] nComp Number of components + * @param [in] boundOffset Array with offsets to bound states of a specific component, or @c nullptr + */ +inline void readAndRegisterExponents(cadet::IParameterProvider& paramProvider, + std::unordered_map& parameters, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx, const std::string& paramName, + cadet::linalg::ActiveDenseMatrix& mat, unsigned int nComp, + unsigned int const* boundOffset) +{ + if (paramProvider.exists(paramName)) { - if (paramProvider.exists(paramName)) - { - const std::vector s = paramProvider.getDoubleArray(paramName); - if (static_cast(s.size()) != mat.elements()) - throw InvalidParameterException("Expected " + paramName + " to be of size " + std::to_string(mat.elements()) + ""); + const std::vector s = paramProvider.getDoubleArray(paramName); + if (static_cast(s.size()) != mat.elements()) + throw InvalidParameterException("Expected " + paramName + " to be of size " + + std::to_string(mat.elements()) + ""); - std::copy(s.begin(), s.end(), mat.data()); - } - else - mat.setAll(0.0); + std::copy(s.begin(), s.end(), mat.data()); + } + else + mat.setAll(0.0); - if (boundOffset) - registerBoundStateRowMatrix(parameters, unitOpIdx, parTypeIdx, paramName, mat, nComp, boundOffset); + if (boundOffset) + registerBoundStateRowMatrix(parameters, unitOpIdx, parTypeIdx, paramName, mat, nComp, boundOffset); + else + registerCompRowMatrix(parameters, unitOpIdx, parTypeIdx, paramName, mat); +} + +/** + * @brief Calculate gradient of reaction rate (flux) in liquid phase + * @param [out] fluxGrad Array that holds the gradient of the reaction rate + * @param [in] r Index of the reaction + * @param [in] nComp Number of components + * @param [in] rate Rate constant + * @param [in] exponents Matrix with exponents in the rate law + * @param [in] y Array of concentrations + */ +inline void fluxGradLiquid(double* fluxGrad, unsigned int r, unsigned int nComp, double rate, + const cadet::linalg::ActiveDenseMatrix& exponents, double const* y) +{ + for (unsigned int c = 0; c < nComp; ++c) + { + if (cadet_unlikely(exponents.native(c, r) != 0.0)) + fluxGrad[c] = rate; else - registerCompRowMatrix(parameters, unitOpIdx, parTypeIdx, paramName, mat); + fluxGrad[c] = 0.0; } - /** - * @brief Calculate gradient of reaction rate (flux) in liquid phase - * @param [out] fluxGrad Array that holds the gradient of the reaction rate - * @param [in] r Index of the reaction - * @param [in] nComp Number of components - * @param [in] rate Rate constant - * @param [in] exponents Matrix with exponents in the rate law - * @param [in] y Array of concentrations - */ - inline void fluxGradLiquid(double* fluxGrad, unsigned int r, unsigned int nComp, double rate, const cadet::linalg::ActiveDenseMatrix& exponents, double const* y) + // Calculate gradient + for (unsigned int c = 0; c < nComp; ++c) { - for (unsigned int c = 0; c < nComp; ++c) + if (cadet_unlikely(exponents.native(c, r) != 0.0)) { - if (cadet_unlikely(exponents.native(c, r) != 0.0)) - fluxGrad[c] = rate; - else - fluxGrad[c] = 0.0; - } + const double exponentValue = static_cast(exponents.native(c, r)); + const double v = pow(y[c], exponentValue); + for (unsigned int j = 0; j < c; ++j) + fluxGrad[j] *= v; - // Calculate gradient - for (unsigned int c = 0; c < nComp; ++c) - { - if (cadet_unlikely(exponents.native(c, r) != 0.0)) - { - const double exponentValue = static_cast(exponents.native(c, r)); - const double v = pow(y[c], exponentValue); - for (unsigned int j = 0; j < c; ++j) - fluxGrad[j] *= v; - - fluxGrad[c] *= exponentValue * pow(y[c], exponentValue - 1.0); + fluxGrad[c] *= exponentValue * pow(y[c], exponentValue - 1.0); - for (unsigned int j = c + 1; j < nComp; ++j) - fluxGrad[j] *= v; - - } + for (unsigned int j = c + 1; j < nComp; ++j) + fluxGrad[j] *= v; } } +} - /** - * @brief Calculate gradient of reaction rate (flux) in liquid-solid phase cell - * @param [out] fluxGrad Array that holds the gradient of the reaction rate - * @param [in] r Index of the reaction - * @param [in] nComp Number of components - * @param [in] nTotalBoundStates Total number of bound states - * @param [in] rate Rate constant - * @param [in] expLiquid Matrix with exponents of the liquid phase concentrations in the rate law - * @param [in] expSolid Matrix with exponents of the solid phase concentrations in the rate law - * @param [in] yLiquid Array of liquid phase concentrations (points to first component of liquid phase concentrations) - * @param [in] ySolid Array of solid phase concentrations (points to first component of solid phase concentrations) - */ - inline void fluxGradCombined(double* fluxGrad, unsigned int r, unsigned int nComp, unsigned int nTotalBoundStates, double rate, - const cadet::linalg::ActiveDenseMatrix& expLiquid, const cadet::linalg::ActiveDenseMatrix& expSolid, double const* yLiquid, double const* ySolid) +/** + * @brief Calculate gradient of reaction rate (flux) in liquid-solid phase cell + * @param [out] fluxGrad Array that holds the gradient of the reaction rate + * @param [in] r Index of the reaction + * @param [in] nComp Number of components + * @param [in] nTotalBoundStates Total number of bound states + * @param [in] rate Rate constant + * @param [in] expLiquid Matrix with exponents of the liquid phase concentrations in the rate law + * @param [in] expSolid Matrix with exponents of the solid phase concentrations in the rate law + * @param [in] yLiquid Array of liquid phase concentrations (points to first component of liquid phase concentrations) + * @param [in] ySolid Array of solid phase concentrations (points to first component of solid phase concentrations) + */ +inline void fluxGradCombined(double* fluxGrad, unsigned int r, unsigned int nComp, unsigned int nTotalBoundStates, + double rate, const cadet::linalg::ActiveDenseMatrix& expLiquid, + const cadet::linalg::ActiveDenseMatrix& expSolid, double const* yLiquid, + double const* ySolid) +{ + for (unsigned int c = 0; c < nComp; ++c) { - for (unsigned int c = 0; c < nComp; ++c) - { - if (cadet_unlikely(expLiquid.native(c, r) != 0.0)) - fluxGrad[c] = rate; - else - fluxGrad[c] = 0.0; - } + if (cadet_unlikely(expLiquid.native(c, r) != 0.0)) + fluxGrad[c] = rate; + else + fluxGrad[c] = 0.0; + } - for (unsigned int c = 0; c < nTotalBoundStates; ++c) - { - if (cadet_unlikely(expSolid.native(c, r) != 0.0)) - fluxGrad[nComp + c] = rate; - else - fluxGrad[nComp + c] = 0.0; - } + for (unsigned int c = 0; c < nTotalBoundStates; ++c) + { + if (cadet_unlikely(expSolid.native(c, r) != 0.0)) + fluxGrad[nComp + c] = rate; + else + fluxGrad[nComp + c] = 0.0; + } - // Calculate gradient - for (unsigned int c = 0; c < nComp; ++c) + // Calculate gradient + for (unsigned int c = 0; c < nComp; ++c) + { + if (cadet_unlikely(expLiquid.native(c, r) != 0.0)) { - if (cadet_unlikely(expLiquid.native(c, r) != 0.0)) - { - const double exponentValue = static_cast(expLiquid.native(c, r)); - const double v = pow(yLiquid[c], exponentValue); - for (unsigned int j = 0; j < c; ++j) - fluxGrad[j] *= v; + const double exponentValue = static_cast(expLiquid.native(c, r)); + const double v = pow(yLiquid[c], exponentValue); + for (unsigned int j = 0; j < c; ++j) + fluxGrad[j] *= v; - fluxGrad[c] *= exponentValue * pow(yLiquid[c], exponentValue - 1.0); + fluxGrad[c] *= exponentValue * pow(yLiquid[c], exponentValue - 1.0); - for (unsigned int j = c + 1; j < nComp + nTotalBoundStates; ++j) - fluxGrad[j] *= v; - - } + for (unsigned int j = c + 1; j < nComp + nTotalBoundStates; ++j) + fluxGrad[j] *= v; } + } - for (unsigned int c = 0; c < nTotalBoundStates; ++c) + for (unsigned int c = 0; c < nTotalBoundStates; ++c) + { + if (cadet_unlikely(expSolid.native(c, r) != 0.0)) { - if (cadet_unlikely(expSolid.native(c, r) != 0.0)) - { - const double exponentValue = static_cast(expSolid.native(c, r)); - const double v = pow(ySolid[c], exponentValue); - for (unsigned int j = 0; j < nComp + c; ++j) - fluxGrad[j] *= v; + const double exponentValue = static_cast(expSolid.native(c, r)); + const double v = pow(ySolid[c], exponentValue); + for (unsigned int j = 0; j < nComp + c; ++j) + fluxGrad[j] *= v; - fluxGrad[nComp + c] *= exponentValue * pow(ySolid[c], exponentValue - 1.0); + fluxGrad[nComp + c] *= exponentValue * pow(ySolid[c], exponentValue - 1.0); - for (unsigned int j = c + 1; j < nTotalBoundStates; ++j) - fluxGrad[nComp + j] *= v; - - } + for (unsigned int j = c + 1; j < nTotalBoundStates; ++j) + fluxGrad[nComp + j] *= v; } } +} - template - inline num_t rateConstantOrZero(const num_t& rate, unsigned int idxReaction, const cadet::linalg::ActiveDenseMatrix& exponents, unsigned int nComp) +template +inline num_t rateConstantOrZero(const num_t& rate, unsigned int idxReaction, + const cadet::linalg::ActiveDenseMatrix& exponents, unsigned int nComp) +{ + for (unsigned int c = 0; c < nComp; ++c) { - for (unsigned int c = 0; c < nComp; ++c) - { - if (cadet_unlikely(exponents.native(c, idxReaction) != 0.0)) - return rate; - } - return 0.0; + if (cadet_unlikely(exponents.native(c, idxReaction) != 0.0)) + return rate; } + return 0.0; +} - template - inline num_t rateConstantOrZero(const num_t& rate, unsigned int idxReaction, const cadet::linalg::ActiveDenseMatrix& expLiquid, const cadet::linalg::ActiveDenseMatrix& expSolid, unsigned int nComp, unsigned int nTotalBoundStates) +template +inline num_t rateConstantOrZero(const num_t& rate, unsigned int idxReaction, + const cadet::linalg::ActiveDenseMatrix& expLiquid, + const cadet::linalg::ActiveDenseMatrix& expSolid, unsigned int nComp, + unsigned int nTotalBoundStates) +{ + for (unsigned int c = 0; c < nComp; ++c) { - for (unsigned int c = 0; c < nComp; ++c) - { - if (cadet_unlikely(expLiquid.native(c, idxReaction) != 0.0)) - return rate; - } - for (unsigned int c = 0; c < nTotalBoundStates; ++c) - { - if (cadet_unlikely(expSolid.native(c, idxReaction) != 0.0)) - return rate; - } - return 0.0; + if (cadet_unlikely(expLiquid.native(c, idxReaction) != 0.0)) + return rate; } + for (unsigned int c = 0; c < nTotalBoundStates; ++c) + { + if (cadet_unlikely(expSolid.native(c, idxReaction) != 0.0)) + return rate; + } + return 0.0; } +} // namespace /** * @brief Defines the multi component Langmuir binding model - * @details Implements the Langmuir adsorption model: \f[ \begin{align} - * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} q_{\text{max},i} \left( 1 - \sum_j \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} q_i - * \end{align} \f] - * Multiple bound states are not supported. + * @details Implements the Langmuir adsorption model: \f[ \begin{align} + * \frac{\mathrm{d}q_i}{\mathrm{d}t} &= k_{a,i} c_{p,i} q_{\text{max},i} \left( 1 - \sum_j + * \frac{q_j}{q_{\text{max},j}} \right) - k_{d,i} q_i \end{align} \f] Multiple bound states are not supported. * Components without bound state (i.e., non-binding components) are supported. - * + * * See @cite Langmuir1916. * @tparam ParamHandler_t Type that can add support for external function dependence */ -template -class MassActionLawReactionBase : public DynamicReactionModelBase +template class MassActionLawReactionBase : public DynamicReactionModelBase { public: + MassActionLawReactionBase() + { + } + virtual ~MassActionLawReactionBase() CADET_NOEXCEPT + { + } - MassActionLawReactionBase() { } - virtual ~MassActionLawReactionBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } - virtual const char* name() const CADET_NOEXCEPT { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } + virtual const char* name() const CADET_NOEXCEPT + { + return ParamHandler_t::identifier(); + } - virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) { _paramHandler.setExternalFunctions(extFuns, size); } - virtual bool dependsOnTime() const CADET_NOEXCEPT { return ParamHandler_t::dependsOnTime(); } - virtual bool requiresWorkspace() const CADET_NOEXCEPT { return true; } - virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT + virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) { - return _paramHandler.cacheSize(maxNumReactions(), nComp, totalNumBoundStates) + std::max(maxNumReactions() * sizeof(active), 2 * (_nComp + totalNumBoundStates) * sizeof(double)); + _paramHandler.setExternalFunctions(extFuns, size); + } + virtual bool dependsOnTime() const CADET_NOEXCEPT + { + return ParamHandler_t::dependsOnTime(); + } + virtual bool requiresWorkspace() const CADET_NOEXCEPT + { + return true; + } + virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT + { + return _paramHandler.cacheSize(maxNumReactions(), nComp, totalNumBoundStates) + + std::max(maxNumReactions() * sizeof(active), 2 * (_nComp + totalNumBoundStates) * sizeof(double)); } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { DynamicReactionModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); @@ -325,7 +364,9 @@ class MassActionLawReactionBase : public DynamicReactionModelBase { const std::size_t numElements = paramProvider.numElements("MAL_STOICHIOMETRY_BULK"); if (numElements % nComp != 0) - throw InvalidParameterException("Size of field MAL_STOICHIOMETRY_BULK must be a positive multiple of NCOMP (" + std::to_string(nComp) + ")"); + throw InvalidParameterException( + "Size of field MAL_STOICHIOMETRY_BULK must be a positive multiple of NCOMP (" + + std::to_string(nComp) + ")"); const unsigned int nReactions = numElements / nComp; @@ -341,7 +382,9 @@ class MassActionLawReactionBase : public DynamicReactionModelBase { const std::size_t numElements = paramProvider.numElements("MAL_STOICHIOMETRY_LIQUID"); if (numElements % nComp != 0) - throw InvalidParameterException("Size of field MAL_STOICHIOMETRY_LIQUID must be a positive multiple of NCOMP (" + std::to_string(nComp) + ")"); + throw InvalidParameterException( + "Size of field MAL_STOICHIOMETRY_LIQUID must be a positive multiple of NCOMP (" + + std::to_string(nComp) + ")"); const unsigned int nReactions = numElements / nComp; @@ -357,7 +400,9 @@ class MassActionLawReactionBase : public DynamicReactionModelBase { const std::size_t numElements = paramProvider.numElements("MAL_STOICHIOMETRY_SOLID"); if (numElements % _nTotalBoundStates != 0) - throw InvalidParameterException("Size of field MAL_STOICHIOMETRY_SOLID must be a positive multiple of NTOTALBND (" + std::to_string(_nTotalBoundStates) + ")"); + throw InvalidParameterException( + "Size of field MAL_STOICHIOMETRY_SOLID must be a positive multiple of NTOTALBND (" + + std::to_string(_nTotalBoundStates) + ")"); const unsigned int nReactions = numElements / _nTotalBoundStates; @@ -372,8 +417,14 @@ class MassActionLawReactionBase : public DynamicReactionModelBase return true; } - virtual unsigned int numReactionsLiquid() const CADET_NOEXCEPT { return _stoichiometryBulk.columns(); } - virtual unsigned int numReactionsCombined() const CADET_NOEXCEPT { return _stoichiometryLiquid.columns() + _stoichiometrySolid.columns(); } + virtual unsigned int numReactionsLiquid() const CADET_NOEXCEPT + { + return _stoichiometryBulk.columns(); + } + virtual unsigned int numReactionsCombined() const CADET_NOEXCEPT + { + return _stoichiometryLiquid.columns() + _stoichiometrySolid.columns(); + } CADET_DYNAMICREACTIONMODEL_BOILERPLATE @@ -396,28 +447,42 @@ class MassActionLawReactionBase : public DynamicReactionModelBase linalg::ActiveDenseMatrix _expSolidFwdLiquid; linalg::ActiveDenseMatrix _expSolidBwdLiquid; - inline int maxNumReactions() const CADET_NOEXCEPT { return std::max(std::max(_stoichiometryBulk.columns(), _stoichiometryLiquid.columns()), _stoichiometrySolid.columns()); } + inline int maxNumReactions() const CADET_NOEXCEPT + { + return std::max(std::max(_stoichiometryBulk.columns(), _stoichiometryLiquid.columns()), + _stoichiometrySolid.columns()); + } virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) { _paramHandler.configure(paramProvider, maxNumReactions(), _nComp, _nBoundStates); _paramHandler.registerParameters(_parameters, unitOpIdx, parTypeIdx, _nComp, _nBoundStates); - if ((_stoichiometryBulk.columns() > 0) && ((static_cast(_paramHandler.kFwdBulk().size()) < _stoichiometryBulk.columns()) || (static_cast(_paramHandler.kBwdBulk().size()) < _stoichiometryBulk.columns()))) - throw InvalidParameterException("MAL_KFWD_BULK and MAL_KBWD_BULK have to have the same size (number of reactions)"); + if ((_stoichiometryBulk.columns() > 0) && + ((static_cast(_paramHandler.kFwdBulk().size()) < _stoichiometryBulk.columns()) || + (static_cast(_paramHandler.kBwdBulk().size()) < _stoichiometryBulk.columns()))) + throw InvalidParameterException( + "MAL_KFWD_BULK and MAL_KBWD_BULK have to have the same size (number of reactions)"); - if ((_stoichiometryLiquid.columns() > 0) && ((static_cast(_paramHandler.kFwdLiquid().size()) < _stoichiometryLiquid.columns()) || (static_cast(_paramHandler.kBwdLiquid().size()) < _stoichiometryLiquid.columns()))) - throw InvalidParameterException("MAL_KFWD_LIQUID and MAL_KBWD_LIQUID have to have the same size (number of reactions)"); + if ((_stoichiometryLiquid.columns() > 0) && + ((static_cast(_paramHandler.kFwdLiquid().size()) < _stoichiometryLiquid.columns()) || + (static_cast(_paramHandler.kBwdLiquid().size()) < _stoichiometryLiquid.columns()))) + throw InvalidParameterException( + "MAL_KFWD_LIQUID and MAL_KBWD_LIQUID have to have the same size (number of reactions)"); - if ((_stoichiometrySolid.columns() > 0) && ((static_cast(_paramHandler.kFwdSolid().size()) < _stoichiometrySolid.columns()) || (static_cast(_paramHandler.kBwdSolid().size()) < _stoichiometrySolid.columns()))) - throw InvalidParameterException("MAL_KFWD_SOLID and MAL_KBWD_SOLID have to have the same size (number of reactions)"); + if ((_stoichiometrySolid.columns() > 0) && + ((static_cast(_paramHandler.kFwdSolid().size()) < _stoichiometrySolid.columns()) || + (static_cast(_paramHandler.kBwdSolid().size()) < _stoichiometrySolid.columns()))) + throw InvalidParameterException( + "MAL_KFWD_SOLID and MAL_KBWD_SOLID have to have the same size (number of reactions)"); if (paramProvider.exists("MAL_STOICHIOMETRY_BULK")) { const std::vector s = paramProvider.getDoubleArray("MAL_STOICHIOMETRY_BULK"); if (static_cast(s.size()) != _stoichiometryBulk.elements()) - throw InvalidParameterException("MAL_STOICHIOMETRY_BULK has changed size (number of reactions changed)"); + throw InvalidParameterException( + "MAL_STOICHIOMETRY_BULK has changed size (number of reactions changed)"); std::copy(s.begin(), s.end(), _stoichiometryBulk.data()); } @@ -427,14 +492,17 @@ class MassActionLawReactionBase : public DynamicReactionModelBase { const std::vector s = paramProvider.getDoubleArray("MAL_EXPONENTS_BULK_FWD"); if (static_cast(s.size()) != _stoichiometryBulk.elements()) - throw InvalidParameterException("Expected MAL_EXPONENTS_BULK_FWD and MAL_STOICHIOMETRY_BULK to be of the same size (" + std::to_string(_stoichiometryBulk.elements()) + ")"); + throw InvalidParameterException( + "Expected MAL_EXPONENTS_BULK_FWD and MAL_STOICHIOMETRY_BULK to be of the same size (" + + std::to_string(_stoichiometryBulk.elements()) + ")"); std::copy(s.begin(), s.end(), _expBulkFwd.data()); } else { // Obtain default values from stoichiometry - std::transform(_stoichiometryBulk.data(), _stoichiometryBulk.data() + _stoichiometryBulk.elements(), _expBulkFwd.data(), [](const active& v) { return std::max(0.0, -static_cast(v)); }); + std::transform(_stoichiometryBulk.data(), _stoichiometryBulk.data() + _stoichiometryBulk.elements(), + _expBulkFwd.data(), [](const active& v) { return std::max(0.0, -static_cast(v)); }); } registerCompRowMatrix(_parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_BULK_FWD", _expBulkFwd); @@ -442,24 +510,27 @@ class MassActionLawReactionBase : public DynamicReactionModelBase { const std::vector s = paramProvider.getDoubleArray("MAL_EXPONENTS_BULK_BWD"); if (static_cast(s.size()) != _stoichiometryBulk.elements()) - throw InvalidParameterException("Expected MAL_EXPONENTS_BULK_BWD and MAL_STOICHIOMETRY_BULK to be of the same size (" + std::to_string(_stoichiometryBulk.elements()) + ")"); + throw InvalidParameterException( + "Expected MAL_EXPONENTS_BULK_BWD and MAL_STOICHIOMETRY_BULK to be of the same size (" + + std::to_string(_stoichiometryBulk.elements()) + ")"); std::copy(s.begin(), s.end(), _expBulkBwd.data()); } else { // Obtain default values from stoichiometry - std::transform(_stoichiometryBulk.data(), _stoichiometryBulk.data() + _stoichiometryBulk.elements(), _expBulkBwd.data(), [](const active& v) { return std::max(0.0, static_cast(v)); }); + std::transform(_stoichiometryBulk.data(), _stoichiometryBulk.data() + _stoichiometryBulk.elements(), + _expBulkBwd.data(), [](const active& v) { return std::max(0.0, static_cast(v)); }); } registerCompRowMatrix(_parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_BULK_BWD", _expBulkBwd); - if (paramProvider.exists("MAL_STOICHIOMETRY_LIQUID")) { const std::vector s = paramProvider.getDoubleArray("MAL_STOICHIOMETRY_LIQUID"); if (static_cast(s.size()) != _stoichiometryLiquid.elements()) - throw InvalidParameterException("MAL_STOICHIOMETRY_LIQUID has changed size (number of reactions changed)"); + throw InvalidParameterException( + "MAL_STOICHIOMETRY_LIQUID has changed size (number of reactions changed)"); std::copy(s.begin(), s.end(), _stoichiometryLiquid.data()); } @@ -469,14 +540,18 @@ class MassActionLawReactionBase : public DynamicReactionModelBase { const std::vector s = paramProvider.getDoubleArray("MAL_EXPONENTS_LIQUID_FWD"); if (static_cast(s.size()) != _stoichiometryLiquid.elements()) - throw InvalidParameterException("Expected MAL_EXPONENTS_LIQUID_FWD and MAL_STOICHIOMETRY_LIQUID to be of the same size (" + std::to_string(_stoichiometryLiquid.elements()) + ")"); + throw InvalidParameterException( + "Expected MAL_EXPONENTS_LIQUID_FWD and MAL_STOICHIOMETRY_LIQUID to be of the same size (" + + std::to_string(_stoichiometryLiquid.elements()) + ")"); std::copy(s.begin(), s.end(), _expLiquidFwd.data()); } else { // Obtain default values from stoichiometry - std::transform(_stoichiometryLiquid.data(), _stoichiometryLiquid.data() + _stoichiometryLiquid.elements(), _expLiquidFwd.data(), [](const active& v) { return std::max(0.0, -static_cast(v)); }); + std::transform(_stoichiometryLiquid.data(), _stoichiometryLiquid.data() + _stoichiometryLiquid.elements(), + _expLiquidFwd.data(), + [](const active& v) { return std::max(0.0, -static_cast(v)); }); } registerCompRowMatrix(_parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_LIQUID_FWD", _expLiquidFwd); @@ -484,89 +559,109 @@ class MassActionLawReactionBase : public DynamicReactionModelBase { const std::vector s = paramProvider.getDoubleArray("MAL_EXPONENTS_LIQUID_BWD"); if (static_cast(s.size()) != _stoichiometryLiquid.elements()) - throw InvalidParameterException("Expected MAL_EXPONENTS_LIQUID_BWD and MAL_STOICHIOMETRY_LIQUID to be of the same size (" + std::to_string(_stoichiometryLiquid.elements()) + ")"); + throw InvalidParameterException( + "Expected MAL_EXPONENTS_LIQUID_BWD and MAL_STOICHIOMETRY_LIQUID to be of the same size (" + + std::to_string(_stoichiometryLiquid.elements()) + ")"); std::copy(s.begin(), s.end(), _expLiquidBwd.data()); } else { // Obtain default values from stoichiometry - std::transform(_stoichiometryLiquid.data(), _stoichiometryLiquid.data() + _stoichiometryLiquid.elements(), _expLiquidBwd.data(), [](const active& v) { return std::max(0.0, static_cast(v)); }); + std::transform(_stoichiometryLiquid.data(), _stoichiometryLiquid.data() + _stoichiometryLiquid.elements(), + _expLiquidBwd.data(), [](const active& v) { return std::max(0.0, static_cast(v)); }); } registerCompRowMatrix(_parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_LIQUID_BWD", _expLiquidBwd); if (!_nBoundStates || !_boundOffset) return true; - readAndRegisterExponents(paramProvider, _parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_LIQUID_FWD_MODSOLID", _expLiquidFwdSolid, _nComp, _boundOffset); - readAndRegisterExponents(paramProvider, _parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_LIQUID_BWD_MODSOLID", _expLiquidBwdSolid, _nComp, _boundOffset); - + readAndRegisterExponents(paramProvider, _parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_LIQUID_FWD_MODSOLID", + _expLiquidFwdSolid, _nComp, _boundOffset); + readAndRegisterExponents(paramProvider, _parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_LIQUID_BWD_MODSOLID", + _expLiquidBwdSolid, _nComp, _boundOffset); if (paramProvider.exists("MAL_STOICHIOMETRY_SOLID")) { const std::vector s = paramProvider.getDoubleArray("MAL_STOICHIOMETRY_SOLID"); if (static_cast(s.size()) != _stoichiometrySolid.elements()) - throw InvalidParameterException("MAL_STOICHIOMETRY_SOLID has changed size (number of reactions changed)"); + throw InvalidParameterException( + "MAL_STOICHIOMETRY_SOLID has changed size (number of reactions changed)"); std::copy(s.begin(), s.end(), _stoichiometrySolid.data()); } - registerBoundStateRowMatrix(_parameters, unitOpIdx, parTypeIdx, "MAL_STOICHIOMETRY_SOLID", _stoichiometrySolid, _nComp, _boundOffset); + registerBoundStateRowMatrix(_parameters, unitOpIdx, parTypeIdx, "MAL_STOICHIOMETRY_SOLID", _stoichiometrySolid, + _nComp, _boundOffset); if (paramProvider.exists("MAL_EXPONENTS_SOLID_FWD")) { const std::vector s = paramProvider.getDoubleArray("MAL_EXPONENTS_SOLID_FWD"); if (static_cast(s.size()) != _stoichiometrySolid.elements()) - throw InvalidParameterException("Expected MAL_EXPONENTS_SOLID_FWD and MAL_STOICHIOMETRY_SOLID to be of the same size (" + std::to_string(_stoichiometrySolid.elements()) + ")"); + throw InvalidParameterException( + "Expected MAL_EXPONENTS_SOLID_FWD and MAL_STOICHIOMETRY_SOLID to be of the same size (" + + std::to_string(_stoichiometrySolid.elements()) + ")"); std::copy(s.begin(), s.end(), _expSolidFwd.data()); } else { // Obtain default values from stoichiometry - std::transform(_stoichiometrySolid.data(), _stoichiometrySolid.data() + _stoichiometrySolid.elements(), _expSolidFwd.data(), [](const active& v) { return std::max(0.0, -static_cast(v)); }); + std::transform(_stoichiometrySolid.data(), _stoichiometrySolid.data() + _stoichiometrySolid.elements(), + _expSolidFwd.data(), [](const active& v) { return std::max(0.0, -static_cast(v)); }); } - registerBoundStateRowMatrix(_parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_SOLID_FWD", _expSolidFwd, _nComp, _boundOffset); + registerBoundStateRowMatrix(_parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_SOLID_FWD", _expSolidFwd, _nComp, + _boundOffset); if (paramProvider.exists("MAL_EXPONENTS_SOLID_BWD")) { const std::vector s = paramProvider.getDoubleArray("MAL_EXPONENTS_SOLID_BWD"); if (static_cast(s.size()) != _stoichiometrySolid.elements()) - throw InvalidParameterException("Expected MAL_EXPONENTS_SOLID_BWD and MAL_STOICHIOMETRY_SOLID to be of the same size (" + std::to_string(_stoichiometrySolid.elements()) + ")"); + throw InvalidParameterException( + "Expected MAL_EXPONENTS_SOLID_BWD and MAL_STOICHIOMETRY_SOLID to be of the same size (" + + std::to_string(_stoichiometrySolid.elements()) + ")"); std::copy(s.begin(), s.end(), _expSolidBwd.data()); } else { // Obtain default values from stoichiometry - std::transform(_stoichiometrySolid.data(), _stoichiometrySolid.data() + _stoichiometrySolid.elements(), _expSolidBwd.data(), [](const active& v) { return std::max(0.0, static_cast(v)); }); + std::transform(_stoichiometrySolid.data(), _stoichiometrySolid.data() + _stoichiometrySolid.elements(), + _expSolidBwd.data(), [](const active& v) { return std::max(0.0, static_cast(v)); }); } - registerBoundStateRowMatrix(_parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_SOLID_BWD", _expSolidBwd, _nComp, _boundOffset); + registerBoundStateRowMatrix(_parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_SOLID_BWD", _expSolidBwd, _nComp, + _boundOffset); - readAndRegisterExponents(paramProvider, _parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_SOLID_FWD_MODLIQUID", _expSolidFwdLiquid, _nComp, nullptr); - readAndRegisterExponents(paramProvider, _parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_SOLID_BWD_MODLIQUID", _expSolidBwdLiquid, _nComp, nullptr); + readAndRegisterExponents(paramProvider, _parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_SOLID_FWD_MODLIQUID", + _expSolidFwdLiquid, _nComp, nullptr); + readAndRegisterExponents(paramProvider, _parameters, unitOpIdx, parTypeIdx, "MAL_EXPONENTS_SOLID_BWD_MODLIQUID", + _expSolidBwdLiquid, _nComp, nullptr); return true; } template - int residualLiquidImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, - StateType const* y, ResidualType* res, const FactorType& factor, LinearBufferAllocator workSpace) const + int residualLiquidImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, + ResidualType* res, const FactorType& factor, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Calculate fluxes typedef typename DoubleActivePromoter::type flux_t; BufferedArray fluxes = workSpace.array(maxNumReactions()); for (int r = 0; r < _stoichiometryBulk.columns(); ++r) { - flux_t fwd = rateConstantOrZero(static_cast::type>(p->kFwdBulk[r]), r, _expBulkFwd, _nComp); + flux_t fwd = + rateConstantOrZero(static_cast::type>(p->kFwdBulk[r]), r, + _expBulkFwd, _nComp); for (int c = 0; c < _nComp; ++c) { if (_expBulkFwd.native(c, r) != 0.0) { if (static_cast(y[c]) > 0.0) - fwd *= pow(static_cast::type>(y[c]), + fwd *= pow( + static_cast::type>(y[c]), static_cast::type>(_expBulkFwd.native(c, r))); else { @@ -576,13 +671,16 @@ class MassActionLawReactionBase : public DynamicReactionModelBase } } - flux_t bwd = rateConstantOrZero(static_cast::type>(p->kBwdBulk[r]), r, _expBulkBwd, _nComp); + flux_t bwd = + rateConstantOrZero(static_cast::type>(p->kBwdBulk[r]), r, + _expBulkBwd, _nComp); for (int c = 0; c < _nComp; ++c) { if (_expBulkBwd.native(c, r) != 0.0) { if (static_cast(y[c]) > 0.0) - bwd *= pow(static_cast::type>(y[c]), + bwd *= pow( + static_cast::type>(y[c]), static_cast::type>(_expBulkBwd.native(c, r))); else { @@ -602,24 +700,29 @@ class MassActionLawReactionBase : public DynamicReactionModelBase } template - int residualCombinedImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, - StateType const* yLiquid, StateType const* ySolid, ResidualType* resLiquid, ResidualType* resSolid, double factor, LinearBufferAllocator workSpace) const + int residualCombinedImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* yLiquid, + StateType const* ySolid, ResidualType* resLiquid, ResidualType* resSolid, double factor, + LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Calculate fluxes in liquid phase typedef typename DoubleActivePromoter::type flux_t; BufferedArray fluxes = workSpace.array(maxNumReactions()); for (int r = 0; r < _stoichiometryLiquid.columns(); ++r) { - flux_t fwd = rateConstantOrZero(static_cast::type>(p->kFwdLiquid[r]), r, _expLiquidFwd, _expLiquidFwdSolid, _nComp, _nTotalBoundStates); + flux_t fwd = + rateConstantOrZero(static_cast::type>(p->kFwdLiquid[r]), r, + _expLiquidFwd, _expLiquidFwdSolid, _nComp, _nTotalBoundStates); for (int c = 0; c < _nComp; ++c) { if (_expLiquidFwd.native(c, r) != 0.0) { if (static_cast(yLiquid[c]) > 0.0) fwd *= pow(static_cast::type>(yLiquid[c]), - static_cast::type>(_expLiquidFwd.native(c, r))); + static_cast::type>( + _expLiquidFwd.native(c, r))); else { fwd *= 0.0; @@ -633,7 +736,8 @@ class MassActionLawReactionBase : public DynamicReactionModelBase { if (static_cast(ySolid[c]) > 0.0) fwd *= pow(static_cast::type>(ySolid[c]), - static_cast::type>(_expLiquidFwdSolid.native(c, r))); + static_cast::type>( + _expLiquidFwdSolid.native(c, r))); else { fwd *= 0.0; @@ -642,14 +746,17 @@ class MassActionLawReactionBase : public DynamicReactionModelBase } } - flux_t bwd = rateConstantOrZero(static_cast::type>(p->kBwdLiquid[r]), r, _expLiquidBwd, _expLiquidBwdSolid, _nComp, _nTotalBoundStates); + flux_t bwd = + rateConstantOrZero(static_cast::type>(p->kBwdLiquid[r]), r, + _expLiquidBwd, _expLiquidBwdSolid, _nComp, _nTotalBoundStates); for (int c = 0; c < _nComp; ++c) { if (_expLiquidBwd.native(c, r) != 0.0) { if (static_cast(yLiquid[c]) > 0.0) bwd *= pow(static_cast::type>(yLiquid[c]), - static_cast::type>(_expLiquidBwd.native(c, r))); + static_cast::type>( + _expLiquidBwd.native(c, r))); else { bwd *= 0.0; @@ -663,7 +770,8 @@ class MassActionLawReactionBase : public DynamicReactionModelBase { if (static_cast(ySolid[c]) > 0.0) bwd *= pow(static_cast::type>(ySolid[c]), - static_cast::type>(_expLiquidBwdSolid.native(c, r))); + static_cast::type>( + _expLiquidBwdSolid.native(c, r))); else { bwd *= 0.0; @@ -684,14 +792,17 @@ class MassActionLawReactionBase : public DynamicReactionModelBase // Calculate fluxes in solid phase for (int r = 0; r < _stoichiometrySolid.columns(); ++r) { - flux_t fwd = rateConstantOrZero(static_cast::type>(p->kFwdSolid[r]), r, _expSolidFwdLiquid, _expSolidFwd, _nComp, _nTotalBoundStates); + flux_t fwd = + rateConstantOrZero(static_cast::type>(p->kFwdSolid[r]), r, + _expSolidFwdLiquid, _expSolidFwd, _nComp, _nTotalBoundStates); for (int c = 0; c < _nComp; ++c) { if (_expSolidFwdLiquid.native(c, r) != 0.0) { if (static_cast(yLiquid[c]) > 0.0) fwd *= pow(static_cast::type>(yLiquid[c]), - static_cast::type>(_expSolidFwdLiquid.native(c, r))); + static_cast::type>( + _expSolidFwdLiquid.native(c, r))); else { fwd *= 0.0; @@ -704,7 +815,8 @@ class MassActionLawReactionBase : public DynamicReactionModelBase if (_expSolidFwd.native(c, r) != 0.0) { if (static_cast(ySolid[c]) > 0.0) - fwd *= pow(static_cast::type>(ySolid[c]), + fwd *= pow( + static_cast::type>(ySolid[c]), static_cast::type>(_expSolidFwd.native(c, r))); else { @@ -714,14 +826,17 @@ class MassActionLawReactionBase : public DynamicReactionModelBase } } - flux_t bwd = rateConstantOrZero(static_cast::type>(p->kBwdSolid[r]), r, _expSolidBwdLiquid, _expSolidBwd, _nComp, _nTotalBoundStates); + flux_t bwd = + rateConstantOrZero(static_cast::type>(p->kBwdSolid[r]), r, + _expSolidBwdLiquid, _expSolidBwd, _nComp, _nTotalBoundStates); for (int c = 0; c < _nComp; ++c) { if (_expSolidBwdLiquid.native(c, r) != 0.0) { if (static_cast(yLiquid[c]) > 0.0) bwd *= pow(static_cast::type>(yLiquid[c]), - static_cast::type>(_expSolidBwdLiquid.native(c, r))); + static_cast::type>( + _expSolidBwdLiquid.native(c, r))); else { bwd *= 0.0; @@ -734,7 +849,8 @@ class MassActionLawReactionBase : public DynamicReactionModelBase if (_expSolidBwd.native(c, r) != 0.0) { if (static_cast(ySolid[c]) > 0.0) - bwd *= pow(static_cast::type>(ySolid[c]), + bwd *= pow( + static_cast::type>(ySolid[c]), static_cast::type>(_expSolidBwd.native(c, r))); else { @@ -754,9 +870,11 @@ class MassActionLawReactionBase : public DynamicReactionModelBase } template - void jacobianLiquidImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double factor, const RowIterator& jac, LinearBufferAllocator workSpace) const + void jacobianLiquidImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double factor, + const RowIterator& jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); BufferedArray fluxes = workSpace.array(2 * _nComp); double* const fluxGradFwd = static_cast(fluxes); @@ -779,9 +897,12 @@ class MassActionLawReactionBase : public DynamicReactionModelBase } template - void jacobianCombinedImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, double factor, const RowIteratorLiquid& jacLiquid, const RowIteratorSolid& jacSolid, LinearBufferAllocator workSpace) const + void jacobianCombinedImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, + double const* ySolid, double factor, const RowIteratorLiquid& jacLiquid, + const RowIteratorSolid& jacSolid, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); BufferedArray fluxes = workSpace.array(2 * (_nComp + _nTotalBoundStates)); double* const fluxGradFwd = static_cast(fluxes); @@ -790,8 +911,10 @@ class MassActionLawReactionBase : public DynamicReactionModelBase for (int r = 0; r < _stoichiometryLiquid.columns(); ++r) { // Calculate gradients of forward and backward fluxes - fluxGradCombined(fluxGradFwd, r, _nComp, _nTotalBoundStates, static_cast(p->kFwdLiquid[r]), _expLiquidFwd, _expLiquidFwdSolid, yLiquid, ySolid); - fluxGradCombined(fluxGradBwd, r, _nComp, _nTotalBoundStates, static_cast(p->kBwdLiquid[r]), _expLiquidBwd, _expLiquidBwdSolid, yLiquid, ySolid); + fluxGradCombined(fluxGradFwd, r, _nComp, _nTotalBoundStates, static_cast(p->kFwdLiquid[r]), + _expLiquidFwd, _expLiquidFwdSolid, yLiquid, ySolid); + fluxGradCombined(fluxGradBwd, r, _nComp, _nTotalBoundStates, static_cast(p->kBwdLiquid[r]), + _expLiquidBwd, _expLiquidBwdSolid, yLiquid, ySolid); // Add gradients to Jacobian RowIteratorLiquid curJac = jacLiquid; @@ -809,8 +932,10 @@ class MassActionLawReactionBase : public DynamicReactionModelBase for (int r = 0; r < _stoichiometrySolid.columns(); ++r) { // Calculate gradients of forward and backward fluxes - fluxGradCombined(fluxGradFwd, r, _nComp, _nTotalBoundStates, static_cast(p->kFwdSolid[r]), _expSolidFwdLiquid, _expSolidFwd, yLiquid, ySolid); - fluxGradCombined(fluxGradBwd, r, _nComp, _nTotalBoundStates, static_cast(p->kBwdSolid[r]), _expSolidBwdLiquid, _expSolidBwd, yLiquid, ySolid); + fluxGradCombined(fluxGradFwd, r, _nComp, _nTotalBoundStates, static_cast(p->kFwdSolid[r]), + _expSolidFwdLiquid, _expSolidFwd, yLiquid, ySolid); + fluxGradCombined(fluxGradBwd, r, _nComp, _nTotalBoundStates, static_cast(p->kBwdSolid[r]), + _expSolidBwdLiquid, _expSolidBwd, yLiquid, ySolid); // Add gradients to Jacobian RowIteratorSolid curJac = jacSolid; @@ -818,7 +943,8 @@ class MassActionLawReactionBase : public DynamicReactionModelBase { const double colFactor = static_cast(_stoichiometrySolid.native(row, r)) * factor; for (int col = 0; col < _nComp + _nTotalBoundStates; ++col) - curJac[col - static_cast(_nComp) - static_cast(row)] += colFactor * (fluxGradFwd[col] - fluxGradBwd[col]); + curJac[col - static_cast(_nComp) - static_cast(row)] += + colFactor * (fluxGradFwd[col] - fluxGradBwd[col]); } } } @@ -829,13 +955,14 @@ typedef MassActionLawReactionBase ExternalMassActi namespace reaction { - void registerMassActionLawReaction(std::unordered_map>& reactions) - { - reactions[MassActionLawReaction::identifier()] = []() { return new MassActionLawReaction(); }; - reactions[ExternalMassActionLawReaction::identifier()] = []() { return new ExternalMassActionLawReaction(); }; - } -} // namespace reaction +void registerMassActionLawReaction( + std::unordered_map>& reactions) +{ + reactions[MassActionLawReaction::identifier()] = []() { return new MassActionLawReaction(); }; + reactions[ExternalMassActionLawReaction::identifier()] = []() { return new ExternalMassActionLawReaction(); }; +} +} // namespace reaction -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/reaction/MichaelisMentenReaction.cpp b/src/libcadet/model/reaction/MichaelisMentenReaction.cpp index d9148cb61..f8400e6b9 100644 --- a/src/libcadet/model/reaction/MichaelisMentenReaction.cpp +++ b/src/libcadet/model/reaction/MichaelisMentenReaction.cpp @@ -43,50 +43,59 @@ ------------------------ */ - namespace cadet { namespace model { -inline const char* MichaelisMentenParamHandler::identifier() CADET_NOEXCEPT { return "MICHAELIS_MENTEN"; } +inline const char* MichaelisMentenParamHandler::identifier() CADET_NOEXCEPT +{ + return "MICHAELIS_MENTEN"; +} -inline bool MichaelisMentenParamHandler::validateConfig(unsigned int nReactions, unsigned int nComp, unsigned int const* nBoundStates) +inline bool MichaelisMentenParamHandler::validateConfig(unsigned int nReactions, unsigned int nComp, + unsigned int const* nBoundStates) { return true; } -inline const char* ExtMichaelisMentenParamHandler::identifier() CADET_NOEXCEPT { return "EXT_MICHAELIS_MENTEN"; } +inline const char* ExtMichaelisMentenParamHandler::identifier() CADET_NOEXCEPT +{ + return "EXT_MICHAELIS_MENTEN"; +} -inline bool ExtMichaelisMentenParamHandler::validateConfig(unsigned int nReactions, unsigned int nComp, unsigned int const* nBoundStates) +inline bool ExtMichaelisMentenParamHandler::validateConfig(unsigned int nReactions, unsigned int nComp, + unsigned int const* nBoundStates) { return true; } namespace { - /** - * @brief Registers a matrix-valued parameter (row-major storage) with components as rows - * @details The matrix-valued parameter has as many rows as there are components in the system. - * @param [in,out] parameters Parameter map - * @param [in] unitOpIdx Unit operation id - * @param [in] parTypeIdx Particle type index - * @param [in] paramName Name of the parameter - * @param [in] mat Matrix to register - */ - inline void registerCompRowMatrix(std::unordered_map& parameters, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx, const std::string& paramName, cadet::linalg::ActiveDenseMatrix& mat) - { - const cadet::StringHash hashName = cadet::hashStringRuntime(paramName); - cadet::registerParam2DArray(parameters, mat.data(), mat.elements(), [=](bool multi, unsigned int row, unsigned int col) - { - return cadet::makeParamId(hashName, unitOpIdx, row, parTypeIdx, cadet::BoundStateIndep, col, cadet::SectionIndep); - }, - mat.columns() - ); - } +/** + * @brief Registers a matrix-valued parameter (row-major storage) with components as rows + * @details The matrix-valued parameter has as many rows as there are components in the system. + * @param [in,out] parameters Parameter map + * @param [in] unitOpIdx Unit operation id + * @param [in] parTypeIdx Particle type index + * @param [in] paramName Name of the parameter + * @param [in] mat Matrix to register + */ +inline void registerCompRowMatrix(std::unordered_map& parameters, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx, const std::string& paramName, + cadet::linalg::ActiveDenseMatrix& mat) +{ + const cadet::StringHash hashName = cadet::hashStringRuntime(paramName); + cadet::registerParam2DArray( + parameters, mat.data(), mat.elements(), + [=](bool multi, unsigned int row, unsigned int col) { + return cadet::makeParamId(hashName, unitOpIdx, row, parTypeIdx, cadet::BoundStateIndep, col, + cadet::SectionIndep); + }, + mat.columns()); } - +} // namespace /** * @brief Defines a Michaelis-Menten reaction kinetic with simple inhibition @@ -103,35 +112,55 @@ namespace * In addition, the reaction might be inhibited by other components. In this * case, the flux has the form * \f[ \begin{align} - * \nu_i = \frac{\mu_{\mathrm{max},i} c_S}{k_{\mathrm{MM},i} + c_S} \prod_j \frac{k_{\mathrm{I},i,j}}{k_{\mathrm{I},i,j} + c_{\mathrm{I},j}}. - * \end{align} \f] - * The value of \f$ k_{\mathrm{I},i,j} \f$ decides whether component \f$ j \f$ - * inhibits reaction \f$ i \f$. If \f$ k_{\mathrm{I},i,j} \leq 0 \f$, the component - * does not inhibit the reaction. + * \nu_i = \frac{\mu_{\mathrm{max},i} c_S}{k_{\mathrm{MM},i} + c_S} \prod_j + * \frac{k_{\mathrm{I},i,j}}{k_{\mathrm{I},i,j} + c_{\mathrm{I},j}}. \end{align} \f] The value of \f$ k_{\mathrm{I},i,j} + * \f$ decides whether component \f$ j \f$ inhibits reaction \f$ i \f$. If \f$ k_{\mathrm{I},i,j} \leq 0 \f$, the + * component does not inhibit the reaction. * * Only reactions in liquid phase are supported (no solid phase or cross-phase reactions). * @tparam ParamHandler_t Type that can add support for external function dependence */ -template -class MichaelisMentenReactionBase : public DynamicReactionModelBase +template class MichaelisMentenReactionBase : public DynamicReactionModelBase { public: + MichaelisMentenReactionBase() : _idxSubstrate(0) + { + } + virtual ~MichaelisMentenReactionBase() CADET_NOEXCEPT + { + } - MichaelisMentenReactionBase() : _idxSubstrate(0) { } - virtual ~MichaelisMentenReactionBase() CADET_NOEXCEPT { } - - static const char* identifier() { return ParamHandler_t::identifier(); } - virtual const char* name() const CADET_NOEXCEPT { return ParamHandler_t::identifier(); } + static const char* identifier() + { + return ParamHandler_t::identifier(); + } + virtual const char* name() const CADET_NOEXCEPT + { + return ParamHandler_t::identifier(); + } - virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) { _paramHandler.setExternalFunctions(extFuns, size); } - virtual bool dependsOnTime() const CADET_NOEXCEPT { return ParamHandler_t::dependsOnTime(); } - virtual bool requiresWorkspace() const CADET_NOEXCEPT { return true; } - virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, unsigned int const* nBoundStates) const CADET_NOEXCEPT + virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) + { + _paramHandler.setExternalFunctions(extFuns, size); + } + virtual bool dependsOnTime() const CADET_NOEXCEPT + { + return ParamHandler_t::dependsOnTime(); + } + virtual bool requiresWorkspace() const CADET_NOEXCEPT { - return _paramHandler.cacheSize(_stoichiometryBulk.columns(), nComp, totalNumBoundStates) + std::max(_stoichiometryBulk.columns() * sizeof(active), 2 * (_nComp + totalNumBoundStates) * sizeof(double)); + return true; + } + virtual unsigned int workspaceSize(unsigned int nComp, unsigned int totalNumBoundStates, + unsigned int const* nBoundStates) const CADET_NOEXCEPT + { + return _paramHandler.cacheSize(_stoichiometryBulk.columns(), nComp, totalNumBoundStates) + + std::max(_stoichiometryBulk.columns() * sizeof(active), + 2 * (_nComp + totalNumBoundStates) * sizeof(double)); } - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { DynamicReactionModelBase::configureModelDiscretization(paramProvider, nComp, nBound, boundOffset); @@ -139,7 +168,9 @@ class MichaelisMentenReactionBase : public DynamicReactionModelBase { const std::size_t numElements = paramProvider.numElements("MM_STOICHIOMETRY_BULK"); if (numElements % nComp != 0) - throw InvalidParameterException("Size of field MM_STOICHIOMETRY_BULK must be a positive multiple of NCOMP (" + std::to_string(nComp) + ")"); + throw InvalidParameterException( + "Size of field MM_STOICHIOMETRY_BULK must be a positive multiple of NCOMP (" + + std::to_string(nComp) + ")"); const unsigned int nReactions = numElements / nComp; @@ -150,8 +181,14 @@ class MichaelisMentenReactionBase : public DynamicReactionModelBase return true; } - virtual unsigned int numReactionsLiquid() const CADET_NOEXCEPT { return _stoichiometryBulk.columns(); } - virtual unsigned int numReactionsCombined() const CADET_NOEXCEPT { return 0; } + virtual unsigned int numReactionsLiquid() const CADET_NOEXCEPT + { + return _stoichiometryBulk.columns(); + } + virtual unsigned int numReactionsCombined() const CADET_NOEXCEPT + { + return 0; + } CADET_DYNAMICREACTIONMODEL_BOILERPLATE @@ -166,11 +203,14 @@ class MichaelisMentenReactionBase : public DynamicReactionModelBase _paramHandler.configure(paramProvider, _stoichiometryBulk.columns(), _nComp, _nBoundStates); _paramHandler.registerParameters(_parameters, unitOpIdx, parTypeIdx, _nComp, _nBoundStates); - if ((_stoichiometryBulk.columns() > 0) && ((_paramHandler.vMax().size() < _stoichiometryBulk.columns()) || (_paramHandler.kMM().size() < _stoichiometryBulk.columns()))) + if ((_stoichiometryBulk.columns() > 0) && ((_paramHandler.vMax().size() < _stoichiometryBulk.columns()) || + (_paramHandler.kMM().size() < _stoichiometryBulk.columns()))) throw InvalidParameterException("MM_VMAX and MM_KMM have to have the same size (number of reactions)"); - - if ((_stoichiometryBulk.columns() > 0) && (_paramHandler.kInhibit().size() < _stoichiometryBulk.columns() * _nComp)) - throw InvalidParameterException("MM_KI have to have the size (number of reactions) x (number of components)"); + + if ((_stoichiometryBulk.columns() > 0) && + (_paramHandler.kInhibit().size() < _stoichiometryBulk.columns() * _nComp)) + throw InvalidParameterException( + "MM_KI have to have the size (number of reactions) x (number of components)"); if (paramProvider.exists("MM_STOICHIOMETRY_BULK")) { @@ -202,10 +242,11 @@ class MichaelisMentenReactionBase : public DynamicReactionModelBase } template - int residualLiquidImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, - StateType const* y, ResidualType* res, const FactorType& factor, LinearBufferAllocator workSpace) const + int residualLiquidImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, + ResidualType* res, const FactorType& factor, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); // Calculate fluxes typedef typename DoubleActivePromoter::type flux_t; @@ -219,11 +260,13 @@ class MichaelisMentenReactionBase : public DynamicReactionModelBase continue; } - fluxes[r] = static_cast::type>(p->vMax[r]) * y[idxSubs] / (static_cast::type>(p->kMM[r]) + y[idxSubs]); + fluxes[r] = static_cast::type>(p->vMax[r]) * y[idxSubs] / + (static_cast::type>(p->kMM[r]) + y[idxSubs]); for (int comp = 0; comp < _nComp; ++comp) { - const flux_t kI = static_cast::type>(p->kInhibit[_nComp * r + comp]); + const flux_t kI = + static_cast::type>(p->kInhibit[_nComp * r + comp]); if (kI <= 0.0) continue; @@ -238,8 +281,9 @@ class MichaelisMentenReactionBase : public DynamicReactionModelBase } template - int residualCombinedImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, - StateType const* yLiquid, StateType const* ySolid, ResidualType* resLiquid, ResidualType* resSolid, double factor, LinearBufferAllocator workSpace) const + int residualCombinedImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* yLiquid, + StateType const* ySolid, ResidualType* resLiquid, ResidualType* resSolid, double factor, + LinearBufferAllocator workSpace) const { std::fill_n(resLiquid, _nComp, 0.0); @@ -252,9 +296,11 @@ class MichaelisMentenReactionBase : public DynamicReactionModelBase } template - void jacobianLiquidImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double factor, const RowIterator& jac, LinearBufferAllocator workSpace) const + void jacobianLiquidImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, double factor, + const RowIterator& jac, LinearBufferAllocator workSpace) const { - typename ParamHandler_t::ParamsHandle const p = _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); + typename ParamHandler_t::ParamsHandle const p = + _paramHandler.update(t, secIdx, colPos, _nComp, _nBoundStates, workSpace); for (int r = 0; r < _stoichiometryBulk.columns(); ++r) { @@ -303,7 +349,9 @@ class MichaelisMentenReactionBase : public DynamicReactionModelBase } template - void jacobianCombinedImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, double factor, const RowIteratorLiquid& jacLiquid, const RowIteratorSolid& jacSolid, LinearBufferAllocator workSpace) const + void jacobianCombinedImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, + double const* ySolid, double factor, const RowIteratorLiquid& jacLiquid, + const RowIteratorSolid& jacSolid, LinearBufferAllocator workSpace) const { } }; @@ -313,13 +361,14 @@ typedef MichaelisMentenReactionBase ExternalMich namespace reaction { - void registerMichaelisMentenReaction(std::unordered_map>& reactions) - { - reactions[MichaelisMentenReaction::identifier()] = []() { return new MichaelisMentenReaction(); }; - reactions[ExternalMichaelisMentenReaction::identifier()] = []() { return new ExternalMichaelisMentenReaction(); }; - } -} // namespace reaction +void registerMichaelisMentenReaction( + std::unordered_map>& reactions) +{ + reactions[MichaelisMentenReaction::identifier()] = []() { return new MichaelisMentenReaction(); }; + reactions[ExternalMichaelisMentenReaction::identifier()] = []() { return new ExternalMichaelisMentenReaction(); }; +} +} // namespace reaction -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/reaction/ReactionModelBase.cpp b/src/libcadet/model/reaction/ReactionModelBase.cpp index 80b92412c..5386eafdf 100644 --- a/src/libcadet/model/reaction/ReactionModelBase.cpp +++ b/src/libcadet/model/reaction/ReactionModelBase.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -26,12 +26,15 @@ namespace cadet namespace model { -DynamicReactionModelBase::DynamicReactionModelBase() : _nComp(0), _nBoundStates(nullptr) { } +DynamicReactionModelBase::DynamicReactionModelBase() : _nComp(0), _nBoundStates(nullptr) +{ +} DynamicReactionModelBase::~DynamicReactionModelBase() CADET_NOEXCEPT { } -bool DynamicReactionModelBase::configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) +bool DynamicReactionModelBase::configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) { _nComp = nComp; _nBoundStates = nBound; @@ -45,7 +48,8 @@ bool DynamicReactionModelBase::configureModelDiscretization(IParameterProvider& return true; } -bool DynamicReactionModelBase::configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) +bool DynamicReactionModelBase::configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx) { // Clear all parameters and reconfigure _parameters.clear(); @@ -56,7 +60,9 @@ std::unordered_map DynamicReactionModelBase::getAllParamete { std::unordered_map data; std::transform(_parameters.begin(), _parameters.end(), std::inserter(data, data.end()), - [](const std::pair& p) { return std::make_pair(p.first, static_cast(*p.second)); }); + [](const std::pair& p) { + return std::make_pair(p.first, static_cast(*p.second)); + }); return data; } @@ -98,6 +104,6 @@ active* DynamicReactionModelBase::getParameter(const ParameterId& pId) return nullptr; } -} // namespace model +} // namespace model -} // namespace cadet +} // namespace cadet diff --git a/src/libcadet/model/reaction/ReactionModelBase.hpp b/src/libcadet/model/reaction/ReactionModelBase.hpp index 279c13dc7..dfe66e5dd 100644 --- a/src/libcadet/model/reaction/ReactionModelBase.hpp +++ b/src/libcadet/model/reaction/ReactionModelBase.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines a ReactionModel base classes. */ @@ -37,15 +37,21 @@ namespace model class DynamicReactionModelBase : public IDynamicReactionModel { public: - DynamicReactionModelBase(); virtual ~DynamicReactionModelBase() CADET_NOEXCEPT; - virtual bool requiresConfiguration() const CADET_NOEXCEPT { return true; } - virtual bool usesParamProviderInDiscretizationConfig() const CADET_NOEXCEPT { return true; } + virtual bool requiresConfiguration() const CADET_NOEXCEPT + { + return true; + } + virtual bool usesParamProviderInDiscretizationConfig() const CADET_NOEXCEPT + { + return true; + } virtual bool configure(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx); - virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset); + virtual bool configureModelDiscretization(IParameterProvider& paramProvider, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset); virtual std::unordered_map getAllParameterValues() const; virtual bool hasParameter(const ParameterId& pId) const; @@ -56,12 +62,14 @@ class DynamicReactionModelBase : public IDynamicReactionModel virtual active* getParameter(const ParameterId& pId); - virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) { } + virtual void setExternalFunctions(IExternalFunction** extFuns, unsigned int size) + { + } protected: - int _nComp; //!< Number of components + int _nComp; //!< Number of components unsigned int const* _nBoundStates; //!< Array with number of bound states for each component - unsigned int const* _boundOffset; //!< Array with offsets to the first bound state of each component + unsigned int const* _boundOffset; //!< Array with offsets to the first bound state of each component int _nTotalBoundStates; std::unordered_map _parameters; //!< Map used to translate ParameterIds to actual variables @@ -79,192 +87,222 @@ class DynamicReactionModelBase : public IDynamicReactionModel virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) = 0; }; - /** * @brief Inserts implementations of all residual() and analyticJacobian() method variants * @details An IDynamicReactionModel implementation has to provide residualLiquidAdd(), residualCombinedAdd(), * analyticJacobianLiquidAdd(), and analyticJacobianCombinedAdd() methods for different variants of state - * and parameter type. This macro saves some time by providing those implementations. It assumes that the + * and parameter type. This macro saves some time by providing those implementations. It assumes that the * implementation provides templatized residualLiquidImpl(), residualCombinedImpl(), jacobianLiquidImpl(), * and jacobianCombinedImpl() function that realize all required variants. - * + * * The implementation is inserted inline in the class declaration. */ #ifdef ENABLE_DG -#define CADET_DYNAMICREACTIONMODEL_BOILERPLATE \ - virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, \ - active* res, const active& factor, LinearBufferAllocator workSpace) const \ - { \ - return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ - } \ - \ - virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, \ - active* res, double factor, LinearBufferAllocator workSpace) const \ - { \ - return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ - } \ - \ - virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - active* res, double factor, LinearBufferAllocator workSpace) const \ - { \ - return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ - } \ - \ - virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - double* res, double factor, LinearBufferAllocator workSpace) const \ - { \ - return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ - } \ - \ - virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* yLiquid, \ - active const* ySolid, active* resLiquid, active* resSolid, double factor, LinearBufferAllocator workSpace) const \ - { \ - return residualCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, resLiquid, resSolid, factor, workSpace); \ - } \ - \ - virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, \ - double const* ySolid, active* resLiquid, active* resSolid, double factor, LinearBufferAllocator workSpace) const \ - { \ - return residualCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, resLiquid, resSolid, factor, workSpace); \ - } \ - \ - virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, \ - double const* ySolid, double* resLiquid, double* resSolid, double factor, LinearBufferAllocator workSpace) const \ - { \ - return residualCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, resLiquid, resSolid, factor, workSpace); \ - } \ - \ - virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - double factor, linalg::BandMatrix::RowIterator jac, LinearBufferAllocator workSpace) const \ - { \ - jacobianLiquidImpl(t, secIdx, colPos, y, factor, jac, workSpace); \ - } \ - \ - virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - double factor, linalg::DenseBandedRowIterator jac, LinearBufferAllocator workSpace) const \ - { \ - jacobianLiquidImpl(t, secIdx, colPos, y, factor, jac, workSpace); \ - } \ - \ - virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - double factor, linalg::BandedSparseRowIterator jac, LinearBufferAllocator workSpace) const \ - { \ - jacobianLiquidImpl(t, secIdx, colPos, y, factor, jac, workSpace); \ - } \ - \ - virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - double factor, linalg::BandedEigenSparseRowIterator jac, LinearBufferAllocator workSpace) const \ - { \ - jacobianLiquidImpl(t, secIdx, colPos, y, factor, jac, workSpace); \ - } \ - \ - virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, \ - double factor, linalg::BandedEigenSparseRowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, LinearBufferAllocator workSpace) const \ - { \ - jacobianCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, factor, jacLiquid, jacSolid, workSpace); \ - } \ - \ - virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, \ - double factor, linalg::BandMatrix::RowIterator jacLiquid, linalg::BandMatrix::RowIterator jacSolid, LinearBufferAllocator workSpace) const \ - { \ - jacobianCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, factor, jacLiquid, jacSolid, workSpace); \ - } \ - \ - virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, \ - double factor, linalg::DenseBandedRowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, LinearBufferAllocator workSpace) const \ - { \ - jacobianCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, factor, jacLiquid, jacSolid, workSpace); \ - } \ - \ - virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, \ - double factor, linalg::BandMatrix::RowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, LinearBufferAllocator workSpace) const \ - { \ - jacobianCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, factor, jacLiquid, jacSolid, workSpace); \ +#define CADET_DYNAMICREACTIONMODEL_BOILERPLATE \ + virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, \ + active* res, const active& factor, LinearBufferAllocator workSpace) const \ + { \ + return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ + } \ + \ + virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, \ + active* res, double factor, LinearBufferAllocator workSpace) const \ + { \ + return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ + } \ + \ + virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ + active* res, double factor, LinearBufferAllocator workSpace) const \ + { \ + return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ + } \ + \ + virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ + double* res, double factor, LinearBufferAllocator workSpace) const \ + { \ + return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ + } \ + \ + virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, \ + active const* yLiquid, active const* ySolid, active* resLiquid, active* resSolid, \ + double factor, LinearBufferAllocator workSpace) const \ + { \ + return residualCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, resLiquid, resSolid, \ + factor, workSpace); \ + } \ + \ + virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, \ + double const* yLiquid, double const* ySolid, active* resLiquid, active* resSolid, \ + double factor, LinearBufferAllocator workSpace) const \ + { \ + return residualCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, resLiquid, resSolid, \ + factor, workSpace); \ + } \ + \ + virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, \ + double const* yLiquid, double const* ySolid, double* resLiquid, double* resSolid, \ + double factor, LinearBufferAllocator workSpace) const \ + { \ + return residualCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, resLiquid, resSolid, \ + factor, workSpace); \ + } \ + \ + virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, \ + double const* y, double factor, linalg::BandMatrix::RowIterator jac, \ + LinearBufferAllocator workSpace) const \ + { \ + jacobianLiquidImpl(t, secIdx, colPos, y, factor, jac, workSpace); \ + } \ + \ + virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, \ + double const* y, double factor, linalg::DenseBandedRowIterator jac, \ + LinearBufferAllocator workSpace) const \ + { \ + jacobianLiquidImpl(t, secIdx, colPos, y, factor, jac, workSpace); \ + } \ + \ + virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, \ + double const* y, double factor, linalg::BandedSparseRowIterator jac, \ + LinearBufferAllocator workSpace) const \ + { \ + jacobianLiquidImpl(t, secIdx, colPos, y, factor, jac, workSpace); \ + } \ + \ + virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, \ + double const* y, double factor, linalg::BandedEigenSparseRowIterator jac, \ + LinearBufferAllocator workSpace) const \ + { \ + jacobianLiquidImpl(t, secIdx, colPos, y, factor, jac, workSpace); \ + } \ + \ + virtual void analyticJacobianCombinedAdd( \ + double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, \ + double factor, linalg::BandedEigenSparseRowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, \ + LinearBufferAllocator workSpace) const \ + { \ + jacobianCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, factor, jacLiquid, jacSolid, workSpace); \ + } \ + \ + virtual void analyticJacobianCombinedAdd( \ + double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, \ + double factor, linalg::BandMatrix::RowIterator jacLiquid, linalg::BandMatrix::RowIterator jacSolid, \ + LinearBufferAllocator workSpace) const \ + { \ + jacobianCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, factor, jacLiquid, jacSolid, workSpace); \ + } \ + \ + virtual void analyticJacobianCombinedAdd( \ + double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, \ + double factor, linalg::DenseBandedRowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, \ + LinearBufferAllocator workSpace) const \ + { \ + jacobianCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, factor, jacLiquid, jacSolid, workSpace); \ + } \ + \ + virtual void analyticJacobianCombinedAdd( \ + double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, \ + double factor, linalg::BandMatrix::RowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, \ + LinearBufferAllocator workSpace) const \ + { \ + jacobianCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, factor, jacLiquid, jacSolid, workSpace); \ } #else -#define CADET_DYNAMICREACTIONMODEL_BOILERPLATE \ - virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, \ - active* res, const active& factor, LinearBufferAllocator workSpace) const \ - { \ - return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ - } \ - \ - virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, \ - active* res, double factor, LinearBufferAllocator workSpace) const \ - { \ - return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ - } \ - \ - virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - active* res, double factor, LinearBufferAllocator workSpace) const \ - { \ - return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ - } \ - \ - virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - double* res, double factor, LinearBufferAllocator workSpace) const \ - { \ - return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ - } \ - \ - virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* yLiquid, \ - active const* ySolid, active* resLiquid, active* resSolid, double factor, LinearBufferAllocator workSpace) const \ - { \ - return residualCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, resLiquid, resSolid, factor, workSpace); \ - } \ - \ - virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, \ - double const* ySolid, active* resLiquid, active* resSolid, double factor, LinearBufferAllocator workSpace) const \ - { \ - return residualCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, resLiquid, resSolid, factor, workSpace); \ - } \ - \ - virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, \ - double const* ySolid, double* resLiquid, double* resSolid, double factor, LinearBufferAllocator workSpace) const \ - { \ - return residualCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, resLiquid, resSolid, factor, workSpace); \ - } \ - \ - virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - double factor, linalg::BandMatrix::RowIterator jac, LinearBufferAllocator workSpace) const \ - { \ - jacobianLiquidImpl(t, secIdx, colPos, y, factor, jac, workSpace); \ - } \ - \ - virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - double factor, linalg::DenseBandedRowIterator jac, LinearBufferAllocator workSpace) const \ - { \ - jacobianLiquidImpl(t, secIdx, colPos, y, factor, jac, workSpace); \ - } \ - \ - virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ - double factor, linalg::BandedSparseRowIterator jac, LinearBufferAllocator workSpace) const \ - { \ - jacobianLiquidImpl(t, secIdx, colPos, y, factor, jac, workSpace); \ - } \ - \ - virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, \ - double factor, linalg::BandMatrix::RowIterator jacLiquid, linalg::BandMatrix::RowIterator jacSolid, LinearBufferAllocator workSpace) const \ - { \ - jacobianCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, factor, jacLiquid, jacSolid, workSpace); \ - } \ - \ - virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, \ - double factor, linalg::DenseBandedRowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, LinearBufferAllocator workSpace) const \ - { \ - jacobianCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, factor, jacLiquid, jacSolid, workSpace); \ - } \ - \ - virtual void analyticJacobianCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, \ - double factor, linalg::BandMatrix::RowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, LinearBufferAllocator workSpace) const \ - { \ - jacobianCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, factor, jacLiquid, jacSolid, workSpace); \ +#define CADET_DYNAMICREACTIONMODEL_BOILERPLATE \ + virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, \ + active* res, const active& factor, LinearBufferAllocator workSpace) const \ + { \ + return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ + } \ + \ + virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, active const* y, \ + active* res, double factor, LinearBufferAllocator workSpace) const \ + { \ + return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ + } \ + \ + virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ + active* res, double factor, LinearBufferAllocator workSpace) const \ + { \ + return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ + } \ + \ + virtual int residualLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, double const* y, \ + double* res, double factor, LinearBufferAllocator workSpace) const \ + { \ + return residualLiquidImpl(t, secIdx, colPos, y, res, factor, workSpace); \ + } \ + \ + virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, \ + active const* yLiquid, active const* ySolid, active* resLiquid, active* resSolid, \ + double factor, LinearBufferAllocator workSpace) const \ + { \ + return residualCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, resLiquid, resSolid, \ + factor, workSpace); \ + } \ + \ + virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, \ + double const* yLiquid, double const* ySolid, active* resLiquid, active* resSolid, \ + double factor, LinearBufferAllocator workSpace) const \ + { \ + return residualCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, resLiquid, resSolid, \ + factor, workSpace); \ + } \ + \ + virtual int residualCombinedAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, \ + double const* yLiquid, double const* ySolid, double* resLiquid, double* resSolid, \ + double factor, LinearBufferAllocator workSpace) const \ + { \ + return residualCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, resLiquid, resSolid, \ + factor, workSpace); \ + } \ + \ + virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, \ + double const* y, double factor, linalg::BandMatrix::RowIterator jac, \ + LinearBufferAllocator workSpace) const \ + { \ + jacobianLiquidImpl(t, secIdx, colPos, y, factor, jac, workSpace); \ + } \ + \ + virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, \ + double const* y, double factor, linalg::DenseBandedRowIterator jac, \ + LinearBufferAllocator workSpace) const \ + { \ + jacobianLiquidImpl(t, secIdx, colPos, y, factor, jac, workSpace); \ + } \ + \ + virtual void analyticJacobianLiquidAdd(double t, unsigned int secIdx, const ColumnPosition& colPos, \ + double const* y, double factor, linalg::BandedSparseRowIterator jac, \ + LinearBufferAllocator workSpace) const \ + { \ + jacobianLiquidImpl(t, secIdx, colPos, y, factor, jac, workSpace); \ + } \ + \ + virtual void analyticJacobianCombinedAdd( \ + double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, \ + double factor, linalg::BandMatrix::RowIterator jacLiquid, linalg::BandMatrix::RowIterator jacSolid, \ + LinearBufferAllocator workSpace) const \ + { \ + jacobianCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, factor, jacLiquid, jacSolid, workSpace); \ + } \ + \ + virtual void analyticJacobianCombinedAdd( \ + double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, \ + double factor, linalg::DenseBandedRowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, \ + LinearBufferAllocator workSpace) const \ + { \ + jacobianCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, factor, jacLiquid, jacSolid, workSpace); \ + } \ + \ + virtual void analyticJacobianCombinedAdd( \ + double t, unsigned int secIdx, const ColumnPosition& colPos, double const* yLiquid, double const* ySolid, \ + double factor, linalg::BandMatrix::RowIterator jacLiquid, linalg::DenseBandedRowIterator jacSolid, \ + LinearBufferAllocator workSpace) const \ + { \ + jacobianCombinedImpl(t, secIdx, colPos, yLiquid, ySolid, factor, jacLiquid, jacSolid, workSpace); \ } #endif - - } // namespace model } // namespace cadet -#endif // LIBCADET_REACTIONMODELBASE_HPP_ +#endif // LIBCADET_REACTIONMODELBASE_HPP_ diff --git a/src/libcadet/nonlin/AdaptiveTrustRegionNewton.cpp b/src/libcadet/nonlin/AdaptiveTrustRegionNewton.cpp index 0f8e4cf0e..b7f3240dd 100644 --- a/src/libcadet/nonlin/AdaptiveTrustRegionNewton.cpp +++ b/src/libcadet/nonlin/AdaptiveTrustRegionNewton.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -20,8 +20,12 @@ namespace cadet namespace nonlin { -AdaptiveTrustRegionNewtonSolver::AdaptiveTrustRegionNewtonSolver() : _initDamping(1e-2), _minDamping(1e-4), _maxIter(50) { } -AdaptiveTrustRegionNewtonSolver::~AdaptiveTrustRegionNewtonSolver() { } +AdaptiveTrustRegionNewtonSolver::AdaptiveTrustRegionNewtonSolver() : _initDamping(1e-2), _minDamping(1e-4), _maxIter(50) +{ +} +AdaptiveTrustRegionNewtonSolver::~AdaptiveTrustRegionNewtonSolver() +{ +} bool AdaptiveTrustRegionNewtonSolver::configure(IParameterProvider& paramProvider) { @@ -34,11 +38,16 @@ bool AdaptiveTrustRegionNewtonSolver::configure(IParameterProvider& paramProvide return true; } -bool AdaptiveTrustRegionNewtonSolver::solve(std::function residual, std::function jacobian, - double tol, double* const point, double* const workingMemory, linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) const +bool AdaptiveTrustRegionNewtonSolver::solve( + std::function residual, + std::function jacobian, double tol, + double* const point, double* const workingMemory, linalg::detail::DenseMatrixBase& jacMatrix, + unsigned int size) const { double* const scaleFactors = workingMemory + 4 * size; - return adaptiveTrustRegionNewtonMethod(residual, [&](double const* const x, double* const y) -> bool { + return adaptiveTrustRegionNewtonMethod( + residual, + [&](double const* const x, double* const y) -> bool { if (!jacobian(x, jacMatrix)) return false; @@ -50,9 +59,13 @@ bool AdaptiveTrustRegionNewtonSolver::solve(std::function residual, std::function jacobian, - double tol, double* const point, double* const workingMemory, linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) const +bool RobustAdaptiveTrustRegionNewtonSolver::solve( + std::function residual, + std::function jacobian, double tol, + double* const point, double* const workingMemory, linalg::detail::DenseMatrixBase& jacMatrix, + unsigned int size) const { double* const scaleFactors = workingMemory + 4 * size; - return robustAdaptiveTrustRegionNewtonMethod(residual, [&](double const* const x, double* const y) -> bool { + return robustAdaptiveTrustRegionNewtonMethod( + residual, + [&](double const* const x, double* const y) -> bool { if (!jacobian(x, jacMatrix)) return false; @@ -78,13 +96,10 @@ bool RobustAdaptiveTrustRegionNewtonSolver::solve(std::function bool { - return jacMatrix.solve(y); - }, - _maxIter, tol, _initDamping, _minDamping, point, workingMemory, size); + [&](double* const y) -> bool { return jacMatrix.solve(y); }, _maxIter, tol, _initDamping, _minDamping, point, + workingMemory, size); } - } // namespace nonlin } // namespace cadet diff --git a/src/libcadet/nonlin/AdaptiveTrustRegionNewton.hpp b/src/libcadet/nonlin/AdaptiveTrustRegionNewton.hpp index e99033872..8ad3928f2 100644 --- a/src/libcadet/nonlin/AdaptiveTrustRegionNewton.hpp +++ b/src/libcadet/nonlin/AdaptiveTrustRegionNewton.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides adaptive trust-region Newton methods for solving nonlinear equation systems */ @@ -34,443 +34,485 @@ namespace cadet namespace nonlin { - /** - * @brief Iterate output policy that does nothing - */ - struct VoidNewtonIterateOutputPolicy +/** + * @brief Iterate output policy that does nothing + */ +struct VoidNewtonIterateOutputPolicy +{ + inline static void outerIteration(unsigned int idxIter, double residualNorm, double const* const res, + double const* const x, double const* const dx, unsigned int size) { - inline static void outerIteration(unsigned int idxIter, double residualNorm, double const* const res, double const* const x, double const* const dx, unsigned int size) { } - inline static void innerIteration(unsigned int idxIter, double residualNorm, double const* const res, double const* const x, double const* const dx, double damping, double mu, unsigned int size) { } - }; - - /** - * @brief Solves nonlinear equations using a residual oriented descent based global adaptive trust-region Newton method - * @details This is an implementation of the NLEQ-RES algorithm described in \cite Deuflhard2011 (p. 131). - * It is a global adaptive trust-region Newton method based on affine contravariance. - * In the framework of affine invariance, the solution of the inner trust region problems is - * a point on the ordinary Newton step line segment. Thus, in the end this algorithm is an adaptively - * damped Newton method with global convergence. - * - * This algorithm is designed to solve the nonlinear equation system - * @f[\begin{align} f(x) = 0, \qquad f: \mathbb{R}^n \to \mathbb{R}^n. \end{align}@f] - * A solution is indicated by the error test - * @f[\begin{align} \left\lVert f(x) \right\rVert_{\ell^2} \leq \text{tol} \end{align}@f] - * with a given tolerance (@p resTol). - * - * The residuals @f$ f(x) @f$ are evaluated using the given function @p residual. - * The resulting linear equation systems - * @f[\begin{align} J_f(x) \Delta x = r, \end{align}@f] - * where @f$ J_f(x) @f$ denotes the Jacobian of @f$ f @f$ at point @f$ x @f$ and @f$ r = f(x) @f$ - * is a given right hand side, are solved using the given function @p jacobianSolver. - * - * The initial damping factor @p damping and the minimal damping factor @p minDamping - * can be chosen based on reference values for different problem difficulties: - * | Difficulty | damping | minDamping | - * | ------------------- | ------- | ---------- | - * | Mildly nonlinear | 1.0 | 1e-4 | - * | Highly nonlinear | 1e-2 | 1e-4 | - * | Extremely nonlinear | 1e-4 | 1e-8 | - * In the case of extremely nonlinear problems, a restricted error test is recommended (not available - * through public interface). - * - * Note that, since this method is based on residual monotonicity, this method can fail - * in practice even if the conditions guaranteeing global convergence are satisfied. - * This is, for example, the case for ill-conditioned Jacobians, since then @f$ x_{k+1} \approx x_k @f$ - * and the iteration is stalled. See pp. 137 in the Deuflhard book. - * A more robust, but also more costly, algorithm is implemented in robustAdaptiveTrustRegionNewtonMethod(). - * @param [in] residual Function providing the residual @f$ f(x) @f$ at position @f$ x @f$ of the nonlinear equation - * system @f$ f(x) = 0 @f$ to be solved. - * The signature of the function is - * `bool residual(double const* const x, double* const r)` - * where the return value communicates whether the evaluation has been successful. - * On exit, the residual is returned in-place in @f$ r @f$. - * If an error is indicated by returning @c false, the algorithm aborts with failure. - * @param [in] jacobianSolver Function calculating the solution of the linear system @f$ J_f(x) \Delta x = r@f$ - * with Jacobian matrix @f$ J_f(x) @f$ at position @f$ x @f$ for a given right hand side @f$ r @f$. - * The signature of the function is - * `bool jacobianSolver(double const* const x, double* const r)` - * where the return value communicates whether the system has been solved correctly. - * On exit, the solution of the linear system is returned in-place in @f$ r @f$ which on entry - * contains the right hand side vector of the linear system. - * If an error is indicated by returning @c false, the algorithm aborts with failure. - * @param [in] maxIter Maximum number of iterations - * @param [in] resTol Termination criterion on the residual @f$\ell^2@f$-norm - * @param [in] damping Initial damping factor (see details for advice) - * @param [in] minDamping Minimal damping factor (see details for advice) - * @param [in,out] point On entry initial guess, on exit solution or last iterate - * @param [in] workingMemory Additional memory of size @f$ 4n @f$ required for performing the iterations, where @f$ n @f$ is the problem @p size - * @param [in] size Size of the problem (i.e., number of equations, length of residual, columns of Jacobian etc.) - * @tparam IterateOutputPolicy Policy that handles output of intermediate values (useful for debugging), see VoidNewtonIterateOutputPolicy - * @return @c true if a solution meeting the residual tolerance was found, @c false otherwise - * @todo Make algorithm more robust by providing means to solver and residual functions to trigger decrease of damping factor (e.g. for negative concentrations) - * @todo Implement scaling of linear systems and norms - */ - template - bool adaptiveTrustRegionNewtonMethod(std::function residual, std::function jacobianSolver, - unsigned int maxIter, double resTol, double damping, double minDamping, double* const point, double* const workingMemory, unsigned int size) + } + inline static void innerIteration(unsigned int idxIter, double residualNorm, double const* const res, + double const* const x, double const* const dx, double damping, double mu, + unsigned int size) { -// const double thetaMax = 0.25; // Determines whether switch to quasi-newton updates is performed (not implemented) - const bool restricted = false; // Determines if restricted monotonicity test is used - double mu = 0.0; - double lastResidualNorm = 0.0; - - // Split working memory into parts - double* const residualMem = workingMemory; - double* const dx = workingMemory + size; - double* const trialPoint = workingMemory + 2 * size; - double* const lastResidual = workingMemory + 3 * size; - - // Evaluate residual - if (!residual(point, lastResidual)) - return false; + } +}; - // Copy residual - std::copy(lastResidual, lastResidual + size, dx); +/** + * @brief Solves nonlinear equations using a residual oriented descent based global adaptive trust-region Newton method + * @details This is an implementation of the NLEQ-RES algorithm described in \cite Deuflhard2011 (p. 131). + * It is a global adaptive trust-region Newton method based on affine contravariance. + * In the framework of affine invariance, the solution of the inner trust region problems is + * a point on the ordinary Newton step line segment. Thus, in the end this algorithm is an adaptively + * damped Newton method with global convergence. + * + * This algorithm is designed to solve the nonlinear equation system + * @f[\begin{align} f(x) = 0, \qquad f: \mathbb{R}^n \to \mathbb{R}^n. \end{align}@f] + * A solution is indicated by the error test + * @f[\begin{align} \left\lVert f(x) \right\rVert_{\ell^2} \leq \text{tol} \end{align}@f] + * with a given tolerance (@p resTol). + * + * The residuals @f$ f(x) @f$ are evaluated using the given function @p residual. + * The resulting linear equation systems + * @f[\begin{align} J_f(x) \Delta x = r, \end{align}@f] + * where @f$ J_f(x) @f$ denotes the Jacobian of @f$ f @f$ at point @f$ x @f$ and @f$ r = f(x) @f$ + * is a given right hand side, are solved using the given function @p jacobianSolver. + * + * The initial damping factor @p damping and the minimal damping factor @p minDamping + * can be chosen based on reference values for different problem difficulties: + * | Difficulty | damping | minDamping | + * | ------------------- | ------- | ---------- | + * | Mildly nonlinear | 1.0 | 1e-4 | + * | Highly nonlinear | 1e-2 | 1e-4 | + * | Extremely nonlinear | 1e-4 | 1e-8 | + * In the case of extremely nonlinear problems, a restricted error test is recommended (not available + * through public interface). + * + * Note that, since this method is based on residual monotonicity, this method can fail + * in practice even if the conditions guaranteeing global convergence are satisfied. + * This is, for example, the case for ill-conditioned Jacobians, since then @f$ x_{k+1} \approx x_k @f$ + * and the iteration is stalled. See pp. 137 in the Deuflhard book. + * A more robust, but also more costly, algorithm is implemented in robustAdaptiveTrustRegionNewtonMethod(). + * @param [in] residual Function providing the residual @f$ f(x) @f$ at position @f$ x @f$ of the nonlinear equation + * system @f$ f(x) = 0 @f$ to be solved. + * The signature of the function is + * `bool residual(double const* const x, double* const r)` + * where the return value communicates whether the evaluation has been successful. + * On exit, the residual is returned in-place in @f$ r @f$. + * If an error is indicated by returning @c false, the algorithm aborts with failure. + * @param [in] jacobianSolver Function calculating the solution of the linear system @f$ J_f(x) \Delta x = r@f$ + * with Jacobian matrix @f$ J_f(x) @f$ at position @f$ x @f$ for a given right hand side @f$ r @f$. + * The signature of the function is + * `bool jacobianSolver(double const* const x, double* const r)` + * where the return value communicates whether the system has been solved correctly. + * On exit, the solution of the linear system is returned in-place in @f$ r @f$ which on entry + * contains the right hand side vector of the linear system. + * If an error is indicated by returning @c false, the algorithm aborts with failure. + * @param [in] maxIter Maximum number of iterations + * @param [in] resTol Termination criterion on the residual @f$\ell^2@f$-norm + * @param [in] damping Initial damping factor (see details for advice) + * @param [in] minDamping Minimal damping factor (see details for advice) + * @param [in,out] point On entry initial guess, on exit solution or last iterate + * @param [in] workingMemory Additional memory of size @f$ 4n @f$ required for performing the iterations, where @f$ n + * @f$ is the problem @p size + * @param [in] size Size of the problem (i.e., number of equations, length of residual, columns of Jacobian etc.) + * @tparam IterateOutputPolicy Policy that handles output of intermediate values (useful for debugging), see + * VoidNewtonIterateOutputPolicy + * @return @c true if a solution meeting the residual tolerance was found, @c false otherwise + * @todo Make algorithm more robust by providing means to solver and residual functions to trigger decrease of damping + * factor (e.g. for negative concentrations) + * @todo Implement scaling of linear systems and norms + */ +template +bool adaptiveTrustRegionNewtonMethod(std::function residual, + std::function jacobianSolver, + unsigned int maxIter, double resTol, double damping, double minDamping, + double* const point, double* const workingMemory, unsigned int size) +{ + // const double thetaMax = 0.25; // Determines whether switch to quasi-newton updates is performed (not + // implemented) + const bool restricted = false; // Determines if restricted monotonicity test is used + double mu = 0.0; + double lastResidualNorm = 0.0; + + // Split working memory into parts + double* const residualMem = workingMemory; + double* const dx = workingMemory + size; + double* const trialPoint = workingMemory + 2 * size; + double* const lastResidual = workingMemory + 3 * size; + + // Evaluate residual + if (!residual(point, lastResidual)) + return false; + + // Copy residual + std::copy(lastResidual, lastResidual + size, dx); - double residualNorm = linalg::l2Norm(lastResidual, size); + double residualNorm = linalg::l2Norm(lastResidual, size); - IterateOutputPolicy::outerIteration(0, residualNorm, lastResidual, point, trialPoint, size); + IterateOutputPolicy::outerIteration(0, residualNorm, lastResidual, point, trialPoint, size); - // Main loop - for (unsigned int kIter = 0; kIter < maxIter; ++kIter) + // Main loop + for (unsigned int kIter = 0; kIter < maxIter; ++kIter) + { + // Convergence test + if (residualNorm <= resTol) + return true; + + // Solve F'(x) * dx = F(x) + // Since we have omitted the minus sign here, we have to take care of the negation later + if (!jacobianSolver(point, dx)) + return false; + + if (kIter > 0) { - // Convergence test - if (residualNorm <= resTol) - return true; + // Compute prediction of damping factor + mu *= lastResidualNorm / residualNorm; + damping = std::min(1.0, mu); + } + + lastResidualNorm = residualNorm; - // Solve F'(x) * dx = F(x) - // Since we have omitted the minus sign here, we have to take care of the negation later - if (!jacobianSolver(point, dx)) + // Line search loop: Use regularity test as abort condition + while (damping >= minDamping) + { + // Compute x_{n+1} = x_n + lambda * dx + // Note that we are subtracting here because we have omitted the negation in the Jacobian solution + for (unsigned int i = 0; i < size; ++i) + trialPoint[i] = point[i] - damping * dx[i]; + + // Evaluate residual + if (!residual(trialPoint, residualMem)) return false; - if (kIter > 0) + residualNorm = linalg::l2Norm(residualMem, size); + + IterateOutputPolicy::innerIteration(kIter + 1, residualNorm, residualMem, trialPoint, dx, damping, mu, + size); + + // Calculate monitoring quantities + const double theta = residualNorm / lastResidualNorm; + + mu = 0.0; + const double factor = 1.0 - damping; + for (unsigned int i = 0; i < size; ++i) { - // Compute prediction of damping factor - mu *= lastResidualNorm / residualNorm; - damping = std::min(1.0, mu); + mu += sqr(residualMem[i] - factor * lastResidual[i]); } + mu = 0.5 * lastResidualNorm * damping * damping / std::sqrt(mu); - lastResidualNorm = residualNorm; + if ((!restricted && (theta >= 1.0)) || (restricted && (theta > 1.0 - damping * 0.25))) + { + // Shrink damping and try again + damping = std::min(mu, 0.5 * damping); + continue; + } - // Line search loop: Use regularity test as abort condition - while (damping >= minDamping) + const double dampingNew = std::min(1.0, mu); + if (dampingNew >= 4.0 * damping) { - // Compute x_{n+1} = x_n + lambda * dx - // Note that we are subtracting here because we have omitted the negation in the Jacobian solution - for (unsigned int i = 0; i < size; ++i) - trialPoint[i] = point[i] - damping * dx[i]; + damping = dampingNew; + continue; + } - // Evaluate residual - if (!residual(trialPoint, residualMem)) - return false; + // Accept the step + break; + } - residualNorm = linalg::l2Norm(residualMem, size); + // Check if regularity test failed or line search loop was aborted because of other reasons + if (damping < minDamping) + return false; - IterateOutputPolicy::innerIteration(kIter + 1, residualNorm, residualMem, trialPoint, dx, damping, mu, size); + IterateOutputPolicy::outerIteration(kIter + 1, residualNorm, residualMem, trialPoint, dx, size); - // Calculate monitoring quantities - const double theta = residualNorm / lastResidualNorm; + // Copy accepted point and last residual + std::copy(trialPoint, trialPoint + size, point); + std::copy(residualMem, residualMem + size, lastResidual); + std::copy(residualMem, residualMem + size, dx); + } + return false; +} - mu = 0.0; - const double factor = 1.0 - damping; - for (unsigned int i = 0; i < size; ++i) - { - mu += sqr(residualMem[i] - factor * lastResidual[i]); - } - mu = 0.5 * lastResidualNorm * damping * damping / std::sqrt(mu); - - if ((!restricted && (theta >= 1.0)) || (restricted && (theta > 1.0 - damping * 0.25))) - { - // Shrink damping and try again - damping = std::min(mu, 0.5 * damping); - continue; - } - - const double dampingNew = std::min(1.0, mu); - if (dampingNew >= 4.0 * damping) - { - damping = dampingNew; - continue; - } - - // Accept the step - break; - } +/** + * @brief Uses an adaptive trust-region Newton method for solving nonlinear equations + * @details Wraps adaptiveTrustRegionNewtonMethod() function. + */ +class AdaptiveTrustRegionNewtonSolver : public Solver +{ +public: + AdaptiveTrustRegionNewtonSolver(); + virtual ~AdaptiveTrustRegionNewtonSolver(); - // Check if regularity test failed or line search loop was aborted because of other reasons - if (damping < minDamping) - return false; + static const char* identifier() + { + return "ATRN_RES"; + } + virtual const char* name() const + { + return AdaptiveTrustRegionNewtonSolver::identifier(); + } + virtual bool configure(IParameterProvider& paramProvider); - IterateOutputPolicy::outerIteration(kIter + 1, residualNorm, residualMem, trialPoint, dx, size); + virtual unsigned int workspaceSize(unsigned int problemSize) const + { + // Method requires 4 * problemSize but we add an additional problemSize for preconditioning + return 5 * problemSize; + } - // Copy accepted point and last residual - std::copy(trialPoint, trialPoint + size, point); - std::copy(residualMem, residualMem + size, lastResidual); - std::copy(residualMem, residualMem + size, dx); - } - return false; + virtual unsigned int numTuningParameters() const + { + return 3; } - /** - * @brief Uses an adaptive trust-region Newton method for solving nonlinear equations - * @details Wraps adaptiveTrustRegionNewtonMethod() function. - */ - class AdaptiveTrustRegionNewtonSolver : public Solver + virtual bool solve(std::function residual, + std::function jacobian, + double tol, double* const point, double* const workingMemory, + linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) const; + +protected: + double _initDamping; //!< Initial damping factor + double _minDamping; //!< Minimal damping factor + unsigned int _maxIter; //!< Maximum number of iterations +}; + +/** + * @brief Solves nonlinear equations using an error oriented descent based global adaptive trust-region Newton method + * @details This is an implementation of the NLEQ-ERR algorithm described in \cite Deuflhard2011 (pp. 148). + * It is a global adaptive trust-region Newton method based on affine covariance. + * In the framework of affine invariance, the solution of the inner trust region problems is + * a point on the ordinary Newton step line segment. Thus, in the end this algorithm is an adaptively + * damped Newton method with global convergence. + * + * This algorithm is designed to solve the nonlinear equation system + * @f[\begin{align} f(x) = 0, \qquad f: \mathbb{R}^n \to \mathbb{R}^n. \end{align}@f] + * A solution is indicated by the error test + * @f[\begin{align} \left\lVert \Delta x \right\rVert_{\ell^2} \leq \text{tol} \end{align}@f] + * with a given tolerance (@p errTol). + * + * The residuals @f$ f(x) @f$ are evaluated using the given function @p residual. + * The resulting linear equation systems + * @f[\begin{align} J_f(x) \Delta x = r, \end{align}@f] + * where @f$ J_f(x) @f$ denotes the Jacobian of @f$ f @f$ at point @f$ x @f$ and @f$ r = f(x) @f$ + * is a given right hand side, are solved using the given function @p jacobianSolver. + * In this algorithm, the linear system has to be solved several times with varying right hand sides @f$ r @f$. + * A repeated solution is requested by the function @p jacobianResolver, which is always preceeded by a + * call to @p jacobianSolver. Thus, a dense matrix can be factorized once in @p jacobianSolver and used + * in subsequent calls of @p jacobianResolver. + * + * The initial damping factor @p damping and the minimal damping factor @p minDamping + * can be chosen based on reference values for different problem difficulties: + * | Difficulty | damping | minDamping | + * | ------------------- | ------- | ---------- | + * | Mildly nonlinear | 1.0 | 1e-4 | + * | Highly nonlinear | 1e-2 | 1e-4 | + * | Extremely nonlinear | 1e-4 | 1e-8 | + * In the case of extremely nonlinear problems, a restricted error test is recommended (not available + * through public interface). + * @param [in] residual Function providing the residual @f$ f(x) @f$ at position @f$ x @f$ of the nonlinear equation + * system @f$ f(x) = 0 @f$ to be solved. + * The signature of the function is + * `bool residual(double const* const x, double* const r)` + * where the return value communicates whether the evaluation has been successful. + * On exit, the residual is returned in-place in @f$ r @f$. + * If an error is indicated by returning @c false, the algorithm aborts with failure. + * @param [in] jacobianSolver Function calculating the solution of the linear system @f$ J_f(x) \Delta x = r@f$ + * with Jacobian matrix @f$ J_f(x) @f$ at position @f$ x @f$ for a given right hand side @f$ r @f$. + * The signature of the function is + * `bool jacobianSolver(double const* const x, double* const r)` + * where the return value communicates whether the system has been solved correctly. + * On exit, the solution of the linear system is returned in-place in @f$ r @f$ which on entry + * contains the right hand side vector of the linear system. + * If an error is indicated by returning @c false, the algorithm aborts with failure. + * @param [in] jacobianResolver Function calculating the solution of the linear system @f$ J_f(x) \Delta x = r@f$ + * with Jacobian matrix @f$ J_f(x) @f$ at the position @f$ x @f$ of a previous call to @p jacobianSolver + * for a given right hand side @f$ r @f$. The signature of the function is + * `bool jacobianResolver(double* const r)` + * where the return value communicates whether the system has been solved correctly. + * On exit, the solution of the linear system is returned in-place in @f$ r @f$ which on entry + * contains the right hand side vector of the linear system. + * If an error is indicated by returning @c false, the algorithm aborts with failure. + * @param [in] maxIter Maximum number of iterations + * @param [in] errTol Termination criterion on the Newton step size @f$\ell^2@f$-norm + * @param [in] damping Initial damping factor (see details for advice) + * @param [in] minDamping Minimal damping factor (see details for advice) + * @param [in,out] point On entry initial guess, on exit solution or last iterate + * @param [in] workingMemory Additional memory of size @f$ 4n @f$ required for performing the iterations, where @f$ n + * @f$ is the problem @p size + * @param [in] size Size of the problem (i.e., number of equations, length of residual, columns of Jacobian etc.) + * @tparam IterateOutputPolicy Policy that handles output of intermediate values (useful for debugging), see + * VoidNewtonIterateOutputPolicy + * @return @c true if a solution meeting the residual tolerance was found, @c false otherwise + * @todo Make algorithm more robust by providing means to solver and residual functions to trigger decrease of damping + * factor (e.g. for negative concentrations) + * @todo Implement scaling of linear systems and norms + */ +template +bool robustAdaptiveTrustRegionNewtonMethod(std::function residual, + std::function jacobianSolver, + std::function jacobianResolver, unsigned int maxIter, + double errTol, double damping, double minDamping, double* const point, + double* const workingMemory, unsigned int size) +{ + // const double thetaMax = 0.25; // Determines whether switch to quasi-newton updates is performed (not + // implemented) + const bool restricted = false; // Determines if restricted monotonicity test is used + double mu = 0.0; + + // Split working memory into parts + double* const dx = workingMemory; + double* const trialPoint = workingMemory + size; + double* const lastDxBar = workingMemory + 2 * size; + double* const lastResidual = workingMemory + 3 * size; + + // Evaluate residual + if (!residual(point, dx)) + return false; + + double errNorm = 0.0; + double lastErrNorm = 0.0; + double errNormTrial = 0.0; + + // Main loop + for (unsigned int kIter = 0; kIter < maxIter; ++kIter) { - public: - AdaptiveTrustRegionNewtonSolver(); - virtual ~AdaptiveTrustRegionNewtonSolver(); + // Solve F'(x) * dx = F(x) + // Since we have omitted the minus sign here, we have to take care of the negation later + if (!jacobianSolver(point, dx)) + return false; + + lastErrNorm = errNorm; + errNorm = linalg::l2Norm(dx, size); - static const char* identifier() { return "ATRN_RES"; } - virtual const char* name() const { return AdaptiveTrustRegionNewtonSolver::identifier(); } - virtual bool configure(IParameterProvider& paramProvider); + IterateOutputPolicy::outerIteration(kIter, errNorm, dx, point, dx, size); - virtual unsigned int workspaceSize(unsigned int problemSize) const + // Convergence test + if (errNorm <= errTol) { - // Method requires 4 * problemSize but we add an additional problemSize for preconditioning - return 5 * problemSize; + // Solution is x + dx = x - (-dx) + // Note that we have to negate dx here since we didn't do that when solving with the Jacobian above + for (unsigned int i = 0; i < size; ++i) + point[i] -= dx[i]; + return true; } - - virtual unsigned int numTuningParameters() const { return 3; } - - virtual bool solve(std::function residual, std::function jacobian, - double tol, double* const point, double* const workingMemory, linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) const; - - protected: - double _initDamping; //!< Initial damping factor - double _minDamping; //!< Minimal damping factor - unsigned int _maxIter; //!< Maximum number of iterations - }; - - /** - * @brief Solves nonlinear equations using an error oriented descent based global adaptive trust-region Newton method - * @details This is an implementation of the NLEQ-ERR algorithm described in \cite Deuflhard2011 (pp. 148). - * It is a global adaptive trust-region Newton method based on affine covariance. - * In the framework of affine invariance, the solution of the inner trust region problems is - * a point on the ordinary Newton step line segment. Thus, in the end this algorithm is an adaptively - * damped Newton method with global convergence. - * - * This algorithm is designed to solve the nonlinear equation system - * @f[\begin{align} f(x) = 0, \qquad f: \mathbb{R}^n \to \mathbb{R}^n. \end{align}@f] - * A solution is indicated by the error test - * @f[\begin{align} \left\lVert \Delta x \right\rVert_{\ell^2} \leq \text{tol} \end{align}@f] - * with a given tolerance (@p errTol). - * - * The residuals @f$ f(x) @f$ are evaluated using the given function @p residual. - * The resulting linear equation systems - * @f[\begin{align} J_f(x) \Delta x = r, \end{align}@f] - * where @f$ J_f(x) @f$ denotes the Jacobian of @f$ f @f$ at point @f$ x @f$ and @f$ r = f(x) @f$ - * is a given right hand side, are solved using the given function @p jacobianSolver. - * In this algorithm, the linear system has to be solved several times with varying right hand sides @f$ r @f$. - * A repeated solution is requested by the function @p jacobianResolver, which is always preceeded by a - * call to @p jacobianSolver. Thus, a dense matrix can be factorized once in @p jacobianSolver and used - * in subsequent calls of @p jacobianResolver. - * - * The initial damping factor @p damping and the minimal damping factor @p minDamping - * can be chosen based on reference values for different problem difficulties: - * | Difficulty | damping | minDamping | - * | ------------------- | ------- | ---------- | - * | Mildly nonlinear | 1.0 | 1e-4 | - * | Highly nonlinear | 1e-2 | 1e-4 | - * | Extremely nonlinear | 1e-4 | 1e-8 | - * In the case of extremely nonlinear problems, a restricted error test is recommended (not available - * through public interface). - * @param [in] residual Function providing the residual @f$ f(x) @f$ at position @f$ x @f$ of the nonlinear equation - * system @f$ f(x) = 0 @f$ to be solved. - * The signature of the function is - * `bool residual(double const* const x, double* const r)` - * where the return value communicates whether the evaluation has been successful. - * On exit, the residual is returned in-place in @f$ r @f$. - * If an error is indicated by returning @c false, the algorithm aborts with failure. - * @param [in] jacobianSolver Function calculating the solution of the linear system @f$ J_f(x) \Delta x = r@f$ - * with Jacobian matrix @f$ J_f(x) @f$ at position @f$ x @f$ for a given right hand side @f$ r @f$. - * The signature of the function is - * `bool jacobianSolver(double const* const x, double* const r)` - * where the return value communicates whether the system has been solved correctly. - * On exit, the solution of the linear system is returned in-place in @f$ r @f$ which on entry - * contains the right hand side vector of the linear system. - * If an error is indicated by returning @c false, the algorithm aborts with failure. - * @param [in] jacobianResolver Function calculating the solution of the linear system @f$ J_f(x) \Delta x = r@f$ - * with Jacobian matrix @f$ J_f(x) @f$ at the position @f$ x @f$ of a previous call to @p jacobianSolver - * for a given right hand side @f$ r @f$. The signature of the function is - * `bool jacobianResolver(double* const r)` - * where the return value communicates whether the system has been solved correctly. - * On exit, the solution of the linear system is returned in-place in @f$ r @f$ which on entry - * contains the right hand side vector of the linear system. - * If an error is indicated by returning @c false, the algorithm aborts with failure. - * @param [in] maxIter Maximum number of iterations - * @param [in] errTol Termination criterion on the Newton step size @f$\ell^2@f$-norm - * @param [in] damping Initial damping factor (see details for advice) - * @param [in] minDamping Minimal damping factor (see details for advice) - * @param [in,out] point On entry initial guess, on exit solution or last iterate - * @param [in] workingMemory Additional memory of size @f$ 4n @f$ required for performing the iterations, where @f$ n @f$ is the problem @p size - * @param [in] size Size of the problem (i.e., number of equations, length of residual, columns of Jacobian etc.) - * @tparam IterateOutputPolicy Policy that handles output of intermediate values (useful for debugging), see VoidNewtonIterateOutputPolicy - * @return @c true if a solution meeting the residual tolerance was found, @c false otherwise - * @todo Make algorithm more robust by providing means to solver and residual functions to trigger decrease of damping factor (e.g. for negative concentrations) - * @todo Implement scaling of linear systems and norms - */ - template - bool robustAdaptiveTrustRegionNewtonMethod(std::function residual, std::function jacobianSolver, - std::function jacobianResolver, unsigned int maxIter, double errTol, double damping, double minDamping, double* const point, - double* const workingMemory, unsigned int size) - { -// const double thetaMax = 0.25; // Determines whether switch to quasi-newton updates is performed (not implemented) - const bool restricted = false; // Determines if restricted monotonicity test is used - double mu = 0.0; - - // Split working memory into parts - double* const dx = workingMemory; - double* const trialPoint = workingMemory + size; - double* const lastDxBar = workingMemory + 2 * size; - double* const lastResidual = workingMemory + 3 * size; - - // Evaluate residual - if (!residual(point, dx)) - return false; - double errNorm = 0.0; - double lastErrNorm = 0.0; - double errNormTrial = 0.0; + if (kIter > 0) + { + // Compute prediction of damping factor + mu = 0.0; + for (unsigned int i = 0; i < size; ++i) + { + mu += sqr(lastDxBar[i] - dx[i]); + } + mu = (lastErrNorm * errNormTrial) / (std::sqrt(mu) * errNorm) * damping; + + damping = std::min(1.0, mu); + } - // Main loop - for (unsigned int kIter = 0; kIter < maxIter; ++kIter) + // Line search loop: Use regularity test as abort condition + while (damping >= minDamping) { - // Solve F'(x) * dx = F(x) - // Since we have omitted the minus sign here, we have to take care of the negation later - if (!jacobianSolver(point, dx)) + // Compute trial point + for (unsigned int i = 0; i < size; ++i) + trialPoint[i] = point[i] - damping * dx[i]; + + // Evaluate residual and solve linear system + if (!residual(trialPoint, lastResidual)) + return false; + + std::copy(lastResidual, lastResidual + size, lastDxBar); + + if (!jacobianResolver(lastDxBar)) return false; - lastErrNorm = errNorm; - errNorm = linalg::l2Norm(dx, size); + errNormTrial = linalg::l2Norm(lastDxBar, size); + const double theta = errNormTrial / errNorm; - IterateOutputPolicy::outerIteration(kIter, errNorm, dx, point, dx, size); + IterateOutputPolicy::innerIteration(kIter + 1, errNormTrial, lastDxBar, trialPoint, dx, damping, mu, size); - // Convergence test - if (errNorm <= errTol) + // Compute new mu value + const double factor = 1.0 - damping; + for (unsigned int i = 0; i < size; ++i) { - // Solution is x + dx = x - (-dx) - // Note that we have to negate dx here since we didn't do that when solving with the Jacobian above - for (unsigned int i = 0; i < size; ++i) - point[i] -= dx[i]; - return true; + mu += sqr(lastDxBar[i] - factor * dx[i]); } + mu = 0.5 * errNorm * damping * damping / std::sqrt(mu); - if (kIter > 0) + if ((!restricted && (theta >= 1.0)) || (restricted && (theta > 1.0 - damping * 0.25))) { - // Compute prediction of damping factor - mu = 0.0; - for (unsigned int i = 0; i < size; ++i) - { - mu += sqr(lastDxBar[i] - dx[i]); - } - mu = (lastErrNorm * errNormTrial) / (std::sqrt(mu) * errNorm) * damping; - - damping = std::min(1.0, mu); + // Shrink damping and try again + damping = std::min(mu, 0.5 * damping); + continue; } - // Line search loop: Use regularity test as abort condition - while (damping >= minDamping) + const double dampingNew = std::min(1.0, mu); + + if ((damping == 1.0) && (dampingNew == 1.0) && (errNormTrial <= errTol)) { - // Compute trial point + // Convergence detected + // Note that we have to negate dx here since we didn't do that when solving with the Jacobian above for (unsigned int i = 0; i < size; ++i) - trialPoint[i] = point[i] - damping * dx[i]; - - // Evaluate residual and solve linear system - if (!residual(trialPoint, lastResidual)) - return false; + point[i] -= lastDxBar[i]; + return true; + } - std::copy(lastResidual, lastResidual + size, lastDxBar); + if (dampingNew >= 4.0 * damping) + { + damping = dampingNew; + continue; + } - if (!jacobianResolver(lastDxBar)) - return false; + // Accept the step + break; + } - errNormTrial = linalg::l2Norm(lastDxBar, size); - const double theta = errNormTrial / errNorm; + // Check if regularity test failed or line search loop was aborted because of other reasons + if (damping < minDamping) + return false; - IterateOutputPolicy::innerIteration(kIter + 1, errNormTrial, lastDxBar, trialPoint, dx, damping, mu, size); + // Copy accepted point + std::copy(trialPoint, trialPoint + size, point); + std::copy(lastResidual, lastResidual + size, dx); + } - // Compute new mu value - const double factor = 1.0 - damping; - for (unsigned int i = 0; i < size; ++i) - { - mu += sqr(lastDxBar[i] - factor * dx[i]); - } - mu = 0.5 * errNorm * damping * damping / std::sqrt(mu); - - if ((!restricted && (theta >= 1.0)) || (restricted && (theta > 1.0 - damping * 0.25))) - { - // Shrink damping and try again - damping = std::min(mu, 0.5 * damping); - continue; - } - - const double dampingNew = std::min(1.0, mu); - - if ((damping == 1.0) && (dampingNew == 1.0) && (errNormTrial <= errTol)) - { - // Convergence detected - // Note that we have to negate dx here since we didn't do that when solving with the Jacobian above - for (unsigned int i = 0; i < size; ++i) - point[i] -= lastDxBar[i]; - return true; - } - - if (dampingNew >= 4.0 * damping) - { - damping = dampingNew; - continue; - } - - // Accept the step - break; - } + return false; +} - // Check if regularity test failed or line search loop was aborted because of other reasons - if (damping < minDamping) - return false; +/** + * @brief Uses a more robust adaptive trust-region Newton method for solving nonlinear equations + * @details Wraps robustAdaptiveTrustRegionNewtonMethod() function. + */ +class RobustAdaptiveTrustRegionNewtonSolver : public Solver +{ +public: + RobustAdaptiveTrustRegionNewtonSolver(); + virtual ~RobustAdaptiveTrustRegionNewtonSolver(); - // Copy accepted point - std::copy(trialPoint, trialPoint + size, point); - std::copy(lastResidual, lastResidual + size, dx); - } + static const char* identifier() + { + return "ATRN_ERR"; + } + virtual const char* name() const + { + return RobustAdaptiveTrustRegionNewtonSolver::identifier(); + } + virtual bool configure(IParameterProvider& paramProvider); - return false; + virtual unsigned int workspaceSize(unsigned int problemSize) const + { + // Method requires 4 * problemSize but we add an additional problemSize for preconditioning + return 5 * problemSize; } - /** - * @brief Uses a more robust adaptive trust-region Newton method for solving nonlinear equations - * @details Wraps robustAdaptiveTrustRegionNewtonMethod() function. - */ - class RobustAdaptiveTrustRegionNewtonSolver : public Solver + virtual unsigned int numTuningParameters() const { - public: - RobustAdaptiveTrustRegionNewtonSolver(); - virtual ~RobustAdaptiveTrustRegionNewtonSolver(); + return 3; + } - static const char* identifier() { return "ATRN_ERR"; } - virtual const char* name() const { return RobustAdaptiveTrustRegionNewtonSolver::identifier(); } - virtual bool configure(IParameterProvider& paramProvider); + virtual bool solve(std::function residual, + std::function jacobian, + double tol, double* const point, double* const workingMemory, + linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) const; - virtual unsigned int workspaceSize(unsigned int problemSize) const - { - // Method requires 4 * problemSize but we add an additional problemSize for preconditioning - return 5 * problemSize; - } - - virtual unsigned int numTuningParameters() const { return 3; } - - virtual bool solve(std::function residual, std::function jacobian, - double tol, double* const point, double* const workingMemory, linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) const; - - protected: - double _initDamping; //!< Initial damping factor - double _minDamping; //!< Minimum damping factor - unsigned int _maxIter; //!< Maximum number of iterations - }; +protected: + double _initDamping; //!< Initial damping factor + double _minDamping; //!< Minimum damping factor + unsigned int _maxIter; //!< Maximum number of iterations +}; } // namespace nonlin } // namespace cadet -#endif // LIBCADET_ADAPTRUSTNEWTON_HPP_ +#endif // LIBCADET_ADAPTRUSTNEWTON_HPP_ diff --git a/src/libcadet/nonlin/CompositeSolver.cpp b/src/libcadet/nonlin/CompositeSolver.cpp index ee625146c..9f8a73f1a 100644 --- a/src/libcadet/nonlin/CompositeSolver.cpp +++ b/src/libcadet/nonlin/CompositeSolver.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -22,7 +22,9 @@ namespace cadet namespace nonlin { -CompositeSolver::CompositeSolver() { } +CompositeSolver::CompositeSolver() +{ +} CompositeSolver::~CompositeSolver() { for (std::vector::iterator it = _solvers.begin(); it != _solvers.end(); ++it) @@ -41,7 +43,7 @@ bool CompositeSolver::configure(IParameterProvider& paramProvider) if (s) _solvers.push_back(s); } - } + } // Configure all solvers (including previously added solvers) bool success = true; @@ -53,8 +55,10 @@ bool CompositeSolver::configure(IParameterProvider& paramProvider) return success; } -bool CompositeSolver::solve(std::function residual, std::function jacobian, - double tol, double* const point, double* const workingMemory, linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) const +bool CompositeSolver::solve(std::function residual, + std::function jacobian, + double tol, double* const point, double* const workingMemory, + linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) const { bool success = true; for (std::vector::const_iterator it = _solvers.begin(); it != _solvers.end(); ++it) @@ -70,7 +74,7 @@ unsigned int CompositeSolver::workspaceSize(unsigned int problemSize) const ws = std::max(ws, (*it)->workspaceSize(problemSize)); return ws; } - + unsigned int CompositeSolver::numTuningParameters() const { unsigned int ntp = 0; diff --git a/src/libcadet/nonlin/CompositeSolver.hpp b/src/libcadet/nonlin/CompositeSolver.hpp index 4b7a62003..160c74e6c 100644 --- a/src/libcadet/nonlin/CompositeSolver.hpp +++ b/src/libcadet/nonlin/CompositeSolver.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides composite solvers that subsequently apply several solvers */ @@ -27,36 +27,44 @@ namespace cadet namespace nonlin { - /** - * @brief Applies multiple solvers subsequently - * @details The CompositeSolver owns all its subsolvers and destroys them when it is destroyed. - */ - class CompositeSolver : public Solver +/** + * @brief Applies multiple solvers subsequently + * @details The CompositeSolver owns all its subsolvers and destroys them when it is destroyed. + */ +class CompositeSolver : public Solver +{ +public: + CompositeSolver(); + virtual ~CompositeSolver(); + + static const char* identifier() + { + return "COMPOSITE"; + } + virtual const char* name() const { - public: - CompositeSolver(); - virtual ~CompositeSolver(); + return CompositeSolver::identifier(); + } - static const char* identifier() { return "COMPOSITE"; } - virtual const char* name() const { return CompositeSolver::identifier(); } + virtual bool configure(IParameterProvider& paramProvider); - virtual bool configure(IParameterProvider& paramProvider); + virtual unsigned int workspaceSize(unsigned int problemSize) const; - virtual unsigned int workspaceSize(unsigned int problemSize) const; - - virtual unsigned int numTuningParameters() const; + virtual unsigned int numTuningParameters() const; - virtual bool solve(std::function residual, std::function jacobian, - double tol, double* const point, double* const workingMemory, linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) const; + virtual bool solve(std::function residual, + std::function jacobian, + double tol, double* const point, double* const workingMemory, + linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) const; - virtual void addSubsolver(Solver* const solver); + virtual void addSubsolver(Solver* const solver); - protected: - std::vector _solvers; - }; +protected: + std::vector _solvers; +}; } // namespace nonlin } // namespace cadet -#endif // LIBCADET_COMPOSITESOLVER_HPP_ +#endif // LIBCADET_COMPOSITESOLVER_HPP_ diff --git a/src/libcadet/nonlin/LevenbergMarquardt.cpp b/src/libcadet/nonlin/LevenbergMarquardt.cpp index 1933da3b4..d7afa264b 100644 --- a/src/libcadet/nonlin/LevenbergMarquardt.cpp +++ b/src/libcadet/nonlin/LevenbergMarquardt.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -20,8 +20,12 @@ namespace cadet namespace nonlin { -LevenbergMarquardtSolver::LevenbergMarquardtSolver() : _initDamping(1e-2), _maxIter(50) { } -LevenbergMarquardtSolver::~LevenbergMarquardtSolver() { } +LevenbergMarquardtSolver::LevenbergMarquardtSolver() : _initDamping(1e-2), _maxIter(50) +{ +} +LevenbergMarquardtSolver::~LevenbergMarquardtSolver() +{ +} bool LevenbergMarquardtSolver::configure(IParameterProvider& paramProvider) { @@ -32,13 +36,15 @@ bool LevenbergMarquardtSolver::configure(IParameterProvider& paramProvider) return true; } -bool LevenbergMarquardtSolver::solve(std::function residual, std::function jacobian, - double tol, double* const point, double* const workingMemory, linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) const +bool LevenbergMarquardtSolver::solve( + std::function residual, + std::function jacobian, double tol, + double* const point, double* const workingMemory, linalg::detail::DenseMatrixBase& jacMatrix, + unsigned int size) const { return levenbergMarquardt(residual, jacobian, _maxIter, tol, _initDamping, point, workingMemory, jacMatrix, size); } - } // namespace nonlin } // namespace cadet diff --git a/src/libcadet/nonlin/LevenbergMarquardt.hpp b/src/libcadet/nonlin/LevenbergMarquardt.hpp index 5f0a41f3b..d21c9dfcd 100644 --- a/src/libcadet/nonlin/LevenbergMarquardt.hpp +++ b/src/libcadet/nonlin/LevenbergMarquardt.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides Levenberg-Marquardt methods for solving nonlinear equation systems */ @@ -35,242 +35,274 @@ namespace cadet namespace nonlin { - namespace detail - { - inline bool checkLevenbergMarquardtConvergence(double trialSumSq, double residualSumSq, double maxGrad, - double resTol, double tolOpt, double relFactor) - { - // Check if residual is small enough - if (trialSumSq <= resTol * resTol) - return true; - // Check if gradient is (comparably) small - if (maxGrad <= relFactor * tolOpt) - return true; - // Check if relative decrease in residual is small - if (std::abs(trialSumSq - residualSumSq) <= resTol * residualSumSq) - return true; - - return false; - } - } +namespace detail +{ +inline bool checkLevenbergMarquardtConvergence(double trialSumSq, double residualSumSq, double maxGrad, double resTol, + double tolOpt, double relFactor) +{ + // Check if residual is small enough + if (trialSumSq <= resTol * resTol) + return true; + // Check if gradient is (comparably) small + if (maxGrad <= relFactor * tolOpt) + return true; + // Check if relative decrease in residual is small + if (std::abs(trialSumSq - residualSumSq) <= resTol * residualSumSq) + return true; + + return false; +} +} // namespace detail - /** - * @brief Iterate output policy that does nothing - */ - struct VoidLMIterateOutputPolicy +/** + * @brief Iterate output policy that does nothing + */ +struct VoidLMIterateOutputPolicy +{ + inline static void outerIteration(unsigned int idxIter, double residualNorm, double const* const res, + double const* const x, double const* const dx, double damping, unsigned int size) { - inline static void outerIteration(unsigned int idxIter, double residualNorm, double const* const res, double const* const x, double const* const dx, double damping, unsigned int size) { } - inline static void innerIteration(unsigned int idxIter, double residualNorm, double const* const res, double const* const x, double const* const dx, double damping, unsigned int size) { } - }; - - /** - * @brief Solves nonlinear equations using a Levenberg-Marquardt method - * @details This is a port of the implementation provided by MATLAB. - * - * This algorithm is designed to solve the nonlinear least squares problem - * @f[\begin{align} \text{min}_x \lVert f(x) \rVert_2^2, \qquad f: \mathbb{R}^n \to \mathbb{R}^n. \end{align}@f] - * If the nonlinear equation system - * @f[\begin{align} f(x) = 0 \end{align}@f] - * is solvable, the least squares problem is equivalent to finding such a solution. - * A solution is indicated by the error test - * @f[\begin{align} \left\lVert f(x) \right\rVert_{\ell^2} \leq \text{tol} \end{align}@f] - * with a given tolerance (@p resTol). However, since the system may not possess a solution, - * the algorithm also stops on small gradients - * @f[ \begin{align} \lVert \nabla \frac{1}{2}\lVert f(x) \rVert_2^2 \rVert_\infty = \lVert \left[J_f(x)\right]^T f(x) \rVert_\infty \leq \text{tol} \end{align} @f] - * and stagnating merit function decrease - * @f[ \begin{align} \left\lvert \left\lVert f\left(x_{n+1} \right) \right\rVert_2^2 - \left\lVert f\left(x_{n} \right) \right\rVert_2^2 \right\rvert \leq \text{tol} \left\lVert f\left(x_{n} \right) \right\rVert_2^2. \end{align} @f] - * - * The residuals @f$ f(x) @f$ are evaluated using the given function @p residual. - * The Jacobian @f$ J_f(x) @f$ of @f$ f @f$ at point @f$ x @f$ is evaluated using the - * given function @p jacobian. - * - * The algorithm uses a damping factor @f$ \lambda @f$ to interpolate between gradient descent and - * Newton iterations. The damping factor @f$ \lambda @f$ is adapted in a line-search-like fashion. - * In each iteration, a linear least squares system - * @f[\begin{align} \text{min}_{\Delta x} \lVert \begin{pmatrix}J_f(x) \\ \lambda I \end{pmatrix} \Delta x + \begin{pmatrix}f(x) \\ 0 \end{pmatrix} \rVert_2^2 \end{align}@f] - * is solved, which is equivalent to solving - * @f[\begin{align} \left(\left[ J_f(x) \right]^T J_f(x) + \lambda I\right) \Delta x = -\left[ J_f(x) \right]^T f(x) \end{align}.@f] - * - * The initial damping factor @p damping is usually chosen as @c 1e-2. - * @param [in] residual Function providing the residual @f$ f(x) @f$ at position @f$ x @f$ of the nonlinear equation - * system @f$ f(x) = 0 @f$ to be solved. - * The signature of the function is - * `bool residual(double const* const x, double* const r)` - * where the return value communicates whether the evaluation has been successful. - * On exit, the residual is returned in-place in @f$ r @f$. - * If an error is indicated by returning @c false, the algorithm aborts with failure. - * @param [in] jacobian Function calculating the system Jacobian @f$ J_f(x) @f$ at position @f$ x@f$. - * The signature of the function is - * `bool jacobianSolver(double const* const x, linalg::detail::DenseMatrixBase& jac)` - * where the return value communicates whether the Jacobian has been computed correctly. - * If an error is indicated by returning @c false, the algorithm aborts with failure. - * @param [in] maxIter Maximum number of iterations - * @param [in] resTol Termination criterion on the residual @f$\ell^2@f$-norm - * @param [in] damping Initial damping factor (see details for advice) - * @param [in,out] point On entry initial guess, on exit solution or last iterate - * @param [in] workingMemory Additional memory of size @f$ 7n @f$ required for performing the iterations, where @f$ n @f$ is the problem @p size - * @param [in,out] jacMatrix Dense matrix used for storing and solving the linear systems (e.g., the Jacobian) - * @param [in] size Size of the problem (i.e., number of equations, length of residual, columns of Jacobian etc.) - * @tparam IterateOutputPolicy Policy that handles output of intermediate values (useful for debugging), see VoidLMIterateOutputPolicy - * @return @c true if a solution meeting the residual tolerance was found, @c false otherwise - * @todo Implement scaling of linear systems and norms - */ - template - bool levenbergMarquardt(std::function residual, std::function jacobian, - unsigned int maxIter, double resTol, double damping, double* const point, double* const workingMemory, linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) + } + inline static void innerIteration(unsigned int idxIter, double residualNorm, double const* const res, + double const* const x, double const* const dx, double damping, unsigned int size) { - const double dampingMultiplier = 10.0; - const unsigned int maxTrials = 10; - - // Split working memory into parts - double* const residualMem = workingMemory; - double* const newResidual = workingMemory + 2 * size; - double* const dx = workingMemory + 3 * size; - double* const trialPoint = workingMemory + 4 * size; - double* const workspace = workingMemory + 5 * size; // 2 * size - - unsigned int nTrials = 0; - const double tolOpt = resTol * 1e-4; - - // Allocate matrix for linear least squares problems - // Upper part of matrix is the system Jacobian, lower part is identity matrix for damping - linalg::DenseMatrix factoredJac; - factoredJac.resize(2 * size, size); - - // Get residual and Jacobian - if (!residual(point, residualMem)) - return false; - if (!jacobian(point, jacMatrix)) - return false; + } +}; - // Compute residual sum of squares - double residualSumSq = linalg::l2NormSquared(residualMem, size); +/** + * @brief Solves nonlinear equations using a Levenberg-Marquardt method + * @details This is a port of the implementation provided by MATLAB. + * + * This algorithm is designed to solve the nonlinear least squares problem + * @f[\begin{align} \text{min}_x \lVert f(x) \rVert_2^2, \qquad f: \mathbb{R}^n \to \mathbb{R}^n. + * \end{align}@f] If the nonlinear equation system + * @f[\begin{align} f(x) = 0 \end{align}@f] + * is solvable, the least squares problem is equivalent to finding such a solution. + * A solution is indicated by the error test + * @f[\begin{align} \left\lVert f(x) \right\rVert_{\ell^2} \leq \text{tol} \end{align}@f] + * with a given tolerance (@p resTol). However, since the system may not possess a solution, + * the algorithm also stops on small gradients + * @f[ \begin{align} \lVert \nabla \frac{1}{2}\lVert f(x) \rVert_2^2 \rVert_\infty = \lVert + * \left[J_f(x)\right]^T f(x) \rVert_\infty \leq \text{tol} \end{align} @f] and stagnating merit function decrease + * @f[ \begin{align} \left\lvert \left\lVert f\left(x_{n+1} \right) \right\rVert_2^2 - \left\lVert f\left(x_{n} + * \right) \right\rVert_2^2 \right\rvert \leq \text{tol} \left\lVert f\left(x_{n} \right) \right\rVert_2^2. \end{align} + * @f] + * + * The residuals @f$ f(x) @f$ are evaluated using the given function @p residual. + * The Jacobian @f$ J_f(x) @f$ of @f$ f @f$ at point @f$ x @f$ is evaluated using the + * given function @p jacobian. + * + * The algorithm uses a damping factor @f$ \lambda @f$ to interpolate between gradient descent and + * Newton iterations. The damping factor @f$ \lambda @f$ is adapted in a line-search-like fashion. + * In each iteration, a linear least squares system + * @f[\begin{align} \text{min}_{\Delta x} \lVert \begin{pmatrix}J_f(x) \\ \lambda I \end{pmatrix} \Delta x + + * \begin{pmatrix}f(x) \\ 0 \end{pmatrix} \rVert_2^2 \end{align}@f] is solved, which is equivalent to solving + * @f[\begin{align} \left(\left[ J_f(x) \right]^T J_f(x) + \lambda I\right) \Delta x = -\left[ J_f(x) \right]^T + * f(x) \end{align}.@f] + * + * The initial damping factor @p damping is usually chosen as @c 1e-2. + * @param [in] residual Function providing the residual @f$ f(x) @f$ at position @f$ x @f$ of the nonlinear equation + * system @f$ f(x) = 0 @f$ to be solved. + * The signature of the function is + * `bool residual(double const* const x, double* const r)` + * where the return value communicates whether the evaluation has been successful. + * On exit, the residual is returned in-place in @f$ r @f$. + * If an error is indicated by returning @c false, the algorithm aborts with failure. + * @param [in] jacobian Function calculating the system Jacobian @f$ J_f(x) @f$ at position @f$ x@f$. + * The signature of the function is + * `bool jacobianSolver(double const* const x, linalg::detail::DenseMatrixBase& jac)` + * where the return value communicates whether the Jacobian has been computed correctly. + * If an error is indicated by returning @c false, the algorithm aborts with failure. + * @param [in] maxIter Maximum number of iterations + * @param [in] resTol Termination criterion on the residual @f$\ell^2@f$-norm + * @param [in] damping Initial damping factor (see details for advice) + * @param [in,out] point On entry initial guess, on exit solution or last iterate + * @param [in] workingMemory Additional memory of size @f$ 7n @f$ required for performing the iterations, where @f$ n + * @f$ is the problem @p size + * @param [in,out] jacMatrix Dense matrix used for storing and solving the linear systems (e.g., the Jacobian) + * @param [in] size Size of the problem (i.e., number of equations, length of residual, columns of Jacobian etc.) + * @tparam IterateOutputPolicy Policy that handles output of intermediate values (useful for debugging), see + * VoidLMIterateOutputPolicy + * @return @c true if a solution meeting the residual tolerance was found, @c false otherwise + * @todo Implement scaling of linear systems and norms + */ +template +bool levenbergMarquardt(std::function residual, + std::function jacobian, + unsigned int maxIter, double resTol, double damping, double* const point, + double* const workingMemory, linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) +{ + const double dampingMultiplier = 10.0; + const unsigned int maxTrials = 10; + + // Split working memory into parts + double* const residualMem = workingMemory; + double* const newResidual = workingMemory + 2 * size; + double* const dx = workingMemory + 3 * size; + double* const trialPoint = workingMemory + 4 * size; + double* const workspace = workingMemory + 5 * size; // 2 * size + + unsigned int nTrials = 0; + const double tolOpt = resTol * 1e-4; + + // Allocate matrix for linear least squares problems + // Upper part of matrix is the system Jacobian, lower part is identity matrix for damping + linalg::DenseMatrix factoredJac; + factoredJac.resize(2 * size, size); + + // Get residual and Jacobian + if (!residual(point, residualMem)) + return false; + if (!jacobian(point, jacMatrix)) + return false; - // Compute gradient J^T r - jacMatrix.transposedMultiplyVector(residualMem, newResidual); - const double relFactor = std::max(linalg::linfNorm(newResidual, size), std::sqrt(std::numeric_limits::epsilon())); + // Compute residual sum of squares + double residualSumSq = linalg::l2NormSquared(residualMem, size); - IterateOutputPolicy::outerIteration(0, std::sqrt(residualSumSq), residualMem, point, nullptr, damping, size); + // Compute gradient J^T r + jacMatrix.transposedMultiplyVector(residualMem, newResidual); + const double relFactor = + std::max(linalg::linfNorm(newResidual, size), std::sqrt(std::numeric_limits::epsilon())); - // Check if initial point is already optimal - if (detail::checkLevenbergMarquardtConvergence(std::numeric_limits::infinity(), residualSumSq, linalg::linfNorm(newResidual, size), resTol, tolOpt, relFactor)) - return true; + IterateOutputPolicy::outerIteration(0, std::sqrt(residualSumSq), residualMem, point, nullptr, damping, size); - // Main loop - for (unsigned int kIter = 0; kIter < maxIter; ++kIter) - { - // Copy jacobian matrix into system matrix and reset the rest - factoredJac.submatrixAssign(jacMatrix, 0, 0, jacMatrix.rows(), jacMatrix.columns()); - factoredJac.submatrixSetAll(0.0, size, 0, jacMatrix.rows(), jacMatrix.rows()); + // Check if initial point is already optimal + if (detail::checkLevenbergMarquardtConvergence(std::numeric_limits::infinity(), residualSumSq, + linalg::linfNorm(newResidual, size), resTol, tolOpt, relFactor)) + return true; - factoredJac.rowScaleFactors(workspace, size); - factoredJac.scaleRows(workspace, size); + // Main loop + for (unsigned int kIter = 0; kIter < maxIter; ++kIter) + { + // Copy jacobian matrix into system matrix and reset the rest + factoredJac.submatrixAssign(jacMatrix, 0, 0, jacMatrix.rows(), jacMatrix.columns()); + factoredJac.submatrixSetAll(0.0, size, 0, jacMatrix.rows(), jacMatrix.rows()); - // Add scaled identity matrix for damping and prepare right hand side - const double sqrtDamping = std::sqrt(damping); - for (unsigned int i = 0; i < size; ++i) - { - factoredJac.native(i + size, i) = sqrtDamping; + factoredJac.rowScaleFactors(workspace, size); + factoredJac.scaleRows(workspace, size); - // Negate residual - dx[i] = -residualMem[i] / workspace[i]; + // Add scaled identity matrix for damping and prepare right hand side + const double sqrtDamping = std::sqrt(damping); + for (unsigned int i = 0; i < size; ++i) + { + factoredJac.native(i + size, i) = sqrtDamping; - // Set lower part of right hand side to zero - dx[i + size] = 0.0; - } + // Negate residual + dx[i] = -residualMem[i] / workspace[i]; + + // Set lower part of right hand side to zero + dx[i + size] = 0.0; + } + + // Compute step + if (!factoredJac.leastSquaresSolve(dx, workspace, 2 * size)) + return false; - // Compute step - if (!factoredJac.leastSquaresSolve(dx, workspace, 2 * size)) - return false; + // Compute trial step x_trial = x_cur + step + for (unsigned int i = 0; i < size; ++i) + trialPoint[i] = point[i] + dx[i]; - // Compute trial step x_trial = x_cur + step - for (unsigned int i = 0; i < size; ++i) - trialPoint[i] = point[i] + dx[i]; + // Evaluate residual at new position + if (!residual(trialPoint, newResidual)) + return false; - // Evaluate residual at new position - if (!residual(trialPoint, newResidual)) - return false; + const double trialSumSq = linalg::l2NormSquared(newResidual, size); + if (trialSumSq < residualSumSq) + { + // Copy residual and trial point over + std::copy(newResidual, newResidual + size, residualMem); + std::copy(trialPoint, trialPoint + size, point); - const double trialSumSq = linalg::l2NormSquared(newResidual, size); - if (trialSumSq < residualSumSq) + if (nTrials == 0) { - // Copy residual and trial point over - std::copy(newResidual, newResidual + size, residualMem); - std::copy(trialPoint, trialPoint + size, point); - - if (nTrials == 0) - { - // Previous step was successful, so try to reduce damping - damping /= dampingMultiplier; - } - else - { - // Previous step was not successful, so we still need to evaluate the Jacobian - if (!jacobian(trialPoint, jacMatrix)) - return false; - } - - // Compute gradient J^T r - jacMatrix.transposedMultiplyVector(residualMem, newResidual); - const double maxGrad = linalg::linfNorm(newResidual, size); - - IterateOutputPolicy::outerIteration(kIter + 1, std::sqrt(trialSumSq), residualMem, point, dx, damping, size); - - // Check convergence - if (detail::checkLevenbergMarquardtConvergence(trialSumSq, residualSumSq, maxGrad, resTol, tolOpt, relFactor)) - return true; - - // Update residual sum of squares - residualSumSq = trialSumSq; - - // This was a successful step - nTrials = 0; + // Previous step was successful, so try to reduce damping + damping /= dampingMultiplier; } else { - IterateOutputPolicy::innerIteration(kIter + 1, std::sqrt(trialSumSq), residualMem, trialPoint, dx, damping, size); + // Previous step was not successful, so we still need to evaluate the Jacobian + if (!jacobian(trialPoint, jacMatrix)) + return false; + } - // We have failed, let's increase damping and try again - damping *= dampingMultiplier; - ++nTrials; + // Compute gradient J^T r + jacMatrix.transposedMultiplyVector(residualMem, newResidual); + const double maxGrad = linalg::linfNorm(newResidual, size); - // Check abort conditions - if (nTrials >= maxTrials) - break; - } + IterateOutputPolicy::outerIteration(kIter + 1, std::sqrt(trialSumSq), residualMem, point, dx, damping, + size); + + // Check convergence + if (detail::checkLevenbergMarquardtConvergence(trialSumSq, residualSumSq, maxGrad, resTol, tolOpt, + relFactor)) + return true; + + // Update residual sum of squares + residualSumSq = trialSumSq; + + // This was a successful step + nTrials = 0; } + else + { + IterateOutputPolicy::innerIteration(kIter + 1, std::sqrt(trialSumSq), residualMem, trialPoint, dx, damping, + size); - return false; + // We have failed, let's increase damping and try again + damping *= dampingMultiplier; + ++nTrials; + + // Check abort conditions + if (nTrials >= maxTrials) + break; + } } - /** - * @brief Uses the Levenberg-Marquardt method for solving nonlinear equations - * @details Wraps levenbergMarquardt() function. - */ - class LevenbergMarquardtSolver : public Solver + return false; +} + +/** + * @brief Uses the Levenberg-Marquardt method for solving nonlinear equations + * @details Wraps levenbergMarquardt() function. + */ +class LevenbergMarquardtSolver : public Solver +{ +public: + LevenbergMarquardtSolver(); + virtual ~LevenbergMarquardtSolver(); + + static const char* identifier() + { + return "LEVMAR"; + } + virtual const char* name() const + { + return LevenbergMarquardtSolver::identifier(); + } + virtual bool configure(IParameterProvider& paramProvider); + + virtual unsigned int workspaceSize(unsigned int problemSize) const + { + return 7 * problemSize; + } + + virtual unsigned int numTuningParameters() const { - public: - LevenbergMarquardtSolver(); - virtual ~LevenbergMarquardtSolver(); - - static const char* identifier() { return "LEVMAR"; } - virtual const char* name() const { return LevenbergMarquardtSolver::identifier(); } - virtual bool configure(IParameterProvider& paramProvider); - - virtual unsigned int workspaceSize(unsigned int problemSize) const { return 7 * problemSize; } - - virtual unsigned int numTuningParameters() const { return 2; } - - virtual bool solve(std::function residual, std::function jacobian, - double tol, double* const point, double* const workingMemory, linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) const; - - protected: - double _initDamping; //!< Initial damping factor - unsigned int _maxIter; //!< Maximum number of iterations - }; + return 2; + } + + virtual bool solve(std::function residual, + std::function jacobian, + double tol, double* const point, double* const workingMemory, + linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) const; + +protected: + double _initDamping; //!< Initial damping factor + unsigned int _maxIter; //!< Maximum number of iterations +}; } // namespace nonlin } // namespace cadet -#endif // LIBCADET_LEVENBERGMARQUARDT_HPP_ +#endif // LIBCADET_LEVENBERGMARQUARDT_HPP_ diff --git a/src/libcadet/nonlin/Solver.cpp b/src/libcadet/nonlin/Solver.cpp index 17dd93768..ecac3a011 100644 --- a/src/libcadet/nonlin/Solver.cpp +++ b/src/libcadet/nonlin/Solver.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -21,23 +21,23 @@ namespace cadet namespace nonlin { - Solver* createSolver(const std::string& name) - { - if (name == AdaptiveTrustRegionNewtonSolver::identifier()) - return new AdaptiveTrustRegionNewtonSolver(); - if (name == RobustAdaptiveTrustRegionNewtonSolver::identifier()) - return new RobustAdaptiveTrustRegionNewtonSolver(); - if (name == LevenbergMarquardtSolver::identifier()) - return new LevenbergMarquardtSolver(); - if (name == CompositeSolver::identifier()) - return new CompositeSolver(); +Solver* createSolver(const std::string& name) +{ + if (name == AdaptiveTrustRegionNewtonSolver::identifier()) + return new AdaptiveTrustRegionNewtonSolver(); + if (name == RobustAdaptiveTrustRegionNewtonSolver::identifier()) + return new RobustAdaptiveTrustRegionNewtonSolver(); + if (name == LevenbergMarquardtSolver::identifier()) + return new LevenbergMarquardtSolver(); + if (name == CompositeSolver::identifier()) + return new CompositeSolver(); - // Default solver - CompositeSolver* cs = new CompositeSolver(); - cs->addSubsolver(new RobustAdaptiveTrustRegionNewtonSolver()); - cs->addSubsolver(new LevenbergMarquardtSolver()); - return cs; - } + // Default solver + CompositeSolver* cs = new CompositeSolver(); + cs->addSubsolver(new RobustAdaptiveTrustRegionNewtonSolver()); + cs->addSubsolver(new LevenbergMarquardtSolver()); + return cs; +} } // namespace nonlin diff --git a/src/libcadet/nonlin/Solver.hpp b/src/libcadet/nonlin/Solver.hpp index fcfc73d2d..480041dca 100644 --- a/src/libcadet/nonlin/Solver.hpp +++ b/src/libcadet/nonlin/Solver.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides a unified interface for nonlinear equation solvers */ @@ -28,91 +28,97 @@ class IParameterProvider; namespace linalg { - namespace detail - { - class DenseMatrixBase; - } +namespace detail +{ +class DenseMatrixBase; } +} // namespace linalg namespace nonlin { +/** + * @brief General interface for all nonlinear equation solvers + * @details Solves nonlinear equations @f$ F(x) = 0 @f$, where @f$ F\colon \mathds{R}^n \to \mathds{R}^n @f$ + * and the Jacobian @f$ J_F(x) @f$ is available. + */ +class Solver +{ +public: + Solver() + { + } + virtual ~Solver() + { + } + /** - * @brief General interface for all nonlinear equation solvers - * @details Solves nonlinear equations @f$ F(x) = 0 @f$, where @f$ F\colon \mathds{R}^n \to \mathds{R}^n @f$ - * and the Jacobian @f$ J_F(x) @f$ is available. + * @brief Returns the name of the nonlinear solver + * @details This name is also used to identify and create the nonlinear solver in the factory. + * @return Name of the nonlinear equation solver */ - class Solver - { - public: - Solver() { } - virtual ~Solver() { } - - /** - * @brief Returns the name of the nonlinear solver - * @details This name is also used to identify and create the nonlinear solver in the factory. - * @return Name of the nonlinear equation solver - */ - virtual const char* name() const = 0; - - /** - * @brief Configures the solver by extracting all parameters from the given @p paramProvider - * @details The scope of the cadet::IParameterProvider is left unchanged on return. - * If no parameters are provided, the solver configuration is left unchanged. - * - * @param [in] paramProvider Parameter provider - * @return @c true if the configuration was successful, otherwise @c false - */ - virtual bool configure(IParameterProvider& paramProvider) = 0; - - /** - * @brief Returns the required amount of working memory (doubles) for a given problem size - * @param [in] problemSize Number of unknowns of the problem - * @return Number of required doubles (working memory) - */ - virtual unsigned int workspaceSize(unsigned int problemSize) const = 0; - - /** - * @brief Returns the number of additional tuning parameters - * @return Number of additional tuning parameters - */ - virtual unsigned int numTuningParameters() const = 0; - - /** - * @brief Solves the nonlinear equation system - * @param [in] residual Function providing the residual @f$ f(x) @f$ at position @f$ x @f$ of the nonlinear equation - * system @f$ f(x) = 0 @f$ to be solved. - * The signature of the function is - * `bool residual(double const* const x, double* const r)` - * where the return value communicates whether the evaluation has been successful. - * On exit, the residual is returned in-place in @f$ r @f$. - * If an error is indicated by returning @c false, the algorithm aborts with failure. - * @param [in] jacobian Function calculating the system Jacobian @f$ J_f(x) @f$ at position @f$ x@f$. - * The signature of the function is - * `bool jacobianSolver(double const* const x, linalg::detail::DenseMatrixBase& jac)` - * where the return value communicates whether the Jacobian has been computed correctly. - * If an error is indicated by returning @c false, the algorithm aborts with failure. - * @param [in] tol Error tolerance used as termination criterion - * @param [in,out] point On entry initial guess, on exit solution or last iterate - * @param [in] workingMemory Additional memory, size is given by workspaceSize() function - * @param [in,out] jacMatrix Dense matrix used for storing and solving the linear systems (e.g., the Jacobian) - * @param [in] size Size of the problem (i.e., number of equations, length of residual, columns of Jacobian etc.) - * @return @c true if a solution meeting the residual tolerance was found, @c false otherwise - */ - virtual bool solve(std::function residual, std::function jacobian, - double tol, double* const point, double* const workingMemory, linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) const = 0; - }; + virtual const char* name() const = 0; /** - * @brief Creates solvers with the given @p name - * @details If a solver with the requested @p name does not exist, a default solver is returned. - * @param [in] name Name of the solver - * @return The requested solver or the default solver if a solver with the given name does not exist + * @brief Configures the solver by extracting all parameters from the given @p paramProvider + * @details The scope of the cadet::IParameterProvider is left unchanged on return. + * If no parameters are provided, the solver configuration is left unchanged. + * + * @param [in] paramProvider Parameter provider + * @return @c true if the configuration was successful, otherwise @c false */ - Solver* createSolver(const std::string& name); + virtual bool configure(IParameterProvider& paramProvider) = 0; + + /** + * @brief Returns the required amount of working memory (doubles) for a given problem size + * @param [in] problemSize Number of unknowns of the problem + * @return Number of required doubles (working memory) + */ + virtual unsigned int workspaceSize(unsigned int problemSize) const = 0; + + /** + * @brief Returns the number of additional tuning parameters + * @return Number of additional tuning parameters + */ + virtual unsigned int numTuningParameters() const = 0; + + /** + * @brief Solves the nonlinear equation system + * @param [in] residual Function providing the residual @f$ f(x) @f$ at position @f$ x @f$ of the nonlinear equation + * system @f$ f(x) = 0 @f$ to be solved. + * The signature of the function is + * `bool residual(double const* const x, double* const r)` + * where the return value communicates whether the evaluation has been successful. + * On exit, the residual is returned in-place in @f$ r @f$. + * If an error is indicated by returning @c false, the algorithm aborts with failure. + * @param [in] jacobian Function calculating the system Jacobian @f$ J_f(x) @f$ at position @f$ x@f$. + * The signature of the function is + * `bool jacobianSolver(double const* const x, linalg::detail::DenseMatrixBase& jac)` + * where the return value communicates whether the Jacobian has been computed correctly. + * If an error is indicated by returning @c false, the algorithm aborts with failure. + * @param [in] tol Error tolerance used as termination criterion + * @param [in,out] point On entry initial guess, on exit solution or last iterate + * @param [in] workingMemory Additional memory, size is given by workspaceSize() function + * @param [in,out] jacMatrix Dense matrix used for storing and solving the linear systems (e.g., the Jacobian) + * @param [in] size Size of the problem (i.e., number of equations, length of residual, columns of Jacobian etc.) + * @return @c true if a solution meeting the residual tolerance was found, @c false otherwise + */ + virtual bool solve(std::function residual, + std::function jacobian, + double tol, double* const point, double* const workingMemory, + linalg::detail::DenseMatrixBase& jacMatrix, unsigned int size) const = 0; +}; + +/** + * @brief Creates solvers with the given @p name + * @details If a solver with the requested @p name does not exist, a default solver is returned. + * @param [in] name Name of the solver + * @return The requested solver or the default solver if a solver with the given name does not exist + */ +Solver* createSolver(const std::string& name); } // namespace nonlin } // namespace cadet -#endif // LIBCADET_NONLINGENERALSOLVER_HPP_ +#endif // LIBCADET_NONLINGENERALSOLVER_HPP_ diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index f72143f6a..2dac834a4 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -1,9 +1,9 @@ # ============================================================================= # CADET -# +# # Copyright © The CADET Authors # Please see the CONTRIBUTORS.md file. -# +# # All rights reserved. This program and the accompanying materials # are made available under the terms of the GNU Public License v3.0 (or, at # your option, any later version) which accompanies this distribution, and @@ -60,4 +60,3 @@ unset(TOOLS_TARGETS) # Info message message(STATUS "Added tools") - diff --git a/src/tools/FormatConverter.cpp b/src/tools/FormatConverter.cpp index f8754bfd6..624714769 100644 --- a/src/tools/FormatConverter.cpp +++ b/src/tools/FormatConverter.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -15,11 +15,12 @@ #include -template -class DualScope +template class DualScope { public: - DualScope(reader_t& reader, writer_t& writer) : _reader(reader), _writer(writer) { } + DualScope(reader_t& reader, writer_t& writer) : _reader(reader), _writer(writer) + { + } DualScope(reader_t& reader, writer_t& writer, const std::string& scope) : _reader(reader), _writer(writer) { _reader.pushGroup(scope); @@ -30,14 +31,15 @@ class DualScope _reader.popGroup(); _writer.popGroup(); } + protected: reader_t& _reader; writer_t& _writer; }; - template -void copyGroup(reader_t& rd, writer_t& wr, const std::string& path, const std::function& filter) +void copyGroup(reader_t& rd, writer_t& wr, const std::string& path, + const std::function& filter) { const std::vector names = rd.itemNames(); @@ -165,14 +167,14 @@ void copyGroup(reader_t& rd, writer_t& wr, const std::string& path, const std::f } } - -template -void copyGroup(reader_t& rd, writer_t& wr, const std::string& path) +template void copyGroup(reader_t& rd, writer_t& wr, const std::string& path) { - copyGroup(rd, wr, path, [](const std::string& scope, const std::string& item) -> bool { return true; }); + copyGroup(rd, wr, path, + [](const std::string& scope, const std::string& item) -> bool { return true; }); } void copyGroup(cadet::io::IFileReader& rd, cadet::io::IFileWriter& wr, const std::string& path) { - copyGroup(rd, wr, path, [](const std::string& scope, const std::string& item) -> bool { return true; }); + copyGroup( + rd, wr, path, [](const std::string& scope, const std::string& item) -> bool { return true; }); } diff --git a/src/tools/FormatConverter.hpp b/src/tools/FormatConverter.hpp index 23f4fc362..a932f88ce 100644 --- a/src/tools/FormatConverter.hpp +++ b/src/tools/FormatConverter.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -20,8 +20,8 @@ namespace cadet namespace io { - class IFileReader; - class IFileWriter; +class IFileReader; +class IFileWriter; } // namespace io } // namespace cadet diff --git a/src/tools/ToolsHelper.hpp b/src/tools/ToolsHelper.hpp index cd913ec01..d43abb330 100644 --- a/src/tools/ToolsHelper.hpp +++ b/src/tools/ToolsHelper.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Provides helper functions for tools apps */ @@ -25,42 +25,54 @@ #include -template -class Scope +template class Scope { public: - Scope(Writer_t& writer) : _writer(writer) { } - Scope(Writer_t& writer, const std::string& scope) : _writer(writer) { _writer.pushGroup(scope); } - ~Scope() { _writer.popGroup(); } + Scope(Writer_t& writer) : _writer(writer) + { + } + Scope(Writer_t& writer, const std::string& scope) : _writer(writer) + { + _writer.pushGroup(scope); + } + ~Scope() + { + _writer.popGroup(); + } + protected: Writer_t& _writer; }; - inline void split(const std::string& s, const char delim, std::vector& elems) { std::stringstream ss(s); std::string item; - while(std::getline(ss, item, delim)) + while (std::getline(ss, item, delim)) elems.push_back(item); } -template -inline void addMiscToCmdLine(TCLAP::CmdLine& cmd, ProgramOptions_t& opts) +template inline void addMiscToCmdLine(TCLAP::CmdLine& cmd, ProgramOptions_t& opts) { - cmd >> (new TCLAP::ValueArg("", "par", "Number of particle cells (default: 4)", false, 4, "Value"))->storeIn(&opts.nPar); - cmd >> (new TCLAP::ValueArg("", "col", "Number of axial cells (default: 10)", false, 10, "Value"))->storeIn(&opts.nCol); + cmd >> (new TCLAP::ValueArg("", "par", "Number of particle cells (default: 4)", false, 4, "Value")) + ->storeIn(&opts.nPar); + cmd >> (new TCLAP::ValueArg("", "col", "Number of axial cells (default: 10)", false, 10, "Value")) + ->storeIn(&opts.nCol); cmd >> (new TCLAP::SwitchArg("", "solverTimes", "Save all solver timesteps"))->storeIn(&opts.solverTimes); - cmd >> (new TCLAP::SwitchArg("k", "kinetic", "Kinetic adsorption model used (default: quasi-stationary)"))->storeIn(&opts.isKinetic); - cmd >> (new TCLAP::ValueArg("j", "threads", "Number of threads (default: 1)", false, 1, "Value"))->storeIn(&opts.nThreads); - cmd >> (new TCLAP::SwitchArg("", "ad", "Calculate Jacobian using AD (default: analytic)"))->storeIn(&opts.adJacobian); + cmd >> (new TCLAP::SwitchArg("k", "kinetic", "Kinetic adsorption model used (default: quasi-stationary)")) + ->storeIn(&opts.isKinetic); + cmd >> (new TCLAP::ValueArg("j", "threads", "Number of threads (default: 1)", false, 1, "Value")) + ->storeIn(&opts.nThreads); + cmd >> + (new TCLAP::SwitchArg("", "ad", "Calculate Jacobian using AD (default: analytic)"))->storeIn(&opts.adJacobian); } inline void addUnitTypeToCmdLine(TCLAP::CmdLine& cmd, std::string& unitType) { - cmd >> (new TCLAP::ValueArg("u", "unit", "Unit operation (default: GRM)", false, "GRM", "Unit"))->storeIn(&unitType); + cmd >> (new TCLAP::ValueArg("u", "unit", "Unit operation (default: GRM)", false, "GRM", "Unit")) + ->storeIn(&unitType); } inline void parseUnitType(std::string& unitType) @@ -78,17 +90,19 @@ inline void parseUnitType(std::string& unitType) inline void parseUnitType(std::string& unitType, bool radialFlow) { parseUnitType(unitType); - + if (radialFlow) unitType = "RADIAL_" + unitType; } inline void addSensitivitiyParserToCmdLine(TCLAP::CmdLine& cmd, std::vector& sensitivities) { - cmd >> (new TCLAP::MultiArg("S", "sens", - "Add parameter sensitivity (use -1 for component, reaction, section, bound phase, or unit operation independent parameters)", - false, "ParamName/Comp/Reaction/Section/ParType/BoundPhase[/Factor/UnitOp][+ParamName/...]") - )->storeIn(&sensitivities); + cmd >> (new TCLAP::MultiArg( + "S", "sens", + "Add parameter sensitivity (use -1 for component, reaction, section, bound phase, or unit operation " + "independent parameters)", + false, "ParamName/Comp/Reaction/Section/ParType/BoundPhase[/Factor/UnitOp][+ParamName/...]")) + ->storeIn(&sensitivities); } template @@ -133,7 +147,8 @@ inline void parseAndWriteSensitivitiesFromCmdLine(Writer_t& writer, const std::v if (tokens.size() < 6) { - std::cout << "Warning: Invalid parameter no " << (i+1) << "." << (j+1) << " (" << fusedParams[j] << ") was ignored" << std::endl; + std::cout << "Warning: Invalid parameter no " << (i + 1) << "." << (j + 1) << " (" << fusedParams[j] + << ") was ignored" << std::endl; continue; } @@ -176,7 +191,8 @@ inline void parseAndWriteSensitivitiesFromCmdLine(Writer_t& writer, const std::v ++correctParams; } else - std::cout << "Warning: Invalid parameter " << (i+1) << " (" << sensitivities[i] << ") was ignored" << std::endl; + std::cout << "Warning: Invalid parameter " << (i + 1) << " (" << sensitivities[i] << ") was ignored" + << std::endl; } writer.template scalar("NSENS", correctParams); @@ -184,19 +200,19 @@ inline void parseAndWriteSensitivitiesFromCmdLine(Writer_t& writer, const std::v inline void addOutputParserToCmdLine(TCLAP::CmdLine& cmd, std::string& outSol) { - cmd >> (new TCLAP::ValueArg("", "outSol", - "Solution output format ([I]nlet,[O]utlet,[B]ulk,[P]article,[F]luxes, default: O)", - false, "O", "IOBPF") - )->storeIn(&outSol); + cmd >> (new TCLAP::ValueArg( + "", "outSol", "Solution output format ([I]nlet,[O]utlet,[B]ulk,[P]article,[F]luxes, default: O)", false, + "O", "IOBPF")) + ->storeIn(&outSol); } inline void addOutputParserToCmdLine(TCLAP::CmdLine& cmd, std::string& outSol, std::string& outSens) { addOutputParserToCmdLine(cmd, outSol); - cmd >> (new TCLAP::ValueArg("", "outSens", - "Sensitivity output format ([I]nlet,[O]utlet,[B]ulk,[P]article,[F]luxes, default: O)", - false, "O", "IOBPF") - )->storeIn(&outSens); + cmd >> (new TCLAP::ValueArg( + "", "outSens", "Sensitivity output format ([I]nlet,[O]utlet,[B]ulk,[P]article,[F]luxes, default: O)", + false, "O", "IOBPF")) + ->storeIn(&outSens); } template @@ -212,21 +228,21 @@ inline void parseAndWriteOutputFormatInternal(Writer_t& writer, const std::strin { switch (outVal[i]) { - case 'I': - inlet = true; - break; - case 'O': - outlet = true; - break; - case 'B': - bulk = true; - break; - case 'P': - particle = true; - break; - case 'F': - flux = true; - break; + case 'I': + inlet = true; + break; + case 'O': + outlet = true; + break; + case 'B': + bulk = true; + break; + case 'P': + particle = true; + break; + case 'F': + flux = true; + break; } } writer.template scalar("WRITE_" + prefix + "_BULK", bulk); @@ -237,17 +253,17 @@ inline void parseAndWriteOutputFormatInternal(Writer_t& writer, const std::strin } template -inline void parseAndWriteOutputFormatsFromCmdLine(Writer_t& writer, const std::string& outSol, const std::string& outSens) +inline void parseAndWriteOutputFormatsFromCmdLine(Writer_t& writer, const std::string& outSol, + const std::string& outSens) { parseAndWriteOutputFormatInternal(writer, "SOLUTION", outSol); parseAndWriteOutputFormatInternal(writer, "SENS", outSens); } -template -inline void parseAndWriteOutputFormatsFromCmdLine(Writer_t& writer, const std::string& outSol) +template inline void parseAndWriteOutputFormatsFromCmdLine(Writer_t& writer, const std::string& outSol) { parseAndWriteOutputFormatInternal(writer, "SOLUTION", outSol); writer.template scalar("WRITE_SOLUTION_TIMES", true); } -#endif // CADETTOOLS_TOOLSHELPER_HPP_ +#endif // CADETTOOLS_TOOLSHELPER_HPP_ diff --git a/src/tools/convertFile.cpp b/src/tools/convertFile.cpp index b8c87b680..a652c9395 100644 --- a/src/tools/convertFile.cpp +++ b/src/tools/convertFile.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -20,23 +20,32 @@ #include "common/TclapUtils.hpp" #include "FormatConverter.hpp" - -template -class FileCloser +template class FileCloser { public: - FileCloser(Writer_t& writer) : _writer(writer) { } - ~FileCloser() { _writer.closeFile(); } + FileCloser(Writer_t& writer) : _writer(writer) + { + } + ~FileCloser() + { + _writer.closeFile(); + } + protected: Writer_t& _writer; }; -template -class DeleterScope +template class DeleterScope { public: - DeleterScope(class_t* elem) : _elem(elem) { } - ~DeleterScope() { delete _elem; } + DeleterScope(class_t* elem) : _elem(elem) + { + } + ~DeleterScope() + { + delete _elem; + } + protected: class_t* const _elem; }; @@ -46,7 +55,8 @@ void translateFormat(cadet::io::IFileReader* rd, cadet::io::IFileWriter* wr) copyGroup(*rd, *wr, "/"); } -bool run(const std::string& inFileName, const std::string& inFileExt, const std::string& outFileName, const std::string& outFileExt, bool translate) +bool run(const std::string& inFileName, const std::string& inFileExt, const std::string& outFileName, + const std::string& outFileExt, bool translate) { cadet::io::IFileReader* rd = cadet::io::createReader(inFileExt); if (!rd) @@ -93,13 +103,15 @@ int main(int argc, char** argv) TCLAP::CmdLine cmd("Converts between CADET file format versions", ' ', "1.0"); cmd.setOutput(&customOut); - cmd >> (new TCLAP::SwitchArg("t", "translate", "Translate file format type keeping its version"))->storeIn(&translateFormat); + cmd >> (new TCLAP::SwitchArg("t", "translate", "Translate file format type keeping its version")) + ->storeIn(&translateFormat); cmd >> (new TCLAP::UnlabeledValueArg("input", "Input file", true, "", "File"))->storeIn(&inFile); - cmd >> (new TCLAP::UnlabeledValueArg("output", "Output file", false, "", "File"))->storeIn(&outFile); + cmd >> + (new TCLAP::UnlabeledValueArg("output", "Output file", false, "", "File"))->storeIn(&outFile); cmd.parse(argc, argv); } - catch (const TCLAP::ArgException &e) + catch (const TCLAP::ArgException& e) { std::cerr << "ERROR: " << e.error() << " for argument " << e.argId() << std::endl; return 1; @@ -118,12 +130,12 @@ int main(int argc, char** argv) std::cerr << "Could not deduce input filetype due to missing extension: " << inFile << std::endl; return 2; } - const std::string fileExtIn = inFile.substr(dotPosIn+1); + const std::string fileExtIn = inFile.substr(dotPosIn + 1); // Set output file name if (outFile.empty()) { - outFile = inFile.substr(0, dotPosIn) + "-converted." + fileExtIn; + outFile = inFile.substr(0, dotPosIn) + "-converted." + fileExtIn; } const std::size_t dotPosOut = outFile.find_last_of('.'); @@ -132,7 +144,7 @@ int main(int argc, char** argv) std::cerr << "Could not deduce output filetype due to missing extension: " << outFile << std::endl; return 2; } - const std::string fileExtOut = outFile.substr(dotPosOut+1); + const std::string fileExtOut = outFile.substr(dotPosOut + 1); try { diff --git a/src/tools/createConvBenchmark.cpp b/src/tools/createConvBenchmark.cpp index 21b34e18e..aaca4efda 100644 --- a/src/tools/createConvBenchmark.cpp +++ b/src/tools/createConvBenchmark.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -38,19 +38,24 @@ int main(int argc, char** argv) try { TCLAP::CustomOutputWithoutVersion customOut("createConvBenchmark"); - TCLAP::CmdLine cmd("Create an HDF5 input file for a single component linear benchmark case with pulse injection", ' ', "1.0"); + TCLAP::CmdLine cmd( + "Create an HDF5 input file for a single component linear benchmark case with pulse injection", ' ', "1.0"); cmd.setOutput(&customOut); - cmd >> (new TCLAP::ValueArg("o", "out", "Write output to file (default: SCLinPulse.h5)", false, "SCLinPulse.h5", "File"))->storeIn(&opts.fileName); - cmd >> (new TCLAP::SwitchArg("k", "kinetic", "Kinetic adsorption model used (default: quasi-stationary)"))->storeIn(&opts.isKinetic); - cmd >> (new TCLAP::SwitchArg("n", "nonbinding", "Create nonbinding model (default: binding)"))->storeIn(&opts.nonBinding); + cmd >> (new TCLAP::ValueArg("o", "out", "Write output to file (default: SCLinPulse.h5)", false, + "SCLinPulse.h5", "File")) + ->storeIn(&opts.fileName); + cmd >> (new TCLAP::SwitchArg("k", "kinetic", "Kinetic adsorption model used (default: quasi-stationary)")) + ->storeIn(&opts.isKinetic); + cmd >> (new TCLAP::SwitchArg("n", "nonbinding", "Create nonbinding model (default: binding)")) + ->storeIn(&opts.nonBinding); addUnitTypeToCmdLine(cmd, opts.unitType); addSensitivitiyParserToCmdLine(cmd, opts.sensitivities); addOutputParserToCmdLine(cmd, opts.outSol, opts.outSens); cmd.parse(argc, argv); } - catch (const TCLAP::ArgException &e) + catch (const TCLAP::ArgException& e) { std::cerr << "ERROR: " << e.error() << " for argument " << e.argId() << std::endl; return 1; @@ -77,7 +82,7 @@ int main(int argc, char** argv) // Transport writer.scalar("VELOCITY", 0.5 / 100.0 / 60.0); - writer.scalar("COL_DISPERSION", 0.002 / ( 100.0 * 100.0 * 60.0)); + writer.scalar("COL_DISPERSION", 0.002 / (100.0 * 100.0 * 60.0)); writer.scalar("COL_DISPERSION_RADIAL", 1e-6); const double filmDiff[] = {0.01 / 100.0 / 60.0}; @@ -105,12 +110,12 @@ int main(int argc, char** argv) // Adsorption if (opts.nonBinding) { - const int nBound[] = { 0 }; + const int nBound[] = {0}; writer.vector("NBOUND", 1, nBound); } else { - const int nBound[] = { 1 }; + const int nBound[] = {1}; writer.vector("NBOUND", 1, nBound); } @@ -120,7 +125,7 @@ int main(int argc, char** argv) writer.scalar("ADSORPTION_MODEL", std::string("LINEAR")); { Scope s2(writer, "adsorption"); - + if (opts.isKinetic) writer.scalar("IS_KINETIC", 1); else @@ -236,9 +241,8 @@ int main(int argc, char** argv) { // Connection list is 3x7 since we have 1 connection between // the two unit operations with 3 ports (and we need to have 7 columns) - const double connMatrix[] = {1, 0, 0, 0, -1, -1, 1.16355283e-09, - 1, 0, 0, 1, -1, -1, 3.49065850e-09, - 1, 0, 0, 2, -1, -1, 5.81776417e-09}; + const double connMatrix[] = {1, 0, 0, 0, -1, -1, 1.16355283e-09, 1, 0, 0, 1, -1, -1, 3.49065850e-09, + 1, 0, 0, 2, -1, -1, 5.81776417e-09}; // Connections: From unit operation 1 port 0 // to unit operation 0 port 0, // connect component -1 (i.e., all components) @@ -251,7 +255,7 @@ int main(int argc, char** argv) // This switch occurs at beginning of section 0 (initial configuration) writer.scalar("SECTION", 0); } - } + } // Solver settings { @@ -268,7 +272,7 @@ int main(int argc, char** argv) { Scope s(writer, "return"); writer.template scalar("WRITE_SOLUTION_TIMES", true); - + Scope s2(writer, "unit_000"); parseAndWriteOutputFormatsFromCmdLine(writer, opts.outSol, opts.outSens); } @@ -278,7 +282,7 @@ int main(int argc, char** argv) Scope s(writer, "solver"); std::vector solTimes; - solTimes.reserve(100*60+1); + solTimes.reserve(100 * 60 + 1); for (int t = 0; t <= 100 * 60; t += 1) solTimes.push_back(t); diff --git a/src/tools/createLWE.cpp b/src/tools/createLWE.cpp index a636a55b5..1282eade8 100644 --- a/src/tools/createLWE.cpp +++ b/src/tools/createLWE.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -25,7 +25,6 @@ #include "io/hdf5/HDF5Writer.hpp" #include "ToolsHelper.hpp" - struct ProgramOptions { std::string fileName; @@ -52,627 +51,629 @@ struct ProgramOptions void configureDiscretization(cadet::io::HDF5Writer& writer, int nCol, int nParType, int nPar, int nRad, bool adJacobian) { - Scope s2(writer, "discretization"); - - writer.scalar("NCOL", nCol); // 64 - if (nParType > 1) - { - const std::vector nPar_vec(nParType, nPar); - writer.vector("NPAR", nPar_vec.size(), nPar_vec.data()); // 16 - } - else - writer.scalar("NPAR", nPar); - - if (nRad > 1) - writer.scalar("NRAD", nRad); - writer.scalar("RADIAL_DISC_TYPE", std::string("EQUIDISTANT")); - - if (nParType > 1) - { - std::vector parDiscType(nParType, std::string("EQUIDISTANT_PAR")); - writer.vector("PAR_DISC_TYPE", parDiscType.size(), parDiscType.data()); - } - else - writer.scalar("PAR_DISC_TYPE", std::string("EQUIDISTANT_PAR")); - - writer.scalar("USE_ANALYTIC_JACOBIAN", !adJacobian); - writer.scalar("MAX_KRYLOV", 0); - writer.scalar("GS_TYPE", 1); - writer.scalar("MAX_RESTARTS", 10); - writer.scalar("SCHUR_SAFETY", 1e-8); - - // WENO - { - Scope s3(writer, "weno"); - - writer.scalar("WENO_ORDER", 3); - writer.scalar("BOUNDARY_MODEL", 0); - writer.scalar("WENO_EPS", 1e-10); - } + Scope s2(writer, "discretization"); + + writer.scalar("NCOL", nCol); // 64 + if (nParType > 1) + { + const std::vector nPar_vec(nParType, nPar); + writer.vector("NPAR", nPar_vec.size(), nPar_vec.data()); // 16 + } + else + writer.scalar("NPAR", nPar); + + if (nRad > 1) + writer.scalar("NRAD", nRad); + writer.scalar("RADIAL_DISC_TYPE", std::string("EQUIDISTANT")); + + if (nParType > 1) + { + std::vector parDiscType(nParType, std::string("EQUIDISTANT_PAR")); + writer.vector("PAR_DISC_TYPE", parDiscType.size(), parDiscType.data()); + } + else + writer.scalar("PAR_DISC_TYPE", std::string("EQUIDISTANT_PAR")); + + writer.scalar("USE_ANALYTIC_JACOBIAN", !adJacobian); + writer.scalar("MAX_KRYLOV", 0); + writer.scalar("GS_TYPE", 1); + writer.scalar("MAX_RESTARTS", 10); + writer.scalar("SCHUR_SAFETY", 1e-8); + + // WENO + { + Scope s3(writer, "weno"); + + writer.scalar("WENO_ORDER", 3); + writer.scalar("BOUNDARY_MODEL", 0); + writer.scalar("WENO_EPS", 1e-10); + } } void configureParticles(cadet::io::HDF5Writer& writer, int nParType) { - const double par_radius = 4.5e-5; - const double par_coreradius = 0.0; - const double par_porosity = 0.75; - - writer.scalar("NPARTYPE", nParType); - - if (nParType > 1) - { - std::vector par_geom; - std::vector par_radii; - std::vector par_coreradii; - std::vector par_porosities; - std::vector par_volfrac; - for (int i = 0; i < nParType; ++i) { - par_radii.push_back(par_radius); - par_geom.push_back("SPHERE"); - par_coreradii.push_back(par_coreradius); - par_porosities.push_back(par_porosity); - par_volfrac.push_back(1.0 / static_cast(nParType)); - } - - writer.vector("PAR_GEOM", par_geom.size(), par_geom.data()); - writer.vector("PAR_RADIUS", par_radii.size(), par_radii.data()); - writer.vector("PAR_CORERADIUS", par_coreradii.size(), par_coreradii.data()); - writer.vector("PAR_POROSITY", par_porosities.size(), par_porosities.data()); - writer.vector("PAR_TYPE_VOLFRAC", nParType, par_volfrac.data()); - } - else - { - writer.scalar("PAR_GEOM", std::string("SPHERE")); - writer.scalar("PAR_RADIUS", par_radius); - writer.scalar("PAR_CORERADIUS", par_coreradius); - writer.scalar("PAR_POROSITY", par_porosity); - } + const double par_radius = 4.5e-5; + const double par_coreradius = 0.0; + const double par_porosity = 0.75; + + writer.scalar("NPARTYPE", nParType); + + if (nParType > 1) + { + std::vector par_geom; + std::vector par_radii; + std::vector par_coreradii; + std::vector par_porosities; + std::vector par_volfrac; + for (int i = 0; i < nParType; ++i) + { + par_radii.push_back(par_radius); + par_geom.push_back("SPHERE"); + par_coreradii.push_back(par_coreradius); + par_porosities.push_back(par_porosity); + par_volfrac.push_back(1.0 / static_cast(nParType)); + } + + writer.vector("PAR_GEOM", par_geom.size(), par_geom.data()); + writer.vector("PAR_RADIUS", par_radii.size(), par_radii.data()); + writer.vector("PAR_CORERADIUS", par_coreradii.size(), par_coreradii.data()); + writer.vector("PAR_POROSITY", par_porosities.size(), par_porosities.data()); + writer.vector("PAR_TYPE_VOLFRAC", nParType, par_volfrac.data()); + } + else + { + writer.scalar("PAR_GEOM", std::string("SPHERE")); + writer.scalar("PAR_RADIUS", par_radius); + writer.scalar("PAR_CORERADIUS", par_coreradius); + writer.scalar("PAR_POROSITY", par_porosity); + } } void configureAdsorption(cadet::io::HDF5Writer& writer, int nParType, bool isKinetic) { - const std::vector nBound(4 * nParType, 1); - writer.vector("NBOUND", nBound.size(), nBound.data()); - - if (nParType > 1) - { - std::vector adsorption_models; - for (int i = 0; i < nParType; ++i) { - adsorption_models.push_back("STERIC_MASS_ACTION"); - } - writer.vector("ADSORPTION_MODEL", adsorption_models.size(), adsorption_models.data()); - writer.scalar("ADSORPTION_MODEL_MULTIPLEX", 0); - } - else - writer.scalar("ADSORPTION_MODEL", std::string("STERIC_MASS_ACTION")); - - const double kA[] = { 0.0, 35.5, 1.59, 7.7 }; - const double kD[] = { 0.0, 1000.0, 1000.0, 1000.0 }; - const double smaLambda = 1.2e3; - const double nu[] = { 0.0, 4.7, 5.29, 3.7 }; - const double sigma[] = { 0.0, 11.83, 10.6, 10.0 }; - - - for (int i = 0; i < nParType; ++i) - { - if (nParType > 1) - { - std::stringstream ss; - ss << std::setfill('0') << std::setw(3) << i; - std::string parIdx = ss.str(); - Scope s2(writer, "adsorption_" + parIdx); - - writer.scalar("IS_KINETIC", isKinetic); - writer.vector("SMA_KA", 4, kA); - writer.vector("SMA_KD", 4, kD); - writer.scalar("SMA_LAMBDA", smaLambda); - writer.vector("SMA_NU", 4, nu); - writer.vector("SMA_SIGMA", 4, sigma); - } - else - { - Scope s2(writer, "adsorption"); - - writer.scalar("IS_KINETIC", isKinetic); - writer.vector("SMA_KA", 4, kA); - writer.vector("SMA_KD", 4, kD); - writer.scalar("SMA_LAMBDA", smaLambda); - writer.vector("SMA_NU", 4, nu); - writer.vector("SMA_SIGMA", 4, sigma); - } - } + const std::vector nBound(4 * nParType, 1); + writer.vector("NBOUND", nBound.size(), nBound.data()); + + if (nParType > 1) + { + std::vector adsorption_models; + for (int i = 0; i < nParType; ++i) + { + adsorption_models.push_back("STERIC_MASS_ACTION"); + } + writer.vector("ADSORPTION_MODEL", adsorption_models.size(), adsorption_models.data()); + writer.scalar("ADSORPTION_MODEL_MULTIPLEX", 0); + } + else + writer.scalar("ADSORPTION_MODEL", std::string("STERIC_MASS_ACTION")); + + const double kA[] = {0.0, 35.5, 1.59, 7.7}; + const double kD[] = {0.0, 1000.0, 1000.0, 1000.0}; + const double smaLambda = 1.2e3; + const double nu[] = {0.0, 4.7, 5.29, 3.7}; + const double sigma[] = {0.0, 11.83, 10.6, 10.0}; + + for (int i = 0; i < nParType; ++i) + { + if (nParType > 1) + { + std::stringstream ss; + ss << std::setfill('0') << std::setw(3) << i; + std::string parIdx = ss.str(); + Scope s2(writer, "adsorption_" + parIdx); + + writer.scalar("IS_KINETIC", isKinetic); + writer.vector("SMA_KA", 4, kA); + writer.vector("SMA_KD", 4, kD); + writer.scalar("SMA_LAMBDA", smaLambda); + writer.vector("SMA_NU", 4, nu); + writer.vector("SMA_SIGMA", 4, sigma); + } + else + { + Scope s2(writer, "adsorption"); + + writer.scalar("IS_KINETIC", isKinetic); + writer.vector("SMA_KA", 4, kA); + writer.vector("SMA_KD", 4, kD); + writer.scalar("SMA_LAMBDA", smaLambda); + writer.vector("SMA_NU", 4, nu); + writer.vector("SMA_SIGMA", 4, sigma); + } + } } void configureFilmDiffusion(cadet::io::HDF5Writer& writer, int nComp, int nParType, bool velocityDependence) { - const double filmDiff[] = { 6.9e-6, 6.9e-6, 6.9e-6, 6.9e-6 }; - if (nParType > 1) - { - writer.scalar("FILM_DIFFUSION_MULTIPLEX", 2); // component and particle type dependent, section independent - - std::vector filmDiffMultiplex; - - for (int i = 0; i < nParType; ++i) { - filmDiffMultiplex.insert(filmDiffMultiplex.end(), filmDiff, filmDiff + nComp); - } - - writer.vector("FILM_DIFFUSION", filmDiffMultiplex.size(), filmDiffMultiplex.data()); - } - else - { - writer.scalar("FILM_DIFFUSION_MULTIPLEX", 0); // componenent dependent, particle type and section independent - writer.vector("FILM_DIFFUSION", 4, filmDiff); - } - - if (velocityDependence) - { - writer.scalar("FILM_DIFFUSION_DEP", "POWER_LAW"); - writer.scalar("FILM_DIFFUSION_DEP_BASE", 1.25); - writer.scalar("FILM_DIFFUSION_DEP_EXPONENT", 1.0); - } + const double filmDiff[] = {6.9e-6, 6.9e-6, 6.9e-6, 6.9e-6}; + if (nParType > 1) + { + writer.scalar("FILM_DIFFUSION_MULTIPLEX", 2); // component and particle type dependent, section independent + + std::vector filmDiffMultiplex; + + for (int i = 0; i < nParType; ++i) + { + filmDiffMultiplex.insert(filmDiffMultiplex.end(), filmDiff, filmDiff + nComp); + } + writer.vector("FILM_DIFFUSION", filmDiffMultiplex.size(), filmDiffMultiplex.data()); + } + else + { + writer.scalar("FILM_DIFFUSION_MULTIPLEX", + 0); // componenent dependent, particle type and section independent + writer.vector("FILM_DIFFUSION", 4, filmDiff); + } + + if (velocityDependence) + { + writer.scalar("FILM_DIFFUSION_DEP", "POWER_LAW"); + writer.scalar("FILM_DIFFUSION_DEP_BASE", 1.25); + writer.scalar("FILM_DIFFUSION_DEP_EXPONENT", 1.0); + } } void configurePoreDiffusion(cadet::io::HDF5Writer& writer, int nComp, int nParType) { - const double parDiff[] = { 7e-10, 6.07e-11, 6.07e-11, 6.07e-11 }; - - if (nParType > 1) - { - std::vector parDiffMultiplex; + const double parDiff[] = {7e-10, 6.07e-11, 6.07e-11, 6.07e-11}; - for (int i = 0; i < nParType; ++i) { - parDiffMultiplex.insert(parDiffMultiplex.end(), parDiff, parDiff + nComp); - } + if (nParType > 1) + { + std::vector parDiffMultiplex; - writer.vector("PAR_DIFFUSION", parDiffMultiplex.size(), parDiffMultiplex.data()); - } - else - { - writer.vector("PAR_DIFFUSION", 4, parDiff); - } + for (int i = 0; i < nParType; ++i) + { + parDiffMultiplex.insert(parDiffMultiplex.end(), parDiff, parDiff + nComp); + } + writer.vector("PAR_DIFFUSION", parDiffMultiplex.size(), parDiffMultiplex.data()); + } + else + { + writer.vector("PAR_DIFFUSION", 4, parDiff); + } } void configureSurfaceDiffusion(cadet::io::HDF5Writer& writer, int nComp, int nParType) { - const double parSurfDiff[] = { 0.0, 0.0, 0.0, 0.0 }; + const double parSurfDiff[] = {0.0, 0.0, 0.0, 0.0}; - if (nParType > 1) - { - std::vector parSurfDiffMultiplex; - - for (int i = 0; i < nParType; ++i) { - parSurfDiffMultiplex.insert(parSurfDiffMultiplex.end(), parSurfDiff, parSurfDiff + nComp); - } + if (nParType > 1) + { + std::vector parSurfDiffMultiplex; - writer.vector("PAR_SURFDIFFUSION", parSurfDiffMultiplex.size(), parSurfDiffMultiplex.data()); - } - else - { - writer.vector("PAR_SURFDIFFUSION", 4, parSurfDiff); - } + for (int i = 0; i < nParType; ++i) + { + parSurfDiffMultiplex.insert(parSurfDiffMultiplex.end(), parSurfDiff, parSurfDiff + nComp); + } + writer.vector("PAR_SURFDIFFUSION", parSurfDiffMultiplex.size(), parSurfDiffMultiplex.data()); + } + else + { + writer.vector("PAR_SURFDIFFUSION", 4, parSurfDiff); + } } void configureFlowDirection(cadet::io::HDF5Writer& writer, bool reverseFlow) { - if (!reverseFlow) - writer.scalar("VELOCITY", 1); - else - writer.scalar("VELOCITY", -1); + if (!reverseFlow) + writer.scalar("VELOCITY", 1); + else + writer.scalar("VELOCITY", -1); } void configureUnitSolver(cadet::io::HDF5Writer& writer) { - Scope su(writer, "solver"); + Scope su(writer, "solver"); - writer.scalar("MAX_KRYLOV", 0); - writer.scalar("GS_TYPE", 1); - writer.scalar("MAX_RESTARTS", 10); - writer.scalar("SCHUR_SAFETY", 1e-8); + writer.scalar("MAX_KRYLOV", 0); + writer.scalar("GS_TYPE", 1); + writer.scalar("MAX_RESTARTS", 10); + writer.scalar("SCHUR_SAFETY", 1e-8); } void configureCstr(cadet::io::HDF5Writer& writer, ProgramOptions& opts, int nComp) { - writer.scalar("POROSITY", 0.37 + (1.0 - 0.37) * 0.75); - configureAdsorption(writer, opts.nParType, opts.isKinetic); + writer.scalar("POROSITY", 0.37 + (1.0 - 0.37) * 0.75); + configureAdsorption(writer, opts.nParType, opts.isKinetic); - writer.scalar("INIT_VOLUME", 1e-3); + writer.scalar("INIT_VOLUME", 1e-3); } void configureLRM(cadet::io::HDF5Writer& writer, ProgramOptions& opts, int nComp) { - if (opts.radialFlow) - { - writer.scalar("COL_LENGTH", 0.0014); - writer.scalar("COL_RADIUS_INNER", 0.01); - writer.scalar("COL_RADIUS_OUTER", 0.04); - } - else - { - writer.scalar("COL_LENGTH", 0.014); - writer.scalar("CROSS_SECTION_AREA", 0.0003141592653589793); - } - - writer.scalar("COL_DISPERSION", 5.75e-8); - if (opts.velocityDependence) - { - writer.scalar("COL_DISPERSION_DEP", "POWER_LAW"); - writer.scalar("COL_DISPERSION_DEP_BASE", 1.25); - writer.scalar("COL_DISPERSION_DEP_EXPONENT", 1.0); - } - - configureDiscretization(writer, opts.nCol, opts.nParType, opts.nPar, opts.nRad, opts.adJacobian); - - writer.scalar("TOTAL_POROSITY", 0.37 + (1.0 - 0.37) * 0.75); - configureAdsorption(writer, opts.nParType, opts.isKinetic); - - configureFlowDirection(writer, opts.reverseFlow); + if (opts.radialFlow) + { + writer.scalar("COL_LENGTH", 0.0014); + writer.scalar("COL_RADIUS_INNER", 0.01); + writer.scalar("COL_RADIUS_OUTER", 0.04); + } + else + { + writer.scalar("COL_LENGTH", 0.014); + writer.scalar("CROSS_SECTION_AREA", 0.0003141592653589793); + } + + writer.scalar("COL_DISPERSION", 5.75e-8); + if (opts.velocityDependence) + { + writer.scalar("COL_DISPERSION_DEP", "POWER_LAW"); + writer.scalar("COL_DISPERSION_DEP_BASE", 1.25); + writer.scalar("COL_DISPERSION_DEP_EXPONENT", 1.0); + } + + configureDiscretization(writer, opts.nCol, opts.nParType, opts.nPar, opts.nRad, opts.adJacobian); + + writer.scalar("TOTAL_POROSITY", 0.37 + (1.0 - 0.37) * 0.75); + configureAdsorption(writer, opts.nParType, opts.isKinetic); + + configureFlowDirection(writer, opts.reverseFlow); } void configureLRMP(cadet::io::HDF5Writer& writer, ProgramOptions& opts, int nComp) { - if (opts.radialFlow) - { - writer.scalar("COL_LENGTH", 0.0014); - writer.scalar("COL_RADIUS_INNER", 0.01); - writer.scalar("COL_RADIUS_OUTER", 0.04); - } - else - { - writer.scalar("COL_LENGTH", 0.014); - writer.scalar("CROSS_SECTION_AREA", 0.0003141592653589793); - } - - writer.scalar("COL_DISPERSION", 5.75e-8); - if (opts.velocityDependence) - { - writer.scalar("COL_DISPERSION_DEP", "POWER_LAW"); - writer.scalar("COL_DISPERSION_DEP_BASE", 1.25); - writer.scalar("COL_DISPERSION_DEP_EXPONENT", 1.0); - } - - configureDiscretization(writer, opts.nCol, opts.nParType, opts.nPar, opts.nRad, opts.adJacobian); - - writer.scalar("COL_POROSITY", 0.37); - configureParticles(writer, opts.nParType); - configureAdsorption(writer, opts.nParType, opts.isKinetic); - - configureFilmDiffusion(writer, nComp, opts.nParType, opts.velocityDependence); - - configureFlowDirection(writer, opts.reverseFlow); + if (opts.radialFlow) + { + writer.scalar("COL_LENGTH", 0.0014); + writer.scalar("COL_RADIUS_INNER", 0.01); + writer.scalar("COL_RADIUS_OUTER", 0.04); + } + else + { + writer.scalar("COL_LENGTH", 0.014); + writer.scalar("CROSS_SECTION_AREA", 0.0003141592653589793); + } + + writer.scalar("COL_DISPERSION", 5.75e-8); + if (opts.velocityDependence) + { + writer.scalar("COL_DISPERSION_DEP", "POWER_LAW"); + writer.scalar("COL_DISPERSION_DEP_BASE", 1.25); + writer.scalar("COL_DISPERSION_DEP_EXPONENT", 1.0); + } + + configureDiscretization(writer, opts.nCol, opts.nParType, opts.nPar, opts.nRad, opts.adJacobian); + + writer.scalar("COL_POROSITY", 0.37); + configureParticles(writer, opts.nParType); + configureAdsorption(writer, opts.nParType, opts.isKinetic); + + configureFilmDiffusion(writer, nComp, opts.nParType, opts.velocityDependence); + + configureFlowDirection(writer, opts.reverseFlow); } void configureGRM(cadet::io::HDF5Writer& writer, ProgramOptions& opts, int nComp) { - if (opts.radialFlow) - { - writer.scalar("COL_LENGTH", 0.0014); - writer.scalar("COL_RADIUS_INNER", 0.01); - writer.scalar("COL_RADIUS_OUTER", 0.04); - } - else - { - writer.scalar("COL_LENGTH", 0.014); - writer.scalar("CROSS_SECTION_AREA", 0.0003141592653589793); - } - writer.scalar("COL_DISPERSION", 5.75e-8); - if (opts.velocityDependence) - { - writer.scalar("COL_DISPERSION_DEP", "POWER_LAW"); - writer.scalar("COL_DISPERSION_DEP_BASE", 1.25); - writer.scalar("COL_DISPERSION_DEP_EXPONENT", 1.0); - } - - configureDiscretization(writer, opts.nCol, opts.nParType, opts.nPar, opts.nRad, opts.adJacobian); - - writer.scalar("COL_POROSITY", 0.37); - configureParticles(writer, opts.nParType); - configureAdsorption(writer, opts.nParType, opts.isKinetic); - - configureFilmDiffusion(writer, nComp, opts.nParType, opts.velocityDependence); - configurePoreDiffusion(writer, nComp, opts.nParType); - configureSurfaceDiffusion(writer, nComp, opts.nParType); - - configureFlowDirection(writer, opts.reverseFlow); + if (opts.radialFlow) + { + writer.scalar("COL_LENGTH", 0.0014); + writer.scalar("COL_RADIUS_INNER", 0.01); + writer.scalar("COL_RADIUS_OUTER", 0.04); + } + else + { + writer.scalar("COL_LENGTH", 0.014); + writer.scalar("CROSS_SECTION_AREA", 0.0003141592653589793); + } + writer.scalar("COL_DISPERSION", 5.75e-8); + if (opts.velocityDependence) + { + writer.scalar("COL_DISPERSION_DEP", "POWER_LAW"); + writer.scalar("COL_DISPERSION_DEP_BASE", 1.25); + writer.scalar("COL_DISPERSION_DEP_EXPONENT", 1.0); + } + + configureDiscretization(writer, opts.nCol, opts.nParType, opts.nPar, opts.nRad, opts.adJacobian); + + writer.scalar("COL_POROSITY", 0.37); + configureParticles(writer, opts.nParType); + configureAdsorption(writer, opts.nParType, opts.isKinetic); + + configureFilmDiffusion(writer, nComp, opts.nParType, opts.velocityDependence); + configurePoreDiffusion(writer, nComp, opts.nParType); + configureSurfaceDiffusion(writer, nComp, opts.nParType); + + configureFlowDirection(writer, opts.reverseFlow); } void configure2DGRM(cadet::io::HDF5Writer& writer, ProgramOptions& opts, int nComp) { - writer.scalar("COL_LENGTH", 0.014); - writer.scalar("CROSS_SECTION_AREA", 0.0003141592653589793); + writer.scalar("COL_LENGTH", 0.014); + writer.scalar("CROSS_SECTION_AREA", 0.0003141592653589793); - writer.scalar("COL_DISPERSION", 5.75e-8); - if (opts.velocityDependence) - { - writer.scalar("COL_DISPERSION_DEP", "POWER_LAW"); - writer.scalar("COL_DISPERSION_DEP_BASE", 1.25); - writer.scalar("COL_DISPERSION_DEP_EXPONENT", 1.0); - } - writer.scalar("COL_DISPERSION_RADIAL", 1e-6); + writer.scalar("COL_DISPERSION", 5.75e-8); + if (opts.velocityDependence) + { + writer.scalar("COL_DISPERSION_DEP", "POWER_LAW"); + writer.scalar("COL_DISPERSION_DEP_BASE", 1.25); + writer.scalar("COL_DISPERSION_DEP_EXPONENT", 1.0); + } + writer.scalar("COL_DISPERSION_RADIAL", 1e-6); - configureDiscretization(writer, opts.nCol, opts.nParType, opts.nPar, opts.nRad, opts.adJacobian); + configureDiscretization(writer, opts.nCol, opts.nParType, opts.nPar, opts.nRad, opts.adJacobian); - writer.scalar("COL_POROSITY", 0.37); - configureParticles(writer, opts.nParType); - configureAdsorption(writer, opts.nParType, opts.isKinetic); + writer.scalar("COL_POROSITY", 0.37); + configureParticles(writer, opts.nParType); + configureAdsorption(writer, opts.nParType, opts.isKinetic); - configureFilmDiffusion(writer, nComp, opts.nParType, false); - configurePoreDiffusion(writer, nComp, opts.nParType); - configureSurfaceDiffusion(writer, nComp, opts.nParType); + configureFilmDiffusion(writer, nComp, opts.nParType, false); + configurePoreDiffusion(writer, nComp, opts.nParType); + configureSurfaceDiffusion(writer, nComp, opts.nParType); - configureFlowDirection(writer, opts.reverseFlow); + configureFlowDirection(writer, opts.reverseFlow); } void configureInitialConditions(cadet::io::HDF5Writer& writer, int nParType) { - const double initC[] = {50.0, 0.0, 0.0, 0.0}; - const double initQ[] = {1.2e3, 0.0, 0.0, 0.0}; - writer.vector("INIT_C", 4, initC); - - if (nParType > 1) - { - //std::vector init_cps; - std::vector init_qs; - - for (int i = 0; i < nParType; ++i) - { - //init_cps.insert(init_cps.end(), initC, initC + 4); - init_qs.insert(init_qs.end(), initQ, initQ + 4); - } - - //writer.vector("INIT_CP", init_cps.size(), init_qs.data()); - writer.vector("INIT_Q", init_qs.size(), init_qs.data()); - } - else - writer.vector("INIT_Q", 4, initQ); + const double initC[] = {50.0, 0.0, 0.0, 0.0}; + const double initQ[] = {1.2e3, 0.0, 0.0, 0.0}; + writer.vector("INIT_C", 4, initC); + + if (nParType > 1) + { + // std::vector init_cps; + std::vector init_qs; + + for (int i = 0; i < nParType; ++i) + { + // init_cps.insert(init_cps.end(), initC, initC + 4); + init_qs.insert(init_qs.end(), initQ, initQ + 4); + } + + // writer.vector("INIT_CP", init_cps.size(), init_qs.data()); + writer.vector("INIT_Q", init_qs.size(), init_qs.data()); + } + else + writer.vector("INIT_Q", 4, initQ); } void configureInlet(cadet::io::HDF5Writer& writer, double startTime) { - Scope su(writer, "unit_001"); - - writer.scalar("UNIT_TYPE", std::string("INLET")); - writer.scalar("INLET_TYPE", std::string("PIECEWISE_CUBIC_POLY")); - writer.scalar("NCOMP", 4); - - if (startTime < 10.0) - { - { - Scope s3(writer, "sec_000"); - - const double constCoeff[] = {50.0, 1.0, 1.0, 1.0}; - const double linCoeff[] = {0.0, 0.0, 0.0, 0.0}; - - writer.vector("CONST_COEFF", 4, constCoeff); - writer.vector("LIN_COEFF", 4, linCoeff); - writer.vector("QUAD_COEFF", 4, linCoeff); - writer.vector("CUBE_COEFF", 4, linCoeff); - } - - { - Scope s3(writer, "sec_001"); - - const double constCoeff[] = {50.0, 0.0, 0.0, 0.0}; - const double linCoeff[] = {0.0, 0.0, 0.0, 0.0}; - - writer.vector("CONST_COEFF", 4, constCoeff); - writer.vector("LIN_COEFF", 4, linCoeff); - writer.vector("QUAD_COEFF", 4, linCoeff); - writer.vector("CUBE_COEFF", 4, linCoeff); - } - - { - Scope s3(writer, "sec_002"); - - const double constCoeff[] = {100.0, 0.0, 0.0, 0.0}; - const double linCoeff[] = {0.2, 0.0, 0.0, 0.0}; - const double quadCoeff[] = {0.0, 0.0, 0.0, 0.0}; - - writer.vector("CONST_COEFF", 4, constCoeff); - writer.vector("LIN_COEFF", 4, linCoeff); - writer.vector("QUAD_COEFF", 4, quadCoeff); - writer.vector("CUBE_COEFF", 4, quadCoeff); - } - } - else if (startTime < 90.0) - { - { - Scope s3(writer, "sec_000"); - - const double constCoeff[] = {50.0, 0.0, 0.0, 0.0}; - const double linCoeff[] = {0.0, 0.0, 0.0, 0.0}; - - writer.vector("CONST_COEFF", 4, constCoeff); - writer.vector("LIN_COEFF", 4, linCoeff); - writer.vector("QUAD_COEFF", 4, linCoeff); - writer.vector("CUBE_COEFF", 4, linCoeff); - } - - { - Scope s3(writer, "sec_001"); - - const double constCoeff[] = {100.0, 0.0, 0.0, 0.0}; - const double linCoeff[] = {0.2, 0.0, 0.0, 0.0}; - const double quadCoeff[] = {0.0, 0.0, 0.0, 0.0}; - - writer.vector("CONST_COEFF", 4, constCoeff); - writer.vector("LIN_COEFF", 4, linCoeff); - writer.vector("QUAD_COEFF", 4, quadCoeff); - writer.vector("CUBE_COEFF", 4, quadCoeff); - } - } - else if (startTime < 1500.0) - { - { - Scope s3(writer, "sec_000"); - - const double constCoeff[] = {100.0 + (startTime - 90.0) * 0.2, 0.0, 0.0, 0.0}; - const double linCoeff[] = {0.2, 0.0, 0.0, 0.0}; - const double quadCoeff[] = {0.0, 0.0, 0.0, 0.0}; - - writer.vector("CONST_COEFF", 4, constCoeff); - writer.vector("LIN_COEFF", 4, linCoeff); - writer.vector("QUAD_COEFF", 4, quadCoeff); - writer.vector("CUBE_COEFF", 4, quadCoeff); - } - } + Scope su(writer, "unit_001"); + + writer.scalar("UNIT_TYPE", std::string("INLET")); + writer.scalar("INLET_TYPE", std::string("PIECEWISE_CUBIC_POLY")); + writer.scalar("NCOMP", 4); + + if (startTime < 10.0) + { + { + Scope s3(writer, "sec_000"); + + const double constCoeff[] = {50.0, 1.0, 1.0, 1.0}; + const double linCoeff[] = {0.0, 0.0, 0.0, 0.0}; + + writer.vector("CONST_COEFF", 4, constCoeff); + writer.vector("LIN_COEFF", 4, linCoeff); + writer.vector("QUAD_COEFF", 4, linCoeff); + writer.vector("CUBE_COEFF", 4, linCoeff); + } + + { + Scope s3(writer, "sec_001"); + + const double constCoeff[] = {50.0, 0.0, 0.0, 0.0}; + const double linCoeff[] = {0.0, 0.0, 0.0, 0.0}; + + writer.vector("CONST_COEFF", 4, constCoeff); + writer.vector("LIN_COEFF", 4, linCoeff); + writer.vector("QUAD_COEFF", 4, linCoeff); + writer.vector("CUBE_COEFF", 4, linCoeff); + } + + { + Scope s3(writer, "sec_002"); + + const double constCoeff[] = {100.0, 0.0, 0.0, 0.0}; + const double linCoeff[] = {0.2, 0.0, 0.0, 0.0}; + const double quadCoeff[] = {0.0, 0.0, 0.0, 0.0}; + + writer.vector("CONST_COEFF", 4, constCoeff); + writer.vector("LIN_COEFF", 4, linCoeff); + writer.vector("QUAD_COEFF", 4, quadCoeff); + writer.vector("CUBE_COEFF", 4, quadCoeff); + } + } + else if (startTime < 90.0) + { + { + Scope s3(writer, "sec_000"); + + const double constCoeff[] = {50.0, 0.0, 0.0, 0.0}; + const double linCoeff[] = {0.0, 0.0, 0.0, 0.0}; + + writer.vector("CONST_COEFF", 4, constCoeff); + writer.vector("LIN_COEFF", 4, linCoeff); + writer.vector("QUAD_COEFF", 4, linCoeff); + writer.vector("CUBE_COEFF", 4, linCoeff); + } + + { + Scope s3(writer, "sec_001"); + + const double constCoeff[] = {100.0, 0.0, 0.0, 0.0}; + const double linCoeff[] = {0.2, 0.0, 0.0, 0.0}; + const double quadCoeff[] = {0.0, 0.0, 0.0, 0.0}; + + writer.vector("CONST_COEFF", 4, constCoeff); + writer.vector("LIN_COEFF", 4, linCoeff); + writer.vector("QUAD_COEFF", 4, quadCoeff); + writer.vector("CUBE_COEFF", 4, quadCoeff); + } + } + else if (startTime < 1500.0) + { + { + Scope s3(writer, "sec_000"); + + const double constCoeff[] = {100.0 + (startTime - 90.0) * 0.2, 0.0, 0.0, 0.0}; + const double linCoeff[] = {0.2, 0.0, 0.0, 0.0}; + const double quadCoeff[] = {0.0, 0.0, 0.0, 0.0}; + + writer.vector("CONST_COEFF", 4, constCoeff); + writer.vector("LIN_COEFF", 4, linCoeff); + writer.vector("QUAD_COEFF", 4, quadCoeff); + writer.vector("CUBE_COEFF", 4, quadCoeff); + } + } } void configureValveSwitches(cadet::io::HDF5Writer& writer, bool hasPorts) { - Scope su(writer, "connections"); - writer.scalar("NSWITCHES", 1); - writer.scalar("CONNECTIONS_INCLUDE_PORTS", 1); - - { - Scope s1(writer, "switch_000"); - - if (!hasPorts) - { - // Connection list is 1x7 since we have 1 connection between - // the two unit operations (and we need to have 7 columns) - const double connMatrix[] = {1, 0, -1, -1, -1, -1, 6.683738370512285e-8}; - // Connections: From unit operation 1 port -1 (i.e., all ports) - // to unit operation 0 port -1 (i.e., all ports), - // connect component -1 (i.e., all components) - // to component -1 (i.e., all components) with - // a flow rate of 6.683738370512285e-8 m^3/s - - writer.vector("CONNECTIONS", 7, connMatrix); - } - else - { - // Connection list is 3x7 since we have 1 connection between - // the two unit operations with 3 ports (and we need to have 7 columns) - const double connMatrix[] = {1.0, 0.0, 0.0, 0.0, -1.0, -1.0, 7.42637597e-09, - 1.0, 0.0, 0.0, 1.0, -1.0, -1.0, 2.22791279e-08, - 1.0, 0.0, 0.0, 2.0, -1.0, -1.0, 3.71318798e-08}; - // Connections: From unit operation 1 port 0 - // to unit operation 0 port 0, - // connect component -1 (i.e., all components) - // to component -1 (i.e., all components) with - // volumetric flow rate 7.42637597e-09 m^3/s - - writer.vector("CONNECTIONS", 21, connMatrix); - } - - // This switch occurs at beginning of section 0 (initial configuration) - writer.scalar("SECTION", 0); - } + Scope su(writer, "connections"); + writer.scalar("NSWITCHES", 1); + writer.scalar("CONNECTIONS_INCLUDE_PORTS", 1); + + { + Scope s1(writer, "switch_000"); + + if (!hasPorts) + { + // Connection list is 1x7 since we have 1 connection between + // the two unit operations (and we need to have 7 columns) + const double connMatrix[] = {1, 0, -1, -1, -1, -1, 6.683738370512285e-8}; + // Connections: From unit operation 1 port -1 (i.e., all ports) + // to unit operation 0 port -1 (i.e., all ports), + // connect component -1 (i.e., all components) + // to component -1 (i.e., all components) with + // a flow rate of 6.683738370512285e-8 m^3/s + + writer.vector("CONNECTIONS", 7, connMatrix); + } + else + { + // Connection list is 3x7 since we have 1 connection between + // the two unit operations with 3 ports (and we need to have 7 columns) + const double connMatrix[] = {1.0, 0.0, 0.0, 0.0, -1.0, -1.0, 7.42637597e-09, + 1.0, 0.0, 0.0, 1.0, -1.0, -1.0, 2.22791279e-08, + 1.0, 0.0, 0.0, 2.0, -1.0, -1.0, 3.71318798e-08}; + // Connections: From unit operation 1 port 0 + // to unit operation 0 port 0, + // connect component -1 (i.e., all components) + // to component -1 (i.e., all components) with + // volumetric flow rate 7.42637597e-09 m^3/s + + writer.vector("CONNECTIONS", 21, connMatrix); + } + + // This switch occurs at beginning of section 0 (initial configuration) + writer.scalar("SECTION", 0); + } } void configureReturn(cadet::io::HDF5Writer& writer, ProgramOptions& opts) { - Scope s(writer, "return"); - writer.template scalar("WRITE_SOLUTION_TIMES", true); + Scope s(writer, "return"); + writer.template scalar("WRITE_SOLUTION_TIMES", true); - Scope s2(writer, "unit_000"); - parseAndWriteOutputFormatsFromCmdLine(writer, opts.outSol, opts.outSens); + Scope s2(writer, "unit_000"); + parseAndWriteOutputFormatsFromCmdLine(writer, opts.outSol, opts.outSens); } void configureSolver(cadet::io::HDF5Writer& writer, ProgramOptions& opts) { - Scope s(writer, "solver"); - - if (!opts.solverTimes) - { - std::vector solTimes; - solTimes.reserve(1501); - for (double t = 0.0; t <= opts.endTime - opts.startTime; t += 1.0) - solTimes.push_back(t); - - writer.vector("USER_SOLUTION_TIMES", solTimes.size(), solTimes.data()); - } - - writer.scalar("NTHREADS", opts.nThreads); - - // Sections - { - Scope s2(writer, "sections"); - - if (opts.startTime < 10.0) - { - writer.scalar("NSEC", 3); - - const double secTimes[] = {0.0, 10.0 - opts.startTime, 90.0 - opts.startTime, 1500.0 - opts.startTime}; - writer.vector("SECTION_TIMES", 4, secTimes); - - const int secCont[] = {0, 0}; - writer.vector("SECTION_CONTINUITY", 2, secCont); - } - else if (opts.startTime < 90.0) - { - writer.scalar("NSEC", 2); - - const double secTimes[] = {0.0, 90.0 - opts.startTime, 1500.0 - opts.startTime}; - writer.vector("SECTION_TIMES", 3, secTimes); - - const int secCont[] = {0, 0}; - writer.vector("SECTION_CONTINUITY", 1, secCont); - } - else if (opts.startTime < 1500.0) - { - writer.scalar("NSEC", 1); - - const double secTimes[] = {0.0, 1500.0 - opts.startTime}; - writer.vector("SECTION_TIMES", 2, secTimes); - } - } - - // Time integrator - { - Scope s2(writer, "time_integrator"); - - writer.scalar("ABSTOL", 1e-8); - writer.scalar("RELTOL", 1e-6); - writer.scalar("ALGTOL", 1e-12); - writer.scalar("INIT_STEP_SIZE", 1e-6); - writer.scalar("MAX_STEPS", 10000); - } + Scope s(writer, "solver"); + + if (!opts.solverTimes) + { + std::vector solTimes; + solTimes.reserve(1501); + for (double t = 0.0; t <= opts.endTime - opts.startTime; t += 1.0) + solTimes.push_back(t); + + writer.vector("USER_SOLUTION_TIMES", solTimes.size(), solTimes.data()); + } + + writer.scalar("NTHREADS", opts.nThreads); + + // Sections + { + Scope s2(writer, "sections"); + + if (opts.startTime < 10.0) + { + writer.scalar("NSEC", 3); + + const double secTimes[] = {0.0, 10.0 - opts.startTime, 90.0 - opts.startTime, 1500.0 - opts.startTime}; + writer.vector("SECTION_TIMES", 4, secTimes); + + const int secCont[] = {0, 0}; + writer.vector("SECTION_CONTINUITY", 2, secCont); + } + else if (opts.startTime < 90.0) + { + writer.scalar("NSEC", 2); + + const double secTimes[] = {0.0, 90.0 - opts.startTime, 1500.0 - opts.startTime}; + writer.vector("SECTION_TIMES", 3, secTimes); + + const int secCont[] = {0, 0}; + writer.vector("SECTION_CONTINUITY", 1, secCont); + } + else if (opts.startTime < 1500.0) + { + writer.scalar("NSEC", 1); + + const double secTimes[] = {0.0, 1500.0 - opts.startTime}; + writer.vector("SECTION_TIMES", 2, secTimes); + } + } + + // Time integrator + { + Scope s2(writer, "time_integrator"); + + writer.scalar("ABSTOL", 1e-8); + writer.scalar("RELTOL", 1e-6); + writer.scalar("ALGTOL", 1e-12); + writer.scalar("INIT_STEP_SIZE", 1e-6); + writer.scalar("MAX_STEPS", 10000); + } } void configureModel(cadet::io::HDF5Writer& writer, ProgramOptions& opts) { - Scope s(writer, "model"); - - writer.scalar("NUNITS", 2); - - bool hasPorts = false; - - { - Scope su(writer, "unit_000"); - - const int nComp = 4; - writer.scalar("NCOMP", nComp); - - parseUnitType(opts.unitType); - writer.scalar("UNIT_TYPE", opts.unitType); - - // Unit operation parameters - if (opts.unitType == "CSTR") - configureCstr(writer, opts, nComp); - else if (opts.unitType == "LUMPED_RATE_MODEL_WITHOUT_PORES") - configureLRM(writer, opts, nComp); - else if (opts.unitType == "LUMPED_RATE_MODEL_WITH_PORES") - configureLRMP(writer, opts, nComp); - else if (opts.unitType == "GENERAL_RATE_MODEL") - configureGRM(writer, opts, nComp); - else if (opts.unitType == "GENERAL_RATE_MODEL_2D") - { - configure2DGRM(writer, opts, nComp); - hasPorts = true; - } - else - throw std::domain_error("Unknown unit operation type " + opts.unitType); - - configureInitialConditions(writer, opts.nParType); - } - - configureInlet(writer, opts.startTime); - configureValveSwitches(writer, hasPorts); - configureUnitSolver(writer); + Scope s(writer, "model"); + + writer.scalar("NUNITS", 2); + + bool hasPorts = false; + + { + Scope su(writer, "unit_000"); + + const int nComp = 4; + writer.scalar("NCOMP", nComp); + + parseUnitType(opts.unitType); + writer.scalar("UNIT_TYPE", opts.unitType); + + // Unit operation parameters + if (opts.unitType == "CSTR") + configureCstr(writer, opts, nComp); + else if (opts.unitType == "LUMPED_RATE_MODEL_WITHOUT_PORES") + configureLRM(writer, opts, nComp); + else if (opts.unitType == "LUMPED_RATE_MODEL_WITH_PORES") + configureLRMP(writer, opts, nComp); + else if (opts.unitType == "GENERAL_RATE_MODEL") + configureGRM(writer, opts, nComp); + else if (opts.unitType == "GENERAL_RATE_MODEL_2D") + { + configure2DGRM(writer, opts, nComp); + hasPorts = true; + } + else + throw std::domain_error("Unknown unit operation type " + opts.unitType); + + configureInitialConditions(writer, opts.nParType); + } + + configureInlet(writer, opts.startTime); + configureValveSwitches(writer, hasPorts); + configureUnitSolver(writer); } int main(int argc, char** argv) @@ -686,16 +687,29 @@ int main(int argc, char** argv) TCLAP::CmdLine cmd("Create an HDF5 input file for load-wash-elution benchmark case", ' ', "1.0"); cmd.setOutput(&customOut); - cmd >> (new TCLAP::ValueArg("o", "out", "Write output to file (default: LWE.h5)", false, "LWE.h5", "File"))->storeIn(&opts.fileName); - cmd >> (new TCLAP::ValueArg("t", "startTime", "Start time of simulation (default: 0sec)", false, 0.0, "Time"))->storeIn(&opts.startTime); - cmd >> (new TCLAP::ValueArg("T", "endTime", "End time of simulation (default: 1500sec)", false, 1500.0, "Time"))->storeIn(&opts.endTime); - cmd >> (new TCLAP::ValueArg("c", "constAlg", "Set all algebraic variables to constant value", false, nanVal, "Value"))->storeIn(&opts.constAlg); - cmd >> (new TCLAP::ValueArg("s", "stddevAlg", "Perturb algebraic variables with normal variates", false, nanVal, "Value"))->storeIn(&opts.stddevAlg); + cmd >> (new TCLAP::ValueArg("o", "out", "Write output to file (default: LWE.h5)", false, "LWE.h5", + "File")) + ->storeIn(&opts.fileName); + cmd >> (new TCLAP::ValueArg("t", "startTime", "Start time of simulation (default: 0sec)", false, 0.0, + "Time")) + ->storeIn(&opts.startTime); + cmd >> (new TCLAP::ValueArg("T", "endTime", "End time of simulation (default: 1500sec)", false, 1500.0, + "Time")) + ->storeIn(&opts.endTime); + cmd >> (new TCLAP::ValueArg("c", "constAlg", "Set all algebraic variables to constant value", false, + nanVal, "Value")) + ->storeIn(&opts.constAlg); + cmd >> (new TCLAP::ValueArg("s", "stddevAlg", "Perturb algebraic variables with normal variates", false, + nanVal, "Value")) + ->storeIn(&opts.stddevAlg); cmd >> (new TCLAP::SwitchArg("", "reverseFlow", "Reverse the flow for column"))->storeIn(&opts.reverseFlow); cmd >> (new TCLAP::SwitchArg("", "radialFlow", "Use radial flow column"))->storeIn(&opts.radialFlow); - cmd >> (new TCLAP::SwitchArg("", "velDep", "Use velocity dependent dispersion and film diffusion"))->storeIn(&opts.velocityDependence); - cmd >> (new TCLAP::ValueArg("", "rad", "Number of radial cells (default: 3)", false, 3, "Value"))->storeIn(&opts.nRad); - cmd >> (new TCLAP::ValueArg("", "parTypes", "Number of particle types (default: 1)", false, 1, "Value"))->storeIn(&opts.nParType); + cmd >> (new TCLAP::SwitchArg("", "velDep", "Use velocity dependent dispersion and film diffusion")) + ->storeIn(&opts.velocityDependence); + cmd >> (new TCLAP::ValueArg("", "rad", "Number of radial cells (default: 3)", false, 3, "Value")) + ->storeIn(&opts.nRad); + cmd >> (new TCLAP::ValueArg("", "parTypes", "Number of particle types (default: 1)", false, 1, "Value")) + ->storeIn(&opts.nParType); addMiscToCmdLine(cmd, opts); addUnitTypeToCmdLine(cmd, opts.unitType); addSensitivitiyParserToCmdLine(cmd, opts.sensitivities); @@ -703,7 +717,7 @@ int main(int argc, char** argv) cmd.parse(argc, argv); } - catch (const TCLAP::ArgException &e) + catch (const TCLAP::ArgException& e) { std::cerr << "ERROR: " << e.error() << " for argument " << e.argId() << std::endl; return 1; @@ -713,13 +727,12 @@ int main(int argc, char** argv) writer.openFile(opts.fileName, "co"); writer.pushGroup("input"); - configureModel(writer, opts); - configureReturn(writer, opts); - configureSolver(writer, opts); + configureModel(writer, opts); + configureReturn(writer, opts); + configureSolver(writer, opts); parseAndWriteSensitivitiesFromCmdLine(writer, opts.sensitivities); writer.closeFile(); return 0; } - diff --git a/src/tools/createMCLin.cpp b/src/tools/createMCLin.cpp index cbca16abf..ce8afd283 100644 --- a/src/tools/createMCLin.cpp +++ b/src/tools/createMCLin.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -39,14 +39,17 @@ int main(int argc, char** argv) TCLAP::CmdLine cmd("Create an HDF5 input file for a two component linear benchmark case", ' ', "1.0"); cmd.setOutput(&customOut); - cmd >> (new TCLAP::ValueArg("o", "out", "Write output to file (default: MCLin.h5)", false, "MCLin.h5", "File"))->storeIn(&opts.fileName); - cmd >> (new TCLAP::SwitchArg("k", "kinetic", "Kinetic adsorption model used (default: quasi-stationary)"))->storeIn(&opts.isKinetic); + cmd >> (new TCLAP::ValueArg("o", "out", "Write output to file (default: MCLin.h5)", false, + "MCLin.h5", "File")) + ->storeIn(&opts.fileName); + cmd >> (new TCLAP::SwitchArg("k", "kinetic", "Kinetic adsorption model used (default: quasi-stationary)")) + ->storeIn(&opts.isKinetic); addSensitivitiyParserToCmdLine(cmd, opts.sensitivities); addOutputParserToCmdLine(cmd, opts.outSol, opts.outSens); cmd.parse(argc, argv); } - catch (const TCLAP::ArgException &e) + catch (const TCLAP::ArgException& e) { std::cerr << "ERROR: " << e.error() << " for argument " << e.argId() << std::endl; return 1; @@ -95,7 +98,7 @@ int main(int argc, char** argv) // Adsorption writer.scalar("ADSORPTION_MODEL", std::string("LINEAR")); - const int nBound[] = { 1, 1 }; + const int nBound[] = {1, 1}; writer.vector("NBOUND", 2, nBound); { Scope s2(writer, "adsorption"); @@ -115,7 +118,7 @@ int main(int argc, char** argv) Scope s2(writer, "discretization"); writer.scalar("NCOL", 10); // 64 - writer.scalar("NPAR", 4); // 16 + writer.scalar("NPAR", 4); // 16 writer.scalar("PAR_DISC_TYPE", std::string("EQUIDISTANT_PAR")); @@ -222,7 +225,7 @@ int main(int argc, char** argv) { Scope s(writer, "return"); writer.template scalar("WRITE_SOLUTION_TIMES", true); - + Scope s2(writer, "unit_000"); parseAndWriteOutputFormatsFromCmdLine(writer, opts.outSol, opts.outSens); } @@ -234,9 +237,9 @@ int main(int argc, char** argv) std::vector solTimes; solTimes.reserve(1501); for (int t = 0; t <= 1500; t += 1) -// solTimes.reserve(101); -// for (int t = 0; t <= 100; t += 1) -// for (double t = 0; t <= 0.01; t += 0.001) + // solTimes.reserve(101); + // for (int t = 0; t <= 100; t += 1) + // for (double t = 0; t <= 0.01; t += 0.001) solTimes.push_back(t); writer.vector("USER_SOLUTION_TIMES", solTimes.size(), solTimes.data()); diff --git a/src/tools/createSCLang.cpp b/src/tools/createSCLang.cpp index 663d44c38..028aca46d 100644 --- a/src/tools/createSCLang.cpp +++ b/src/tools/createSCLang.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -38,7 +38,6 @@ struct ProgramOptions std::string outSens; }; - int main(int argc, char** argv) { ProgramOptions opts; @@ -49,14 +48,16 @@ int main(int argc, char** argv) TCLAP::CmdLine cmd("Create an HDF5 input file for single component Langmuir benchmark case", ' ', "1.0"); cmd.setOutput(&customOut); - cmd >> (new TCLAP::ValueArg("o", "out", "Write output to file (default: SCLang.h5)", false, "SCLang.h5", "File"))->storeIn(&opts.fileName); + cmd >> (new TCLAP::ValueArg("o", "out", "Write output to file (default: SCLang.h5)", false, + "SCLang.h5", "File")) + ->storeIn(&opts.fileName); addMiscToCmdLine(cmd, opts); addSensitivitiyParserToCmdLine(cmd, opts.sensitivities); addOutputParserToCmdLine(cmd, opts.outSol, opts.outSens); cmd.parse(argc, argv); } - catch (const TCLAP::ArgException &e) + catch (const TCLAP::ArgException& e) { std::cerr << "ERROR: " << e.error() << " for argument " << e.argId() << std::endl; return 1; @@ -105,14 +106,14 @@ int main(int argc, char** argv) writer.vector("INIT_Q", 1, initQ); // Adsorption - const int nBound[] = { 1 }; + const int nBound[] = {1}; writer.vector("NBOUND", 1, nBound); writer.scalar("ADSORPTION_MODEL", std::string("MULTI_COMPONENT_LANGMUIR")); { Scope s2(writer, "adsorption"); - + writer.scalar("IS_KINETIC", opts.isKinetic); - + const double kA[] = {1.14}; const double kD[] = {0.002}; const double qMax[] = {4.88}; @@ -207,10 +208,9 @@ int main(int argc, char** argv) { Scope s(writer, "return"); writer.template scalar("WRITE_SOLUTION_TIMES", true); - + Scope s2(writer, "unit_000"); parseAndWriteOutputFormatsFromCmdLine(writer, opts.outSol, opts.outSens); - } // Solver diff --git a/src/tools/createSCLin.cpp b/src/tools/createSCLin.cpp index a77046a36..2875adb4c 100644 --- a/src/tools/createSCLin.cpp +++ b/src/tools/createSCLin.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -32,21 +32,24 @@ struct ProgramOptions int main(int argc, char** argv) { ProgramOptions opts; - + try { TCLAP::CustomOutputWithoutVersion customOut("createSCL"); TCLAP::CmdLine cmd("Create an HDF5 input file for a single component linear benchmark case", ' ', "1.0"); cmd.setOutput(&customOut); - cmd >> (new TCLAP::ValueArg("o", "out", "Write output to file (default: SCLin.h5)", false, "SCLin.h5", "File"))->storeIn(&opts.fileName); - cmd >> (new TCLAP::SwitchArg("k", "kinetic", "Kinetic adsorption model used (default: quasi-stationary)"))->storeIn(&opts.isKinetic); + cmd >> (new TCLAP::ValueArg("o", "out", "Write output to file (default: SCLin.h5)", false, + "SCLin.h5", "File")) + ->storeIn(&opts.fileName); + cmd >> (new TCLAP::SwitchArg("k", "kinetic", "Kinetic adsorption model used (default: quasi-stationary)")) + ->storeIn(&opts.isKinetic); addSensitivitiyParserToCmdLine(cmd, opts.sensitivities); addOutputParserToCmdLine(cmd, opts.outSol, opts.outSens); cmd.parse(argc, argv); } - catch (const TCLAP::ArgException &e) + catch (const TCLAP::ArgException& e) { std::cerr << "ERROR: " << e.error() << " for argument " << e.argId() << std::endl; return 1; @@ -94,12 +97,12 @@ int main(int argc, char** argv) writer.vector("INIT_Q", 1, initQ); // Adsorption - const int nBound[] = { 1 }; + const int nBound[] = {1}; writer.vector("NBOUND", 1, nBound); writer.scalar("ADSORPTION_MODEL", std::string("LINEAR")); { Scope s2(writer, "adsorption"); - + if (opts.isKinetic) writer.scalar("IS_KINETIC", 1); else @@ -116,7 +119,7 @@ int main(int argc, char** argv) Scope s2(writer, "discretization"); writer.scalar("NCOL", 10); // 64 - writer.scalar("NPAR", 4); // 16 + writer.scalar("NPAR", 4); // 16 writer.scalar("PAR_DISC_TYPE", std::string("EQUIDISTANT_PAR")); @@ -223,7 +226,7 @@ int main(int argc, char** argv) { Scope s(writer, "return"); writer.template scalar("WRITE_SOLUTION_TIMES", true); - + Scope s2(writer, "unit_000"); parseAndWriteOutputFormatsFromCmdLine(writer, opts.outSol, opts.outSens); } @@ -235,9 +238,9 @@ int main(int argc, char** argv) std::vector solTimes; solTimes.reserve(1501); for (int t = 0; t <= 1500; t += 1) -// solTimes.reserve(101); -// for (int t = 0; t <= 100; t += 1) -// for (double t = 0; t <= 0.01; t += 0.001) + // solTimes.reserve(101); + // for (int t = 0; t <= 100; t += 1) + // for (double t = 0; t <= 0.01; t += 0.001) solTimes.push_back(t); writer.vector("USER_SOLUTION_TIMES", solTimes.size(), solTimes.data()); diff --git a/src/tools/createSCLinStep.cpp b/src/tools/createSCLinStep.cpp index e2d6d8a12..c282c2f28 100644 --- a/src/tools/createSCLinStep.cpp +++ b/src/tools/createSCLinStep.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -36,17 +36,21 @@ int main(int argc, char** argv) try { TCLAP::CustomOutputWithoutVersion customOut("createSCLstep"); - TCLAP::CmdLine cmd("Create an HDF5 input file for a single component linear benchmark case with pulse injection", ' ', "1.0"); + TCLAP::CmdLine cmd( + "Create an HDF5 input file for a single component linear benchmark case with pulse injection", ' ', "1.0"); cmd.setOutput(&customOut); - cmd >> (new TCLAP::ValueArg("o", "out", "Write output to file (default: SCLinStep.h5)", false, "SCLinStep.h5", "File"))->storeIn(&opts.fileName); - cmd >> (new TCLAP::SwitchArg("k", "kinetic", "Kinetic adsorption model used (default: quasi-stationary)"))->storeIn(&opts.isKinetic); + cmd >> (new TCLAP::ValueArg("o", "out", "Write output to file (default: SCLinStep.h5)", false, + "SCLinStep.h5", "File")) + ->storeIn(&opts.fileName); + cmd >> (new TCLAP::SwitchArg("k", "kinetic", "Kinetic adsorption model used (default: quasi-stationary)")) + ->storeIn(&opts.isKinetic); addSensitivitiyParserToCmdLine(cmd, opts.sensitivities); addOutputParserToCmdLine(cmd, opts.outSol, opts.outSens); cmd.parse(argc, argv); } - catch (const TCLAP::ArgException &e) + catch (const TCLAP::ArgException& e) { std::cerr << "ERROR: " << e.error() << " for argument " << e.argId() << std::endl; return 1; @@ -94,12 +98,12 @@ int main(int argc, char** argv) writer.vector("INIT_Q", 1, initQ); // Adsorption - const int nBound[] = { 1 }; + const int nBound[] = {1}; writer.vector("NBOUND", 1, nBound); writer.scalar("ADSORPTION_MODEL", std::string("LINEAR")); { Scope s2(writer, "adsorption"); - + if (opts.isKinetic) writer.scalar("IS_KINETIC", 1); else @@ -116,7 +120,7 @@ int main(int argc, char** argv) Scope s2(writer, "discretization"); writer.scalar("NCOL", 10); // 64 - writer.scalar("NPAR", 4); // 16 + writer.scalar("NPAR", 4); // 16 writer.scalar("PAR_DISC_TYPE", std::string("EQUIDISTANT_PAR")); @@ -192,7 +196,7 @@ int main(int argc, char** argv) writer.scalar("SECTION", 0); writer.vector("CONNECTIONS", 7, connMatrix); } - } + } // Solver settings { @@ -209,7 +213,7 @@ int main(int argc, char** argv) { Scope s(writer, "return"); writer.template scalar("WRITE_SOLUTION_TIMES", true); - + Scope s2(writer, "unit_000"); parseAndWriteOutputFormatsFromCmdLine(writer, opts.outSol, opts.outSens); } @@ -221,9 +225,9 @@ int main(int argc, char** argv) std::vector solTimes; solTimes.reserve(201); for (int t = 0; t <= 200; t += 1) -// solTimes.reserve(101); -// for (int t = 0; t <= 100; t += 1) -// for (double t = 0; t <= 0.01; t += 0.001) + // solTimes.reserve(101); + // for (int t = 0; t <= 100; t += 1) + // for (double t = 0; t <= 0.01; t += 0.001) solTimes.push_back(t); writer.vector("USER_SOLUTION_TIMES", solTimes.size(), solTimes.data()); diff --git a/test/AD.cpp b/test/AD.cpp index a2c5cb6b2..dc266a896 100644 --- a/test/AD.cpp +++ b/test/AD.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -81,7 +81,8 @@ TEST_CASE("Extract banded Jacobian via AD", "[AD],[BandMatrix]") cadet::ad::extractBandedJacobianFromAd(res, 0, lowerBand, bm); // Get reference matrix - const cadet::linalg::BandMatrix ref = cadet::test::createBandMatrix(matSize, lowerBand, upperBand); + const cadet::linalg::BandMatrix ref = + cadet::test::createBandMatrix(matSize, lowerBand, upperBand); // Compare matrices const unsigned int n = ref.rows() * ref.stride(); @@ -129,23 +130,28 @@ TEST_CASE("Extract dense submatrix from banded Jacobian via AD", "[AD],[DenseMat dm.setAll(0.0); cadet::ad::extractDenseJacobianFromBandedAd(res, 1, 0, lowerBand, lowerBand, upperBand, dm); - cadet::test::checkMatrixAgainstLinearArray(dm.data(), {6, 7, 8, 9, 0, 0, 11, 12, 13, 14, 15, 0, 16, 17, 18, 19, 20, 21}); + cadet::test::checkMatrixAgainstLinearArray(dm.data(), + {6, 7, 8, 9, 0, 0, 11, 12, 13, 14, 15, 0, 16, 17, 18, 19, 20, 21}); dm.setAll(0.0); cadet::ad::extractDenseJacobianFromBandedAd(res, 2, 0, lowerBand, lowerBand, upperBand, dm); - cadet::test::checkMatrixAgainstLinearArray(dm.data(), {12, 13, 14, 15, 0, 0, 17, 18, 19, 20, 21, 0, 22, 23, 24, 25, 26, 27}); + cadet::test::checkMatrixAgainstLinearArray(dm.data(), + {12, 13, 14, 15, 0, 0, 17, 18, 19, 20, 21, 0, 22, 23, 24, 25, 26, 27}); dm.setAll(0.0); cadet::ad::extractDenseJacobianFromBandedAd(res, 5, 0, lowerBand, lowerBand, upperBand, dm); - cadet::test::checkMatrixAgainstLinearArray(dm.data(), {30, 31, 32, 33, 0, 0, 35, 36, 37, 38, 39, 0, 40, 41, 42, 43, 44, 0}); + cadet::test::checkMatrixAgainstLinearArray(dm.data(), + {30, 31, 32, 33, 0, 0, 35, 36, 37, 38, 39, 0, 40, 41, 42, 43, 44, 0}); dm.setAll(0.0); cadet::ad::extractDenseJacobianFromBandedAd(res, 6, 0, lowerBand, lowerBand, upperBand, dm); - cadet::test::checkMatrixAgainstLinearArray(dm.data(), {36, 37, 38, 39, 0, 0, 41, 42, 43, 44, 0, 0, 45, 46, 47, 48, 0, 0}); + cadet::test::checkMatrixAgainstLinearArray(dm.data(), + {36, 37, 38, 39, 0, 0, 41, 42, 43, 44, 0, 0, 45, 46, 47, 48, 0, 0}); dm.setAll(0.0); cadet::ad::extractDenseJacobianFromBandedAd(res, 7, 0, lowerBand, lowerBand, upperBand, dm); - cadet::test::checkMatrixAgainstLinearArray(dm.data(), {42, 43, 44, 0, 0, 0, 46, 47, 48, 0, 0, 0, 49, 50, 51, 0, 0, 0}); + cadet::test::checkMatrixAgainstLinearArray(dm.data(), + {42, 43, 44, 0, 0, 0, 46, 47, 48, 0, 0, 0, 49, 50, 51, 0, 0, 0}); delete[] x; delete[] res; @@ -176,25 +182,18 @@ TEST_CASE("Extract square dense submatrix from banded Jacobian via AD", "[AD],[D dm.setAll(0.0); cadet::ad::extractDenseJacobianFromBandedAd(res, 0, 0, lowerBand, lowerBand, upperBand, dm); - cadet::test::checkMatrixAgainstLinearArray(dm.data(), {1, 2, 3, 4, 5, 6, 7, 8, - 14, 15, 16, 17, 18, 19, 20, 21, - 28, 29, 30, 31, 32, 33, 34, 35, - 43, 44, 45, 46, 47, 48, 49, 50, - 59, 60, 61, 62, 63, 64, 65, 66, - 76, 77, 78, 79, 80, 81, 82, 83, - 94, 95, 96, 97, 98, 99, 100, 101, - 113, 114, 115, 116, 117, 118, 119, 120}); + cadet::test::checkMatrixAgainstLinearArray( + dm.data(), {1, 2, 3, 4, 5, 6, 7, 8, 14, 15, 16, 17, 18, 19, 20, 21, 28, 29, 30, 31, 32, 33, + 34, 35, 43, 44, 45, 46, 47, 48, 49, 50, 59, 60, 61, 62, 63, 64, 65, 66, 76, 77, 78, 79, + 80, 81, 82, 83, 94, 95, 96, 97, 98, 99, 100, 101, 113, 114, 115, 116, 117, 118, 119, 120}); dm.setAll(0.0); cadet::ad::extractDenseJacobianFromBandedAd(res, 8, 0, lowerBand, lowerBand, upperBand, dm); - cadet::test::checkMatrixAgainstLinearArray(dm.data(), {141, 142, 143, 144, 145, 146, 147, 148, - 161, 162, 163, 164, 165, 166, 167, 168, - 181, 182, 183, 184, 185, 186, 187, 188, - 201, 202, 203, 204, 205, 206, 207, 208, - 221, 222, 223, 224, 225, 226, 227, 228, - 241, 242, 243, 244, 245, 246, 247, 248, - 261, 262, 263, 264, 265, 266, 267, 268, - 281, 282, 283, 284, 285, 286, 287, 288}); + cadet::test::checkMatrixAgainstLinearArray( + dm.data(), + {141, 142, 143, 144, 145, 146, 147, 148, 161, 162, 163, 164, 165, 166, 167, 168, 181, 182, 183, 184, 185, 186, + 187, 188, 201, 202, 203, 204, 205, 206, 207, 208, 221, 222, 223, 224, 225, 226, 227, 228, 241, 242, 243, 244, + 245, 246, 247, 248, 261, 262, 263, 264, 265, 266, 267, 268, 281, 282, 283, 284, 285, 286, 287, 288}); delete[] x; delete[] res; @@ -235,8 +234,10 @@ TEST_CASE("Banded AD Jacobian vs FD", "[AD],[BandMatrix]") std::vector colB(matSize, 0.0); cadet::test::compareJacobianFD( - [=](double const* y, double* r) { std::fill_n(r, matSize, 0.0); bandMatrixJacobian(y, r, matSize, lowerBand, upperBand); }, - [&](double const* y, double* r) { bm.multiplyVector(y, r); }, - y.data(), dir.data(), colA.data(), colB.data(), matSize, matSize, 1e-7, 0.0, 1e-15 - ); + [=](double const* y, double* r) { + std::fill_n(r, matSize, 0.0); + bandMatrixJacobian(y, r, matSize, lowerBand, upperBand); + }, + [&](double const* y, double* r) { bm.multiplyVector(y, r); }, y.data(), dir.data(), colA.data(), colB.data(), + matSize, matSize, 1e-7, 0.0, 1e-15); } diff --git a/test/Approx.hpp b/test/Approx.hpp index c2bbc9f34..a737128f9 100644 --- a/test/Approx.hpp +++ b/test/Approx.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines a helper class for comparing two doubles in tests. */ @@ -30,165 +30,179 @@ namespace cadet namespace test { - namespace detail - { - // Performs equivalent check of std::fabs(lhs - rhs) <= margin - // But without the subtraction to allow for INFINITY in comparison - inline bool marginComparison(double lhs, double rhs, double margin) - { - return (lhs + margin >= rhs) && (rhs + margin >= lhs); - } - } - - /** - * @brief Represents an approximate number to compare against - * @details This class is based on Catch::Approx and only differs in the order of equality checks. - * Whereas CATCH checks absolute error first, this class first checks the relative error. - */ - class RelApprox { - private: - bool equalityComparisonImpl(const double other) const - { - // First try with relative comparison, then try absolute comparison - return detail::marginComparison(_value, other, _epsilon * (_scale + std::fabs(_value))) || detail::marginComparison(_value, other, _margin); - } - - public: - explicit RelApprox(double value) : _epsilon(std::numeric_limits::epsilon() * 100.0), _margin(0.0), _scale(0.0), _value(value) { } - - static RelApprox custom() - { - return RelApprox(0.0); - } - - static inline double defaultEpsilon() { return std::numeric_limits::epsilon() * 100.0; } - static inline double defaultMargin() { return 0.0; } - - template ::value>::type> - RelApprox operator()(T const& value) - { - RelApprox approx(static_cast(value)); - approx.epsilon(_epsilon); - approx.margin(_margin); - approx.scale(_scale); - return approx; - } - - template ::value>::type> - explicit RelApprox(T const& value) : RelApprox(static_cast(value)) { } - - template ::value>::type> - friend bool operator==(const T& lhs, RelApprox const& rhs) - { - const auto lhs_v = static_cast(lhs); - return rhs.equalityComparisonImpl(lhs_v); - } - - template ::value>::type> - friend bool operator==(RelApprox const& lhs, const T& rhs) - { - return operator==(rhs, lhs); - } - - template ::value>::type> - friend bool operator != (T const& lhs, RelApprox const& rhs) - { - return !operator==(lhs, rhs); - } - - template ::value>::type> - friend bool operator!=(RelApprox const& lhs, T const& rhs) - { - return !operator==(rhs, lhs); - } - - template ::value>::type> - friend bool operator<=(T const& lhs, RelApprox const& rhs) - { - return (static_cast(lhs) < rhs._value) || (lhs == rhs); - } - - template ::value>::type> - friend bool operator<=(RelApprox const& lhs, T const& rhs) - { - return (lhs._value < static_cast(rhs)) || (lhs == rhs); - } - - template ::value>::type> - friend bool operator>=(T const& lhs, RelApprox const& rhs) - { - return (static_cast(lhs) > rhs._value) || (lhs == rhs); - } - - template ::value>::type> - friend bool operator>=(RelApprox const& lhs, T const& rhs) - { - return (lhs._value > static_cast(rhs)) || (lhs == rhs); - } - - template ::value>::type> - RelApprox& epsilon(T const& newEpsilon) - { - const double epsilonAsDouble = static_cast(newEpsilon); - if (epsilonAsDouble < 0 || epsilonAsDouble > 1.0) - throw std::domain_error("Invalid RelApprox::epsilon: " + Catch::Detail::stringify(epsilonAsDouble) + ", RelApprox::epsilon has to be between 0 and 1"); - - _epsilon = epsilonAsDouble; - return *this; - } - - template ::value>::type> - RelApprox& margin(T const& newMargin) - { - const double marginAsDouble = static_cast(newMargin); - if (marginAsDouble < 0) - throw std::domain_error("Invalid RelApprox::margin: " + Catch::Detail::stringify(marginAsDouble) + ", RelApprox::Margin has to be non-negative."); - - _margin = marginAsDouble; - return *this; - } - - template ::value>::type> - RelApprox& scale(T const& newScale) - { - _scale = static_cast(newScale); - return *this; - } - - std::string toString() const - { - Catch::ReusableStringStream rss; - rss << "RelApprox( " << ::Catch::Detail::stringify(_value) << " )"; - return rss.str(); - } - - private: - double _epsilon; - double _margin; - double _scale; - double _value; - }; - - /** - * @brief Creates a RelApprox object with given values - * @param [in] val Reference value - * @param [in] relTol Relative tolerance - * @param [in] absTol Absolute tolerance - * @return RelApprox object with the given values - */ - inline RelApprox makeApprox(double val, double relTol, double absTol) - { - return RelApprox(val).epsilon(relTol).margin(absTol); +namespace detail +{ +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +inline bool marginComparison(double lhs, double rhs, double margin) +{ + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} +} // namespace detail + +/** + * @brief Represents an approximate number to compare against + * @details This class is based on Catch::Approx and only differs in the order of equality checks. + * Whereas CATCH checks absolute error first, this class first checks the relative error. + */ +class RelApprox +{ +private: + bool equalityComparisonImpl(const double other) const + { + // First try with relative comparison, then try absolute comparison + return detail::marginComparison(_value, other, _epsilon * (_scale + std::fabs(_value))) || + detail::marginComparison(_value, other, _margin); + } + +public: + explicit RelApprox(double value) + : _epsilon(std::numeric_limits::epsilon() * 100.0), _margin(0.0), _scale(0.0), _value(value) + { + } + + static RelApprox custom() + { + return RelApprox(0.0); + } + + static inline double defaultEpsilon() + { + return std::numeric_limits::epsilon() * 100.0; + } + static inline double defaultMargin() + { + return 0.0; + } + + template ::value>::type> + RelApprox operator()(T const& value) + { + RelApprox approx(static_cast(value)); + approx.epsilon(_epsilon); + approx.margin(_margin); + approx.scale(_scale); + return approx; + } + + template ::value>::type> + explicit RelApprox(T const& value) : RelApprox(static_cast(value)) + { + } + + template ::value>::type> + friend bool operator==(const T& lhs, RelApprox const& rhs) + { + const auto lhs_v = static_cast(lhs); + return rhs.equalityComparisonImpl(lhs_v); + } + + template ::value>::type> + friend bool operator==(RelApprox const& lhs, const T& rhs) + { + return operator==(rhs, lhs); + } + + template ::value>::type> + friend bool operator!=(T const& lhs, RelApprox const& rhs) + { + return !operator==(lhs, rhs); + } + + template ::value>::type> + friend bool operator!=(RelApprox const& lhs, T const& rhs) + { + return !operator==(rhs, lhs); + } + + template ::value>::type> + friend bool operator<=(T const& lhs, RelApprox const& rhs) + { + return (static_cast(lhs) < rhs._value) || (lhs == rhs); + } + + template ::value>::type> + friend bool operator<=(RelApprox const& lhs, T const& rhs) + { + return (lhs._value < static_cast(rhs)) || (lhs == rhs); + } + + template ::value>::type> + friend bool operator>=(T const& lhs, RelApprox const& rhs) + { + return (static_cast(lhs) > rhs._value) || (lhs == rhs); + } + + template ::value>::type> + friend bool operator>=(RelApprox const& lhs, T const& rhs) + { + return (lhs._value > static_cast(rhs)) || (lhs == rhs); + } + + template ::value>::type> + RelApprox& epsilon(T const& newEpsilon) + { + const double epsilonAsDouble = static_cast(newEpsilon); + if (epsilonAsDouble < 0 || epsilonAsDouble > 1.0) + throw std::domain_error("Invalid RelApprox::epsilon: " + Catch::Detail::stringify(epsilonAsDouble) + + ", RelApprox::epsilon has to be between 0 and 1"); + + _epsilon = epsilonAsDouble; + return *this; + } + + template ::value>::type> + RelApprox& margin(T const& newMargin) + { + const double marginAsDouble = static_cast(newMargin); + if (marginAsDouble < 0) + throw std::domain_error("Invalid RelApprox::margin: " + Catch::Detail::stringify(marginAsDouble) + + ", RelApprox::Margin has to be non-negative."); + + _margin = marginAsDouble; + return *this; } + template ::value>::type> + RelApprox& scale(T const& newScale) + { + _scale = static_cast(newScale); + return *this; + } + + std::string toString() const + { + Catch::ReusableStringStream rss; + rss << "RelApprox( " << ::Catch::Detail::stringify(_value) << " )"; + return rss.str(); + } + +private: + double _epsilon; + double _margin; + double _scale; + double _value; +}; + +/** + * @brief Creates a RelApprox object with given values + * @param [in] val Reference value + * @param [in] relTol Relative tolerance + * @param [in] absTol Absolute tolerance + * @return RelApprox object with the given values + */ +inline RelApprox makeApprox(double val, double relTol, double absTol) +{ + return RelApprox(val).epsilon(relTol).margin(absTol); +} + } // namespace test } // namespace cadet namespace Catch { -template<> -struct StringMaker +template <> struct StringMaker { static std::string convert(cadet::test::RelApprox const& value) { diff --git a/test/BandMatrix.cpp b/test/BandMatrix.cpp index 9d4cdd5fc..ed9aee999 100644 --- a/test/BandMatrix.cpp +++ b/test/BandMatrix.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -45,7 +45,8 @@ inline cadet::linalg::FactorizableBandMatrix fromBandMatrix(const cadet::linalg: * @param [in] numCols Number of columns to extract * @param [out] out Memory in which the dense submatrix is written in row-major format */ -inline void extractDenseSubMatrix(const cadet::linalg::BandMatrix& mat, unsigned int startRow, int startDiag, unsigned int numRows, unsigned int numCols, double* const out) +inline void extractDenseSubMatrix(const cadet::linalg::BandMatrix& mat, unsigned int startRow, int startDiag, + unsigned int numRows, unsigned int numCols, double* const out) { std::vector x(numCols, 0.0); std::vector y(numRows, 0.0); @@ -102,12 +103,12 @@ TEST_CASE("FactorizableBandMatrix iterator read access", "[BandMatrix],[LinAlg]" TEST_CASE("FactorizableBandMatrix solves", "[BandMatrix],[LinAlg]") { - using cadet::linalg::FactorizableBandMatrix; using cadet::linalg::BandMatrix; + using cadet::linalg::FactorizableBandMatrix; const BandMatrix bm = cadet::test::createBandMatrix(10, 2, 3); FactorizableBandMatrix fbm = fromBandMatrix(bm); - + REQUIRE(fbm.factorize()); // Prepare some right hand side @@ -134,9 +135,11 @@ TEST_CASE("FactorizableBandMatrix solves", "[BandMatrix],[LinAlg]") * @param [in] numCols Number of columns to extract * @param [in] ref Reference values (matrix in row-major format) */ -void testSubMatrixMultiply(const cadet::linalg::BandMatrix& bm, int startRow, int startDiag, int numRows, int numCols, const std::vector& ref) +void testSubMatrixMultiply(const cadet::linalg::BandMatrix& bm, int startRow, int startDiag, int numRows, int numCols, + const std::vector& ref) { - SECTION("From row " + std::to_string(startRow) + ", diagonal " + std::to_string(startDiag) + " extract " + std::to_string(numRows) + "x" + std::to_string(numCols) + " matrix") + SECTION("From row " + std::to_string(startRow) + ", diagonal " + std::to_string(startDiag) + " extract " + + std::to_string(numRows) + "x" + std::to_string(numCols) + " matrix") { std::vector out(bm.rows() * bm.stride(), 0.0); extractDenseSubMatrix(bm, startRow, startDiag, numRows, numCols, out.data()); @@ -152,13 +155,20 @@ TEST_CASE("BandMatrix::submatrixMultiplyVector", "[BandMatrix],[LinAlg]") { const BandMatrix bm = cadet::test::createBandMatrix(8, 2, 3); - testSubMatrixMultiply(bm, 0, 0, 5, 5, {1, 2, 3, 4, 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 16, 17, 18, 19, 0, 0, 22, 23, 24}); - testSubMatrixMultiply(bm, 2, 0, 5, 5, {12, 13, 14, 15, 0, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 0, 28, 29, 30, 31, 0, 0, 33, 34, 35}); - testSubMatrixMultiply(bm, 2, -2, 5, 5, {10, 11, 12, 13, 14, 0, 16, 17, 18, 19, 0, 0, 22, 23, 24, 0, 0, 0, 28, 29, 0, 0, 0, 0, 33}); - testSubMatrixMultiply(bm, 2, -1, 5, 5, {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0, 22, 23, 24, 25, 0, 0, 28, 29, 30, 0, 0, 0, 33, 34}); - testSubMatrixMultiply(bm, 2, 1, 5, 5, {13, 14, 15, 0, 0, 18, 19, 20, 21, 0, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 0, 33, 34, 35, 36}); - testSubMatrixMultiply(bm, 2, 3, 5, 5, {15, 0, 0, 0, 0, 20, 21, 0, 0, 0, 25, 26, 27, 0, 0, 30, 31, 32, 0, 0, 34, 35, 36, 0, 0}); - testSubMatrixMultiply(bm, 2, 0, 5, 7, {12, 13, 14, 15, 0, 0, 0, 17, 18, 19, 20, 21, 0, 0, 22, 23, 24, 25, 26, 27, 0, 0, 28, 29, 30, 31, 32, 0, 0, 0, 33, 34, 35, 36, 0}); + testSubMatrixMultiply(bm, 0, 0, 5, 5, + {1, 2, 3, 4, 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 16, 17, 18, 19, 0, 0, 22, 23, 24}); + testSubMatrixMultiply(bm, 2, 0, 5, 5, {12, 13, 14, 15, 0, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 0, 28, 29, 30, 31, 0, 0, 33, 34, 35}); + testSubMatrixMultiply(bm, 2, -2, 5, 5, {10, 11, 12, 13, 14, 0, 16, 17, 18, 19, 0, 0, 22, + 23, 24, 0, 0, 0, 28, 29, 0, 0, 0, 0, 33}); + testSubMatrixMultiply(bm, 2, -1, 5, 5, {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0, 22, 23, + 24, 25, 0, 0, 28, 29, 30, 0, 0, 0, 33, 34}); + testSubMatrixMultiply(bm, 2, 1, 5, 5, {13, 14, 15, 0, 0, 18, 19, 20, 21, 0, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 0, 33, 34, 35, 36}); + testSubMatrixMultiply(bm, 2, 3, 5, 5, + {15, 0, 0, 0, 0, 20, 21, 0, 0, 0, 25, 26, 27, 0, 0, 30, 31, 32, 0, 0, 34, 35, 36, 0, 0}); + testSubMatrixMultiply(bm, 2, 0, 5, 7, {12, 13, 14, 15, 0, 0, 0, 17, 18, 19, 20, 21, 0, 0, 22, 23, 24, 25, + 26, 27, 0, 0, 28, 29, 30, 31, 32, 0, 0, 0, 33, 34, 35, 36, 0}); testSubMatrixMultiply(bm, 2, -1, 5, 3, {11, 12, 13, 16, 17, 18, 0, 22, 23, 0, 0, 28, 0, 0, 0}); testSubMatrixMultiply(bm, 2, 1, 5, 3, {13, 14, 15, 18, 19, 20, 23, 24, 25, 28, 29, 30, 0, 33, 34}); testSubMatrixMultiply(bm, 2, 2, 5, 3, {14, 15, 0, 19, 20, 21, 24, 25, 26, 29, 30, 31, 33, 34, 35}); @@ -173,7 +183,7 @@ TEST_CASE("BandMatrix::submatrixMultiplyVector", "[BandMatrix],[LinAlg]") SECTION("Matrix size: 24 rows, 6+1+9 bandwidth") { const BandMatrix bm = cadet::test::createBandMatrix(24, 6, 9); - + testSubMatrixMultiply(bm, 3, 1, 1, 2, {38, 39}); testSubMatrixMultiply(bm, 3, -1, 1, 3, {36, 37, 38}); } diff --git a/test/BindingModelAutoJacobian.cpp b/test/BindingModelAutoJacobian.cpp index 60ea5869f..c942114ab 100644 --- a/test/BindingModelAutoJacobian.cpp +++ b/test/BindingModelAutoJacobian.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -30,72 +30,120 @@ namespace cadet { - class BindingWithoutJacobian : public model::BindingModelBase +class BindingWithoutJacobian : public model::BindingModelBase +{ +public: + BindingWithoutJacobian() { - public: - BindingWithoutJacobian() { } + } - virtual const char* name() const CADET_NOEXCEPT { return "BindingWithoutJacobian"; } - virtual bool dependsOnTime() const CADET_NOEXCEPT { return false; } + virtual const char* name() const CADET_NOEXCEPT + { + return "BindingWithoutJacobian"; + } + virtual bool dependsOnTime() const CADET_NOEXCEPT + { + return false; + } - virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) override { return true; } - virtual bool hasSalt() const CADET_NOEXCEPT { return false; } - virtual bool supportsMultistate() const CADET_NOEXCEPT { return true; } - virtual bool supportsNonBinding() const CADET_NOEXCEPT { return true; } + virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx) override + { + return true; + } + virtual bool hasSalt() const CADET_NOEXCEPT + { + return false; + } + virtual bool supportsMultistate() const CADET_NOEXCEPT + { + return true; + } + virtual bool supportsNonBinding() const CADET_NOEXCEPT + { + return true; + } - CADET_BINDINGMODEL_RESIDUAL_BOILERPLATE - protected: - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return false; } + CADET_BINDINGMODEL_RESIDUAL_BOILERPLATE +protected: + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return false; + } - template - int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + template + int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + { + int bndIdx = 0; + for (int i = 0; i < _nComp; ++i) { - int bndIdx = 0; - for (int i = 0; i < _nComp; ++i) + // Skip components without bound states (bound state index bndIdx is not advanced) + if (_nBoundStates[i] == 0) + continue; + + // Residual + for (int j = 0; j < _nBoundStates[i]; ++j) { - // Skip components without bound states (bound state index bndIdx is not advanced) - if (_nBoundStates[i] == 0) - continue; - - // Residual - for (int j = 0; j < _nBoundStates[i]; ++j) - { - res[bndIdx] = (i+2+j) * y[bndIdx] - (i+1) * yCp[i]; - - // Next bound state - ++bndIdx; - } - } + res[bndIdx] = (i + 2 + j) * y[bndIdx] - (i + 1) * yCp[i]; - return 0; + // Next bound state + ++bndIdx; + } } - }; - class BindingWithJacobian : public model::BindingModelBase + return 0; + } +}; + +class BindingWithJacobian : public model::BindingModelBase +{ +public: + BindingWithJacobian() { - public: - BindingWithJacobian() { } + } - virtual const char* name() const CADET_NOEXCEPT { return "BindingWithJacobian"; } - virtual bool dependsOnTime() const CADET_NOEXCEPT { return false; } + virtual const char* name() const CADET_NOEXCEPT + { + return "BindingWithJacobian"; + } + virtual bool dependsOnTime() const CADET_NOEXCEPT + { + return false; + } - virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, ParticleTypeIdx parTypeIdx) override { return true; } - virtual bool hasSalt() const CADET_NOEXCEPT { return false; } - virtual bool supportsMultistate() const CADET_NOEXCEPT { return true; } - virtual bool supportsNonBinding() const CADET_NOEXCEPT { return true; } + virtual bool configureImpl(IParameterProvider& paramProvider, UnitOpIdx unitOpIdx, + ParticleTypeIdx parTypeIdx) override + { + return true; + } + virtual bool hasSalt() const CADET_NOEXCEPT + { + return false; + } + virtual bool supportsMultistate() const CADET_NOEXCEPT + { + return true; + } + virtual bool supportsNonBinding() const CADET_NOEXCEPT + { + return true; + } - CADET_BINDINGMODEL_RESIDUAL_BOILERPLATE - protected: - virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT { return true; } + CADET_BINDINGMODEL_RESIDUAL_BOILERPLATE +protected: + virtual bool implementsAnalyticJacobian() const CADET_NOEXCEPT + { + return true; + } - template - int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, - CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const - { - return 0; - } - }; + template + int fluxImpl(double t, unsigned int secIdx, const ColumnPosition& colPos, StateType const* y, + CpStateType const* yCp, ResidualType* res, LinearBufferAllocator workSpace) const + { + return 0; + } +}; } // namespace cadet @@ -134,10 +182,12 @@ TEST_CASE("Automatic AD binding model Jacobian vs FD", "[BindingModel],[Jacobian // Compare against FD: Since our flux() is linear, the Jacobian should be exact cadet::test::compareJacobianFD( - [=](double const* y, double* r) { std::fill_n(r, totalBoundStates, 0.0); bm.flux(0.0, 0u, cadet::ColumnPosition{0.0, 0.0, 0.0}, y + nComp, y, r, workSpace); }, + [=](double const* y, double* r) { + std::fill_n(r, totalBoundStates, 0.0); + bm.flux(0.0, 0u, cadet::ColumnPosition{0.0, 0.0, 0.0}, y + nComp, y, r, workSpace); + }, [&](double const* y, double* r) { jacAna.submatrixMultiplyVector(y, nComp, 0, totalBoundStates, numDofs, r); }, - y.data(), dir.data(), colA.data(), colB.data(), numDofs, totalBoundStates, 1e-7, 0.0, 1e-15 - ); + y.data(), dir.data(), colA.data(), colB.data(), numDofs, totalBoundStates, 1e-7, 0.0, 1e-15); } TEST_CASE("Automatic AD disabled for binding model with Jacobian", "[BindingModel],[Jacobian],[CI]") @@ -160,7 +210,8 @@ TEST_CASE("Automatic AD disabled for binding model with Jacobian", "[BindingMode REQUIRE(bm.workspaceSize(4, totalBoundStates, boundOffset) == 0); } -TEST_CASE("Full analytic Jacobian vs AD only enabled for binding model for a LRMP with multi-state SMA", "[BindingModel],[Jacobian],[Simulation],[CI]") +TEST_CASE("Full analytic Jacobian vs AD only enabled for binding model for a LRMP with multi-state SMA", + "[BindingModel],[Jacobian],[Simulation],[CI]") { nlohmann::json jsonJpp = createLWEJson("LUMPED_RATE_MODEL_WITH_PORES", "FV"); @@ -186,7 +237,8 @@ TEST_CASE("Full analytic Jacobian vs AD only enabled for binding model for a LRM double const* BndADJacOutlet = BndADJacData->outlet(); const unsigned int nComp = FullAnaJacData->numComponents(); - for (unsigned int i = 0; i < FullAnaJacData->numDataPoints() * FullAnaJacData->numInletPorts() * nComp; ++i, ++FullAnaJacOutlet, ++BndADJacOutlet) + for (unsigned int i = 0; i < FullAnaJacData->numDataPoints() * FullAnaJacData->numInletPorts() * nComp; + ++i, ++FullAnaJacOutlet, ++BndADJacOutlet) { CAPTURE(i); CHECK((*FullAnaJacOutlet) == cadet::test::makeApprox(*BndADJacOutlet, 1e-3, 5e-9)); // 1e-10, 1e-15 for DG diff --git a/test/BindingModelTests.cpp b/test/BindingModelTests.cpp index 49941c024..a854dcf4d 100644 --- a/test/BindingModelTests.cpp +++ b/test/BindingModelTests.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -34,15 +34,15 @@ namespace { - inline cadet::model::IBindingModel* createBindingModel(const char* name) - { - cadet::BindingModelFactory bmf; - cadet::model::IBindingModel* const bm = bmf.create(name); - - REQUIRE(nullptr != bm); - return bm; - } +inline cadet::model::IBindingModel* createBindingModel(const char* name) +{ + cadet::BindingModelFactory bmf; + cadet::model::IBindingModel* const bm = bmf.create(name); + + REQUIRE(nullptr != bm); + return bm; } +} // namespace namespace cadet { @@ -64,7 +64,8 @@ ConfiguredBindingModel::~ConfiguredBindingModel() ::operator delete(_bufferMemory); } -ConfiguredBindingModel ConfiguredBindingModel::create(const char* name, unsigned int nComp, unsigned int const* nBound, bool isKinetic, const char* config) +ConfiguredBindingModel ConfiguredBindingModel::create(const char* name, unsigned int nComp, unsigned int const* nBound, + bool isKinetic, const char* config) { cadet::model::IBindingModel* const bm = createBindingModel(name); @@ -73,7 +74,7 @@ ConfiguredBindingModel ConfiguredBindingModel::create(const char* name, unsigned boundOffset[0] = 0; for (unsigned int i = 1; i < nComp; ++i) { - boundOffset[i] = boundOffset[i-1] + nBound[i-1]; + boundOffset[i] = boundOffset[i - 1] + nBound[i - 1]; } const unsigned int totalBoundStates = boundOffset[nComp - 1] + nBound[nComp - 1]; @@ -111,7 +112,8 @@ ConfiguredBindingModel ConfiguredBindingModel::create(const char* name, unsigned return ConfiguredBindingModel(bm, nComp, nBound, boundOffset, buffer, bufferEnd, std::move(extFuns)); } -ConfiguredBindingModel ConfiguredBindingModel::create(const char* name, unsigned int nComp, unsigned int const* nBound, int const* isKinetic, const char* config) +ConfiguredBindingModel ConfiguredBindingModel::create(const char* name, unsigned int nComp, unsigned int const* nBound, + int const* isKinetic, const char* config) { cadet::model::IBindingModel* const bm = createBindingModel(name); @@ -120,7 +122,7 @@ ConfiguredBindingModel ConfiguredBindingModel::create(const char* name, unsigned boundOffset[0] = 0; for (unsigned int i = 1; i < nComp; ++i) { - boundOffset[i] = boundOffset[i-1] + nBound[i-1]; + boundOffset[i] = boundOffset[i - 1] + nBound[i - 1]; } const unsigned int totalBoundStates = boundOffset[nComp - 1] + nBound[nComp - 1]; @@ -187,7 +189,8 @@ int ConfiguredBindingModel::requiredBufferSize() CADET_NOEXCEPT return 0; } -void testJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, bool isKinetic, const char* config, double const* point, bool skipStructureTest, double absTol, double relTol) +void testJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, bool isKinetic, + const char* config, double const* point, bool skipStructureTest, double absTol, double relTol) { ConfiguredBindingModel cbm = ConfiguredBindingModel::create(modelName, nComp, nBound, isKinetic, config); @@ -204,7 +207,8 @@ void testJacobianAD(const char* modelName, unsigned int nComp, unsigned int cons // Calculate analytic Jacobian cadet::linalg::DenseMatrix jacAna; jacAna.resize(numDofs, numDofs); - cbm.model().analyticJacobian(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBound, cbm.nComp(), jacAna.row(cbm.nComp()), cbm.buffer()); + cbm.model().analyticJacobian(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBound, cbm.nComp(), jacAna.row(cbm.nComp()), + cbm.buffer()); // Enable AD cadet::ad::setDirections(cadet::ad::getMaxDirections()); @@ -214,7 +218,8 @@ void testJacobianAD(const char* modelName, unsigned int nComp, unsigned int cons // Evaluate with AD ad::prepareAdVectorSeedsForDenseMatrix(adY, 0, numDofs); ad::copyToAd(yState.data(), adY, numDofs); - cbm.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, adY + cbm.nComp(), adY, adRes, cbm.buffer(), cadet::WithoutParamSensitivity()); + cbm.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, adY + cbm.nComp(), adY, adRes, cbm.buffer(), + cadet::WithoutParamSensitivity()); // Extract Jacobian cadet::linalg::DenseMatrix jacAD; @@ -227,14 +232,20 @@ void testJacobianAD(const char* modelName, unsigned int nComp, unsigned int cons if (!skipStructureTest) { cadet::test::checkJacobianPatternFD( - [&](double const* lDir, double* res) -> void { cbm.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, lDir + cbm.nComp(), lDir, res, cbm.buffer()); }, - [&](double const* lDir, double* res) -> void { jacAna.submatrixMultiplyVector(lDir, cbm.nComp(), 0, numEq, numDofs, res); }, + [&](double const* lDir, double* res) -> void { + cbm.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, lDir + cbm.nComp(), lDir, res, cbm.buffer()); + }, + [&](double const* lDir, double* res) -> void { + jacAna.submatrixMultiplyVector(lDir, cbm.nComp(), 0, numEq, numDofs, res); + }, yState.data(), dir.data(), colA.data(), colB.data(), numDofs, numEq); cadet::test::checkJacobianPatternFD( - [&](double const* lDir, double* res) -> void { cbm.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, lDir + cbm.nComp(), lDir, res, cbm.buffer()); }, - [&](double const* lDir, double* res) -> void { jacAD.multiplyVector(lDir, res); }, - yState.data(), dir.data(), colA.data(), colB.data(), numDofs, numEq); + [&](double const* lDir, double* res) -> void { + cbm.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, lDir + cbm.nComp(), lDir, res, cbm.buffer()); + }, + [&](double const* lDir, double* res) -> void { jacAD.multiplyVector(lDir, res); }, yState.data(), + dir.data(), colA.data(), colB.data(), numDofs, numEq); } // Check Jacobians against each other @@ -249,7 +260,8 @@ void testJacobianAD(const char* modelName, unsigned int nComp, unsigned int cons } } -void testNormalExternalConsistency(const char* modelName, const char* modelNameExt, unsigned int nComp, unsigned int const* nBound, bool isKinetic, const char* config, double const* point) +void testNormalExternalConsistency(const char* modelName, const char* modelNameExt, unsigned int nComp, + unsigned int const* nBound, bool isKinetic, const char* config, double const* point) { ConfiguredBindingModel cbm = ConfiguredBindingModel::create(modelName, nComp, nBound, isKinetic, config); ConfiguredBindingModel cbmExt = ConfiguredBindingModel::create(modelNameExt, nComp, nBound, isKinetic, config); @@ -277,11 +289,13 @@ void testNormalExternalConsistency(const char* modelName, const char* modelNameE // Calculate analytic Jacobians cadet::linalg::DenseMatrix jac; jac.resize(numDofs, numDofs); - cbm.model().analyticJacobian(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBound, cbm.nComp(), jac.row(cbm.nComp()), cbm.buffer()); + cbm.model().analyticJacobian(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBound, cbm.nComp(), jac.row(cbm.nComp()), + cbm.buffer()); cadet::linalg::DenseMatrix jacExt; jacExt.resize(numDofs, numDofs); - cbmExt.model().analyticJacobian(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBound, cbm.nComp(), jacExt.row(cbmExt.nComp()), cbmExt.buffer()); + cbmExt.model().analyticJacobian(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBound, cbm.nComp(), + jacExt.row(cbmExt.nComp()), cbmExt.buffer()); // Check Jacobians against each other for (unsigned int r = cbm.nComp(); r < numDofs; ++r) @@ -296,7 +310,8 @@ void testNormalExternalConsistency(const char* modelName, const char* modelNameE } } -void testNonBindingConsistency(const char* modelName, unsigned int nComp, unsigned int const* nBound, bool isKinetic, const char* config, bool useAD, double const* point) +void testNonBindingConsistency(const char* modelName, unsigned int nComp, unsigned int const* nBound, bool isKinetic, + const char* config, bool useAD, double const* point) { ConfiguredBindingModel cbm = ConfiguredBindingModel::create(modelName, nComp, nBound, isKinetic, config); @@ -321,7 +336,8 @@ void testNonBindingConsistency(const char* modelName, unsigned int nComp, unsign // Evaluate with AD ad::prepareAdVectorSeedsForDenseMatrix(adY, 0, numDofs); ad::copyToAd(yState.data(), adY, numDofs); - cbm.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, adY + cbm.nComp(), adY, adRes, cbm.buffer(), cadet::WithoutParamSensitivity()); + cbm.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, adY + cbm.nComp(), adY, adRes, cbm.buffer(), + cadet::WithoutParamSensitivity()); // Extract Jacobian ad::extractDenseJacobianFromAd(adRes, 0, jac); @@ -332,7 +348,8 @@ void testNonBindingConsistency(const char* modelName, unsigned int nComp, unsign else { jac.resize(numDofs, numDofs); - cbm.model().analyticJacobian(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBound, cbm.nComp(), jac.row(cbm.nComp()), cbm.buffer()); + cbm.model().analyticJacobian(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBound, cbm.nComp(), jac.row(cbm.nComp()), + cbm.buffer()); } // Check that columns of non-binding liquid phase components are all zero @@ -359,10 +376,14 @@ void testNonBindingConsistency(const char* modelName, unsigned int nComp, unsign } } -void testNonbindingBindingConsistency(const char* modelName, unsigned int nCompBnd, unsigned int nCompNonBnd, unsigned int const* nBound, unsigned int const* nBoundNonBnd, bool isKinetic, const char* configBnd, const char* configNonBnd, bool useAD, double const* pointBnd, double const* pointNonBnd) +void testNonbindingBindingConsistency(const char* modelName, unsigned int nCompBnd, unsigned int nCompNonBnd, + unsigned int const* nBound, unsigned int const* nBoundNonBnd, bool isKinetic, + const char* configBnd, const char* configNonBnd, bool useAD, + double const* pointBnd, double const* pointNonBnd) { ConfiguredBindingModel cbmBnd = ConfiguredBindingModel::create(modelName, nCompBnd, nBound, isKinetic, configBnd); - ConfiguredBindingModel cbmNonBnd = ConfiguredBindingModel::create(modelName, nCompNonBnd, nBoundNonBnd, isKinetic, configNonBnd); + ConfiguredBindingModel cbmNonBnd = + ConfiguredBindingModel::create(modelName, nCompNonBnd, nBoundNonBnd, isKinetic, configNonBnd); // Setup all binding const unsigned int numDofsBnd = cbmBnd.nComp() + cbmBnd.numBoundStates(); @@ -387,8 +408,10 @@ void testNonbindingBindingConsistency(const char* modelName, unsigned int nCompB std::vector res1(numEqBnd, 0.0); std::vector res2(numEqBnd, 0.0); - cbmBnd.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBoundBnd, yStateBnd.data(), res1.data(), cbmBnd.buffer()); - cbmNonBnd.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBoundNonBnd, yStateNonBnd.data(), res2.data(), cbmNonBnd.buffer()); + cbmBnd.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBoundBnd, yStateBnd.data(), res1.data(), + cbmBnd.buffer()); + cbmNonBnd.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBoundNonBnd, yStateNonBnd.data(), res2.data(), + cbmNonBnd.buffer()); for (unsigned int i = 0; i < numEqBnd; ++i) { @@ -412,7 +435,8 @@ void testNonbindingBindingConsistency(const char* modelName, unsigned int nCompB // Evaluate with AD, all binding ad::prepareAdVectorSeedsForDenseMatrix(adY, 0, numDofsBnd); ad::copyToAd(yStateBnd.data(), adY, numDofsBnd); - cbmBnd.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, adY + cbmBnd.nComp(), adY, adRes, cbmBnd.buffer(), cadet::WithoutParamSensitivity()); + cbmBnd.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, adY + cbmBnd.nComp(), adY, adRes, cbmBnd.buffer(), + cadet::WithoutParamSensitivity()); // Extract Jacobian, all binding ad::extractDenseJacobianFromAd(adRes, 0, jacBnd); @@ -422,7 +446,8 @@ void testNonbindingBindingConsistency(const char* modelName, unsigned int nCompB ad::resetAd(adY, numDofsNonBnd); ad::prepareAdVectorSeedsForDenseMatrix(adY, 0, numDofsNonBnd); ad::copyToAd(yStateNonBnd.data(), adY, numDofsNonBnd); - cbmNonBnd.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, adY + cbmNonBnd.nComp(), adY, adRes, cbmNonBnd.buffer(), cadet::WithoutParamSensitivity()); + cbmNonBnd.model().flux(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, adY + cbmNonBnd.nComp(), adY, adRes, + cbmNonBnd.buffer(), cadet::WithoutParamSensitivity()); // Extract Jacobian, with nonbinding ad::extractDenseJacobianFromAd(adRes, 0, jacNonBnd); @@ -434,8 +459,10 @@ void testNonbindingBindingConsistency(const char* modelName, unsigned int nCompB { jacBnd.resize(numDofsBnd, numDofsBnd); jacNonBnd.resize(numDofsNonBnd, numDofsNonBnd); - cbmBnd.model().analyticJacobian(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBoundBnd, cbmBnd.nComp(), jacBnd.row(cbmBnd.nComp()), cbmBnd.buffer()); - cbmNonBnd.model().analyticJacobian(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBoundNonBnd, cbmNonBnd.nComp(), jacNonBnd.row(cbmNonBnd.nComp()), cbmNonBnd.buffer()); + cbmBnd.model().analyticJacobian(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBoundBnd, cbmBnd.nComp(), + jacBnd.row(cbmBnd.nComp()), cbmBnd.buffer()); + cbmNonBnd.model().analyticJacobian(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yBoundNonBnd, cbmNonBnd.nComp(), + jacNonBnd.row(cbmNonBnd.nComp()), cbmNonBnd.buffer()); } // Compare Jacobians @@ -467,7 +494,8 @@ void testNonbindingBindingConsistency(const char* modelName, unsigned int nCompB { CAPTURE(col); CAPTURE(row); - CHECK(jacBnd.native(row + rowOffsetBnd, col + nCompBnd) == jacNonBnd.native(row + rowOffsetNonBnd, col + nCompNonBnd)); + CHECK(jacBnd.native(row + rowOffsetBnd, col + nCompBnd) == + jacNonBnd.native(row + rowOffsetNonBnd, col + nCompNonBnd)); } } } diff --git a/test/BindingModelTests.hpp b/test/BindingModelTests.hpp index 2745f23da..60c2007b0 100644 --- a/test/BindingModelTests.hpp +++ b/test/BindingModelTests.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines tests for binding models. */ @@ -27,7 +27,7 @@ namespace cadet namespace model { - class IBindingModel; +class IBindingModel; } namespace test @@ -36,147 +36,211 @@ namespace test namespace binding { - class ConstExternalFunction : public cadet::IExternalFunction - { - public: - virtual bool configure(cadet::IParameterProvider* paramProvider) { return true; } - virtual const char* name() const CADET_NOEXCEPT { return "CONSTFUN"; } - virtual double externalProfile(double t, double z, double rho, double r, unsigned int sec) { return 1.0; } - virtual double timeDerivative(double t, double z, double rho, double r, unsigned int sec) { return 0.0; } - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { } - }; - - class LinearExternalFunction : public cadet::IExternalFunction - { - public: - virtual bool configure(cadet::IParameterProvider* paramProvider) { return true; } - virtual const char* name() const CADET_NOEXCEPT { return "LINFUN"; } - virtual double externalProfile(double t, double z, double rho, double r, unsigned int sec) { return t; } - virtual double timeDerivative(double t, double z, double rho, double r, unsigned int sec) { return 1.0; } - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { } - }; - - class ConfiguredBindingModel - { - public: - - ConfiguredBindingModel(ConfiguredBindingModel&& cpy) CADET_NOEXCEPT - : _binding(cpy._binding), _nComp(cpy._nComp), _nBound(cpy._nBound), _boundOffset(cpy._boundOffset), _buffer(std::move(cpy._buffer)), _bufferMemory(cpy._bufferMemory), _extFuns(std::move(cpy._extFuns)) - { - cpy._binding = nullptr; - cpy._nBound = nullptr; - cpy._boundOffset = nullptr; - cpy._bufferMemory = nullptr; - } - - ~ConfiguredBindingModel(); - - inline ConfiguredBindingModel& operator=(ConfiguredBindingModel&& cpy) CADET_NOEXCEPT - { - _binding = cpy._binding; - _nComp = cpy._nComp; - _nBound = cpy._nBound; - _boundOffset = cpy._boundOffset; - _buffer = std::move(cpy._buffer); - _bufferMemory = cpy._bufferMemory; - _extFuns = std::move(cpy._extFuns); - - cpy._binding = nullptr; - cpy._nBound = nullptr; - cpy._boundOffset = nullptr; - cpy._bufferMemory = nullptr; - - return *this; - } - - static ConfiguredBindingModel create(const char* name, unsigned int nComp, unsigned int const* nBound, bool isKinetic, const char* config); - static ConfiguredBindingModel create(const char* name, unsigned int nComp, unsigned int const* nBound, int const* isKinetic, const char* config); - - void increaseBufferSize(int inc); - int requiredBufferSize() CADET_NOEXCEPT; - - inline cadet::model::IBindingModel& model() { return *_binding; } - inline const cadet::model::IBindingModel& model() const { return *_binding; } - - inline cadet::LinearBufferAllocator buffer() { return _buffer; } - inline unsigned int nComp() const { return _nComp; } - inline unsigned int const* nBound() const { return _nBound; } - inline unsigned int const* boundOffset() const { return _boundOffset; } - - inline unsigned int numBoundStates() const { return _boundOffset[_nComp - 1] + _nBound[_nComp - 1]; } - - private: - - ConfiguredBindingModel(cadet::model::IBindingModel* binding, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset, void* bufferStart, void* bufferEnd, std::vector&& extFuns) - : _binding(binding), _nComp(nComp), _nBound(nBound), _boundOffset(boundOffset), _buffer(bufferStart, bufferEnd), _bufferMemory(bufferStart), _extFuns(std::move(extFuns)) - { - } - - cadet::model::IBindingModel* _binding; - unsigned int _nComp; - unsigned int const* _nBound; - unsigned int const* _boundOffset; - cadet::LinearBufferAllocator _buffer; - void* _bufferMemory; - std::vector _extFuns; - }; - - /** - * @brief Checks the analytic Jacobian of the binding model against AD - * @param [in] modelName Name of the binding model - * @param [in] nComp Number of components - * @param [in] nBound Array with number of bound states for each component - * @param [in] isKinetic Determines whether kinetic or quasi-stationary binding mode is applied - * @param [in] config JSON string with binding model parameters - * @param [in] point Liquid phase and solid phase values to check Jacobian at - * @param [in] skipStructureTest Determines whether the structural test using finite differences is skipped - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, bool isKinetic, const char* config, double const* point, bool skipStructureTest = false, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0); - - /** - * @brief Checks residual and analytic Jacobian of normal model variant against externally dependent ones - * @param [in] modelName Name of the binding model - * @param [in] modelNameExt Name of the externally dependent binding model variant - * @param [in] nComp Number of components - * @param [in] nBound Array with number of bound states for each component - * @param [in] isKinetic Determines whether kinetic or quasi-stationary binding mode is applied - * @param [in] config JSON string with binding model parameters for both variants - * @param [in] point Liquid phase and solid phase values to evaluate residual at - */ - void testNormalExternalConsistency(const char* modelName, const char* modelNameExt, unsigned int nComp, unsigned int const* nBound, bool isKinetic, const char* config, double const* point); - - /** - * @brief Checks whether Jacobian columns of non-binding liquid phase components are all zero - * @param [in] modelName Name of the binding model - * @param [in] nComp Number of components - * @param [in] nBound Array with number of bound states for each component - * @param [in] isKinetic Determines whether kinetic or quasi-stationary binding mode is applied - * @param [in] config JSON string with binding model parameters - * @param [in] useAD Determines whether the Jacobian is computed via AD or analytically - * @param [in] point Liquid phase and solid phase values to evaluate Jacobian at - */ - void testNonBindingConsistency(const char* modelName, unsigned int nComp, unsigned int const* nBound, bool isKinetic, const char* config, bool useAD, double const* point); - - /** - * @brief Checks whether Jacobian and residual of variants with all-binding and some non-binding components match - * @param [in] modelName Name of the binding model - * @param [in] nCompBnd Number of components in all binding variant - * @param [in] nCompNonBnd Number of components in non-binding variant - * @param [in] nBound Array with number of bound states for each component in all binding variant - * @param [in] nBoundNonBnd Array with number of bound states for each component in non-binding variant - * @param [in] isKinetic Determines whether kinetic or quasi-stationary binding mode is applied - * @param [in] configBnd JSON string with binding model parameters for all binding variant - * @param [in] configNonBnd JSON string with binding model parameters for non-binding variant - * @param [in] useAD Determines whether the Jacobian is computed via AD or analytically - * @param [in] pointBnd Liquid phase and solid phase values to evaluate Jacobian at in all binding variant - * @param [in] pointNonBnd Liquid phase and solid phase values to evaluate Jacobian at in non-binding variant - */ - void testNonbindingBindingConsistency(const char* modelName, unsigned int nCompBnd, unsigned int nCompNonBnd, unsigned int const* nBound, unsigned int const* nBoundNonBnd, bool isKinetic, const char* configBnd, const char* configNonBnd, bool useAD, double const* pointBnd, double const* pointNonBnd); +class ConstExternalFunction : public cadet::IExternalFunction +{ +public: + virtual bool configure(cadet::IParameterProvider* paramProvider) + { + return true; + } + virtual const char* name() const CADET_NOEXCEPT + { + return "CONSTFUN"; + } + virtual double externalProfile(double t, double z, double rho, double r, unsigned int sec) + { + return 1.0; + } + virtual double timeDerivative(double t, double z, double rho, double r, unsigned int sec) + { + return 0.0; + } + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) + { + } +}; + +class LinearExternalFunction : public cadet::IExternalFunction +{ +public: + virtual bool configure(cadet::IParameterProvider* paramProvider) + { + return true; + } + virtual const char* name() const CADET_NOEXCEPT + { + return "LINFUN"; + } + virtual double externalProfile(double t, double z, double rho, double r, unsigned int sec) + { + return t; + } + virtual double timeDerivative(double t, double z, double rho, double r, unsigned int sec) + { + return 1.0; + } + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) + { + } +}; + +class ConfiguredBindingModel +{ +public: + ConfiguredBindingModel(ConfiguredBindingModel&& cpy) CADET_NOEXCEPT : _binding(cpy._binding), + _nComp(cpy._nComp), + _nBound(cpy._nBound), + _boundOffset(cpy._boundOffset), + _buffer(std::move(cpy._buffer)), + _bufferMemory(cpy._bufferMemory), + _extFuns(std::move(cpy._extFuns)) + { + cpy._binding = nullptr; + cpy._nBound = nullptr; + cpy._boundOffset = nullptr; + cpy._bufferMemory = nullptr; + } + + ~ConfiguredBindingModel(); + + inline ConfiguredBindingModel& operator=(ConfiguredBindingModel&& cpy) CADET_NOEXCEPT + { + _binding = cpy._binding; + _nComp = cpy._nComp; + _nBound = cpy._nBound; + _boundOffset = cpy._boundOffset; + _buffer = std::move(cpy._buffer); + _bufferMemory = cpy._bufferMemory; + _extFuns = std::move(cpy._extFuns); + + cpy._binding = nullptr; + cpy._nBound = nullptr; + cpy._boundOffset = nullptr; + cpy._bufferMemory = nullptr; + + return *this; + } + + static ConfiguredBindingModel create(const char* name, unsigned int nComp, unsigned int const* nBound, + bool isKinetic, const char* config); + static ConfiguredBindingModel create(const char* name, unsigned int nComp, unsigned int const* nBound, + int const* isKinetic, const char* config); + + void increaseBufferSize(int inc); + int requiredBufferSize() CADET_NOEXCEPT; + + inline cadet::model::IBindingModel& model() + { + return *_binding; + } + inline const cadet::model::IBindingModel& model() const + { + return *_binding; + } + + inline cadet::LinearBufferAllocator buffer() + { + return _buffer; + } + inline unsigned int nComp() const + { + return _nComp; + } + inline unsigned int const* nBound() const + { + return _nBound; + } + inline unsigned int const* boundOffset() const + { + return _boundOffset; + } + + inline unsigned int numBoundStates() const + { + return _boundOffset[_nComp - 1] + _nBound[_nComp - 1]; + } + +private: + ConfiguredBindingModel(cadet::model::IBindingModel* binding, unsigned int nComp, unsigned int const* nBound, + unsigned int const* boundOffset, void* bufferStart, void* bufferEnd, + std::vector&& extFuns) + : _binding(binding), _nComp(nComp), _nBound(nBound), _boundOffset(boundOffset), _buffer(bufferStart, bufferEnd), + _bufferMemory(bufferStart), _extFuns(std::move(extFuns)) + { + } + + cadet::model::IBindingModel* _binding; + unsigned int _nComp; + unsigned int const* _nBound; + unsigned int const* _boundOffset; + cadet::LinearBufferAllocator _buffer; + void* _bufferMemory; + std::vector _extFuns; +}; + +/** + * @brief Checks the analytic Jacobian of the binding model against AD + * @param [in] modelName Name of the binding model + * @param [in] nComp Number of components + * @param [in] nBound Array with number of bound states for each component + * @param [in] isKinetic Determines whether kinetic or quasi-stationary binding mode is applied + * @param [in] config JSON string with binding model parameters + * @param [in] point Liquid phase and solid phase values to check Jacobian at + * @param [in] skipStructureTest Determines whether the structural test using finite differences is skipped + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, bool isKinetic, + const char* config, double const* point, bool skipStructureTest = false, double absTol = 0.0, + double relTol = std::numeric_limits::epsilon() * 100.0); + +/** + * @brief Checks residual and analytic Jacobian of normal model variant against externally dependent ones + * @param [in] modelName Name of the binding model + * @param [in] modelNameExt Name of the externally dependent binding model variant + * @param [in] nComp Number of components + * @param [in] nBound Array with number of bound states for each component + * @param [in] isKinetic Determines whether kinetic or quasi-stationary binding mode is applied + * @param [in] config JSON string with binding model parameters for both variants + * @param [in] point Liquid phase and solid phase values to evaluate residual at + */ +void testNormalExternalConsistency(const char* modelName, const char* modelNameExt, unsigned int nComp, + unsigned int const* nBound, bool isKinetic, const char* config, double const* point); + +/** + * @brief Checks whether Jacobian columns of non-binding liquid phase components are all zero + * @param [in] modelName Name of the binding model + * @param [in] nComp Number of components + * @param [in] nBound Array with number of bound states for each component + * @param [in] isKinetic Determines whether kinetic or quasi-stationary binding mode is applied + * @param [in] config JSON string with binding model parameters + * @param [in] useAD Determines whether the Jacobian is computed via AD or analytically + * @param [in] point Liquid phase and solid phase values to evaluate Jacobian at + */ +void testNonBindingConsistency(const char* modelName, unsigned int nComp, unsigned int const* nBound, bool isKinetic, + const char* config, bool useAD, double const* point); + +/** + * @brief Checks whether Jacobian and residual of variants with all-binding and some non-binding components match + * @param [in] modelName Name of the binding model + * @param [in] nCompBnd Number of components in all binding variant + * @param [in] nCompNonBnd Number of components in non-binding variant + * @param [in] nBound Array with number of bound states for each component in all binding variant + * @param [in] nBoundNonBnd Array with number of bound states for each component in non-binding variant + * @param [in] isKinetic Determines whether kinetic or quasi-stationary binding mode is applied + * @param [in] configBnd JSON string with binding model parameters for all binding variant + * @param [in] configNonBnd JSON string with binding model parameters for non-binding variant + * @param [in] useAD Determines whether the Jacobian is computed via AD or analytically + * @param [in] pointBnd Liquid phase and solid phase values to evaluate Jacobian at in all binding variant + * @param [in] pointNonBnd Liquid phase and solid phase values to evaluate Jacobian at in non-binding variant + */ +void testNonbindingBindingConsistency(const char* modelName, unsigned int nCompBnd, unsigned int nCompNonBnd, + unsigned int const* nBound, unsigned int const* nBoundNonBnd, bool isKinetic, + const char* configBnd, const char* configNonBnd, bool useAD, + double const* pointBnd, double const* pointNonBnd); } // namespace binding } // namespace test } // namespace cadet -#endif // CADETTEST_BINDINGMODELTEST_HPP_ +#endif // CADETTEST_BINDINGMODELTEST_HPP_ diff --git a/test/BindingModels.cpp b/test/BindingModels.cpp index 661c2f11a..55c85bac1 100644 --- a/test/BindingModels.cpp +++ b/test/BindingModels.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -15,14 +15,14 @@ #include "BindingModelTests.hpp" #include "BindingModels.hpp" -CADET_BINDINGTEST("LINEAR", "EXT_LINEAR", (1,1), (1,0,1), (1.0, 2.0, 0.0, 0.0), (1.0, 3.0, 2.0, 0.0, 0.0), \ - R"json( "LIN_KA": [1.0, 2.0], +CADET_BINDINGTEST("LINEAR", "EXT_LINEAR", (1, 1), (1, 0, 1), (1.0, 2.0, 0.0, 0.0), (1.0, 3.0, 2.0, 0.0, 0.0), + R"json( "LIN_KA": [1.0, 2.0], "LIN_KD": [0.1, 0.2] - )json", \ - R"json( "LIN_KA": [1.0, 3.0, 2.0], + )json", + R"json( "LIN_KA": [1.0, 3.0, 2.0], "LIN_KD": [0.1, 0.3, 0.2] - )json", \ - R"json( "EXT_LIN_KA": [0.0, 0.0], + )json", + R"json( "EXT_LIN_KA": [0.0, 0.0], "EXT_LIN_KA_T": [1.0, 2.0], "EXT_LIN_KA_TT": [0.0, 0.0], "EXT_LIN_KA_TTT": [0.0, 0.0], @@ -30,8 +30,8 @@ CADET_BINDINGTEST("LINEAR", "EXT_LINEAR", (1,1), (1,0,1), (1.0, 2.0, 0.0, 0.0), "EXT_LIN_KD_T": [0.1, 0.2], "EXT_LIN_KD_TT": [0.0, 0.0], "EXT_LIN_KD_TTT": [0.0, 0.0] - )json", \ - R"json( "EXT_LIN_KA": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_LIN_KA": [0.0, 0.0, 0.0], "EXT_LIN_KA_T": [1.0, 3.0, 2.0], "EXT_LIN_KA_TT": [0.0, 0.0, 0.0], "EXT_LIN_KA_TTT": [0.0, 0.0, 0.0], @@ -39,19 +39,20 @@ CADET_BINDINGTEST("LINEAR", "EXT_LINEAR", (1,1), (1,0,1), (1.0, 2.0, 0.0, 0.0), "EXT_LIN_KD_T": [0.1, 0.3, 0.2], "EXT_LIN_KD_TT": [0.0, 0.0, 0.0], "EXT_LIN_KD_TTT": [0.0, 0.0, 0.0] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) - CADET_BINDINGTEST("FREUNDLICH_LDF", "EXT_FREUNDLICH_LDF", (1, 1), (1, 0, 1), (1.0, 2.0, 0.0, 0.0), (1.0, 3.0, 2.0, 0.0, 0.0), \ - R"json( "FLDF_KKIN": [1.0, 2.0], +CADET_BINDINGTEST("FREUNDLICH_LDF", "EXT_FREUNDLICH_LDF", (1, 1), (1, 0, 1), (1.0, 2.0, 0.0, 0.0), + (1.0, 3.0, 2.0, 0.0, 0.0), + R"json( "FLDF_KKIN": [1.0, 2.0], "FLDF_KF": [0.1, 0.2], "FLDF_N": [0.5, 1.2] - )json", \ - R"json( "FLDF_KKIN": [1.0, 0.5, 2.0], + )json", + R"json( "FLDF_KKIN": [1.0, 0.5, 2.0], "FLDF_KF": [0.1, 0.3, 0.2], "FLDF_N": [0.5, 2.2, 1.2] - )json", \ - R"json( "EXT_FLDF_KKIN": [0.0, 0.0], + )json", + R"json( "EXT_FLDF_KKIN": [0.0, 0.0], "EXT_FLDF_KKIN_T": [1.0, 2.0], "EXT_FLDF_KKIN_TT": [0.0, 0.0], "EXT_FLDF_KKIN_TTT": [0.0, 0.0], @@ -63,8 +64,8 @@ CADET_BINDINGTEST("LINEAR", "EXT_LINEAR", (1,1), (1,0,1), (1.0, 2.0, 0.0, 0.0), "EXT_FLDF_N_T": [0.5, 1.2], "EXT_FLDF_N_TT": [0.0, 0.0], "EXT_FLDF_N_TTT": [0.0, 0.0] - )json", \ - R"json( "EXT_FLDF_KKIN": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_FLDF_KKIN": [0.0, 0.0, 0.0], "EXT_FLDF_KKIN_T": [1.0, 0.5, 2.0], "EXT_FLDF_KKIN_TT": [0.0, 0.0, 0.0], "EXT_FLDF_KKIN_TTT": [0.0, 0.0, 0.0], @@ -76,19 +77,20 @@ CADET_BINDINGTEST("LINEAR", "EXT_LINEAR", (1,1), (1,0,1), (1.0, 2.0, 0.0, 0.0), "EXT_FLDF_N_T": [0.5, 2.0, 1.2], "EXT_FLDF_N_TT": [0.0, 0.0, 0.0], "EXT_FLDF_N_TTT": [0.0, 0.0, 0.0] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) -CADET_BINDINGTEST("MULTI_COMPONENT_LANGMUIR", "EXT_MULTI_COMPONENT_LANGMUIR", (1,1), (1,0,1), (1.0, 2.0, 0.0, 0.0), (1.0, 3.0, 2.0, 0.0, 0.0), \ - R"json( "MCL_KA": [1.14, 2.0], +CADET_BINDINGTEST("MULTI_COMPONENT_LANGMUIR", "EXT_MULTI_COMPONENT_LANGMUIR", (1, 1), (1, 0, 1), (1.0, 2.0, 0.0, 0.0), + (1.0, 3.0, 2.0, 0.0, 0.0), + R"json( "MCL_KA": [1.14, 2.0], "MCL_KD": [0.004, 0.008], "MCL_QMAX": [4.88, 3.5] - )json", \ - R"json( "MCL_KA": [1.14, 1.0, 2.0], + )json", + R"json( "MCL_KA": [1.14, 1.0, 2.0], "MCL_KD": [0.004, 2.0, 0.008], "MCL_QMAX": [4.88, 3.0, 3.5] - )json", \ - R"json( "EXT_MCL_KA": [0.0, 0.0], + )json", + R"json( "EXT_MCL_KA": [0.0, 0.0], "EXT_MCL_KA_T": [1.14, 2.0], "EXT_MCL_KA_TT": [0.0, 0.0], "EXT_MCL_KA_TTT": [0.0, 0.0], @@ -100,8 +102,8 @@ CADET_BINDINGTEST("MULTI_COMPONENT_LANGMUIR", "EXT_MULTI_COMPONENT_LANGMUIR", (1 "EXT_MCL_QMAX_T": [4.88, 3.5], "EXT_MCL_QMAX_TT": [0.0, 0.0], "EXT_MCL_QMAX_TTT": [0.0, 0.0] - )json", \ - R"json( "EXT_MCL_KA": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_MCL_KA": [0.0, 0.0, 0.0], "EXT_MCL_KA_T": [1.14, 1.0, 2.0], "EXT_MCL_KA_TT": [0.0, 0.0, 0.0], "EXT_MCL_KA_TTT": [0.0, 0.0, 0.0], @@ -113,19 +115,20 @@ CADET_BINDINGTEST("MULTI_COMPONENT_LANGMUIR", "EXT_MULTI_COMPONENT_LANGMUIR", (1 "EXT_MCL_QMAX_T": [4.88, 3.0, 3.5], "EXT_MCL_QMAX_TT": [0.0, 0.0, 0.0], "EXT_MCL_QMAX_TTT": [0.0, 0.0, 0.0] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) - CADET_BINDINGTEST("MULTI_COMPONENT_LANGMUIR_LDF", "EXT_MULTI_COMPONENT_LANGMUIR_LDF", (1, 1), (1, 0, 1), (1.0, 2.0, 0.0, 0.0), (1.0, 3.0, 2.0, 0.0, 0.0), \ - R"json( "MCLLDF_KEQ": [1.14, 2.0], +CADET_BINDINGTEST("MULTI_COMPONENT_LANGMUIR_LDF", "EXT_MULTI_COMPONENT_LANGMUIR_LDF", (1, 1), (1, 0, 1), + (1.0, 2.0, 0.0, 0.0), (1.0, 3.0, 2.0, 0.0, 0.0), + R"json( "MCLLDF_KEQ": [1.14, 2.0], "MCLLDF_KKIN": [0.004, 0.008], "MCLLDF_QMAX": [4.88, 3.5] - )json", \ - R"json( "MCLLDF_KEQ": [1.14, 1.0, 2.0], + )json", + R"json( "MCLLDF_KEQ": [1.14, 1.0, 2.0], "MCLLDF_KKIN": [0.004, 2.0, 0.008], "MCLLDF_QMAX": [4.88, 3.0, 3.5] - )json", \ - R"json( "EXT_MCLLDF_KEQ": [0.0, 0.0], + )json", + R"json( "EXT_MCLLDF_KEQ": [0.0, 0.0], "EXT_MCLLDF_KEQ_T": [1.14, 2.0], "EXT_MCLLDF_KEQ_TT": [0.0, 0.0], "EXT_MCLLDF_KEQ_TTT": [0.0, 0.0], @@ -137,8 +140,8 @@ CADET_BINDINGTEST("MULTI_COMPONENT_LANGMUIR", "EXT_MULTI_COMPONENT_LANGMUIR", (1 "EXT_MCLLDF_QMAX_T": [4.88, 3.5], "EXT_MCLLDF_QMAX_TT": [0.0, 0.0], "EXT_MCLLDF_QMAX_TTT": [0.0, 0.0] - )json", \ - R"json( "EXT_MCLLDF_KEQ": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_MCLLDF_KEQ": [0.0, 0.0, 0.0], "EXT_MCLLDF_KEQ_T": [1.14, 1.0, 2.0], "EXT_MCLLDF_KEQ_TT": [0.0, 0.0, 0.0], "EXT_MCLLDF_KEQ_TTT": [0.0, 0.0, 0.0], @@ -150,19 +153,20 @@ CADET_BINDINGTEST("MULTI_COMPONENT_LANGMUIR", "EXT_MULTI_COMPONENT_LANGMUIR", (1 "EXT_MCLLDF_QMAX_T": [4.88, 3.0, 3.5], "EXT_MCLLDF_QMAX_TT": [0.0, 0.0, 0.0], "EXT_MCLLDF_QMAX_TTT": [0.0, 0.0, 0.0] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) - CADET_BINDINGTEST("MULTI_COMPONENT_LANGMUIR_LDF_LIQUID_PHASE", "EXT_MULTI_COMPONENT_LANGMUIR_LDF_LIQUID_PHASE", (1, 1), (1, 0, 1), (1.0, 2.0, 0.0, 0.0), (1.0, 3.0, 2.0, 0.0, 0.0), \ - R"json( "MCLLDFC_KEQ": [1.14, 2.0], +CADET_BINDINGTEST("MULTI_COMPONENT_LANGMUIR_LDF_LIQUID_PHASE", "EXT_MULTI_COMPONENT_LANGMUIR_LDF_LIQUID_PHASE", (1, 1), + (1, 0, 1), (1.0, 2.0, 0.0, 0.0), (1.0, 3.0, 2.0, 0.0, 0.0), + R"json( "MCLLDFC_KEQ": [1.14, 2.0], "MCLLDFC_KKIN": [0.004, 0.008], "MCLLDFC_QMAX": [4.88, 3.5] - )json", \ - R"json( "MCLLDFC_KEQ": [1.14, 1.0, 2.0], + )json", + R"json( "MCLLDFC_KEQ": [1.14, 1.0, 2.0], "MCLLDFC_KKIN": [0.004, 2.0, 0.008], "MCLLDFC_QMAX": [4.88, 3.0, 3.5] - )json", \ - R"json( "EXT_MCLLDFC_KEQ": [0.0, 0.0], + )json", + R"json( "EXT_MCLLDFC_KEQ": [0.0, 0.0], "EXT_MCLLDFC_KEQ_T": [1.14, 2.0], "EXT_MCLLDFC_KEQ_TT": [0.0, 0.0], "EXT_MCLLDFC_KEQ_TTT": [0.0, 0.0], @@ -174,8 +178,8 @@ CADET_BINDINGTEST("MULTI_COMPONENT_LANGMUIR", "EXT_MULTI_COMPONENT_LANGMUIR", (1 "EXT_MCLLDFC_QMAX_T": [4.88, 3.5], "EXT_MCLLDFC_QMAX_TT": [0.0, 0.0], "EXT_MCLLDFC_QMAX_TTT": [0.0, 0.0] - )json", \ - R"json( "EXT_MCLLDFC_KEQ": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_MCLLDFC_KEQ": [0.0, 0.0, 0.0], "EXT_MCLLDFC_KEQ_T": [1.14, 1.0, 2.0], "EXT_MCLLDFC_KEQ_TT": [0.0, 0.0, 0.0], "EXT_MCLLDFC_KEQ_TTT": [0.0, 0.0, 0.0], @@ -187,21 +191,22 @@ CADET_BINDINGTEST("MULTI_COMPONENT_LANGMUIR", "EXT_MULTI_COMPONENT_LANGMUIR", (1 "EXT_MCLLDFC_QMAX_T": [4.88, 3.0, 3.5], "EXT_MCLLDFC_QMAX_TT": [0.0, 0.0, 0.0], "EXT_MCLLDFC_QMAX_TTT": [0.0, 0.0, 0.0] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) -CADET_BINDINGTEST("MULTI_COMPONENT_ANTILANGMUIR", "EXT_MULTI_COMPONENT_ANTILANGMUIR", (1,1), (1,0,1), (1.0, 2.0, 0.0, 0.0), (1.0, 3.0, 2.0, 0.0, 0.0), \ - R"json( "MCAL_KA": [1.14, 2.0], +CADET_BINDINGTEST("MULTI_COMPONENT_ANTILANGMUIR", "EXT_MULTI_COMPONENT_ANTILANGMUIR", (1, 1), (1, 0, 1), + (1.0, 2.0, 0.0, 0.0), (1.0, 3.0, 2.0, 0.0, 0.0), + R"json( "MCAL_KA": [1.14, 2.0], "MCAL_KD": [0.004, 0.008], "MCAL_QMAX": [4.88, 3.5], "MCAL_ANTILANGMUIR": [1.0, -1.0] - )json", \ - R"json( "MCAL_KA": [1.14, 1.0, 2.0], + )json", + R"json( "MCAL_KA": [1.14, 1.0, 2.0], "MCAL_KD": [0.004, 2.0, 0.008], "MCAL_QMAX": [4.88, 3.0, 3.5], "MCAL_ANTILANGMUIR": [1.0, 0.0, -1.0] - )json", \ - R"json( "EXT_MCAL_KA": [0.0, 0.0], + )json", + R"json( "EXT_MCAL_KA": [0.0, 0.0], "EXT_MCAL_KA_T": [1.14, 2.0], "EXT_MCAL_KA_TT": [0.0, 0.0], "EXT_MCAL_KA_TTT": [0.0, 0.0], @@ -217,8 +222,8 @@ CADET_BINDINGTEST("MULTI_COMPONENT_ANTILANGMUIR", "EXT_MULTI_COMPONENT_ANTILANGM "EXT_MCAL_ANTILANGMUIR_T": [0.0, 0.0], "EXT_MCAL_ANTILANGMUIR_TT": [0.0, 0.0], "EXT_MCAL_ANTILANGMUIR_TTT": [0.0, 0.0] - )json", \ - R"json( "EXT_MCAL_KA": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_MCAL_KA": [0.0, 0.0, 0.0], "EXT_MCAL_KA_T": [1.14, 1.0, 2.0], "EXT_MCAL_KA_TT": [0.0, 0.0, 0.0], "EXT_MCAL_KA_TTT": [0.0, 0.0, 0.0], @@ -234,26 +239,26 @@ CADET_BINDINGTEST("MULTI_COMPONENT_ANTILANGMUIR", "EXT_MULTI_COMPONENT_ANTILANGM "EXT_MCAL_ANTILANGMUIR_T": [0.0, 0.0, 0.0], "EXT_MCAL_ANTILANGMUIR_TT": [0.0, 0.0, 0.0], "EXT_MCAL_ANTILANGMUIR_TTT": [0.0, 0.0, 0.0] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) - -CADET_BINDINGTEST("MOBILE_PHASE_MODULATOR", "EXT_MOBILE_PHASE_MODULATOR", (1,1,1), (1,1,0,1), (1.2, 1.5, 2.0, 0.5, 1.5, 1.8), (1.2, 1.5, 1.0, 2.0, 0.5, 1.5, 1.8), \ - R"json( "MPM_KA": [0.0, 1.14, 2.0], +CADET_BINDINGTEST("MOBILE_PHASE_MODULATOR", "EXT_MOBILE_PHASE_MODULATOR", (1, 1, 1), (1, 1, 0, 1), + (1.2, 1.5, 2.0, 0.5, 1.5, 1.8), (1.2, 1.5, 1.0, 2.0, 0.5, 1.5, 1.8), + R"json( "MPM_KA": [0.0, 1.14, 2.0], "MPM_KD": [0.0, 0.004, 0.008], "MPM_QMAX": [0.0, 4.88, 3.5], "MPM_GAMMA": [0.0, 0.5, -1.0], "MPM_BETA": [0.0, 1.5, 2.0], "MPM_LINEAR_THRESHOLD": 1e-10 - )json", \ - R"json( "MPM_KA": [0.0, 1.14, 1.0, 2.0], + )json", + R"json( "MPM_KA": [0.0, 1.14, 1.0, 2.0], "MPM_KD": [0.0, 0.004, 2.0, 0.008], "MPM_QMAX": [0.0, 4.88, 3.0, 3.5], "MPM_GAMMA": [0.0, 0.5, 0.0, -1.0], "MPM_BETA": [0.0, 1.5, 0.0, 2.0], "MPM_LINEAR_THRESHOLD": 1e-10 - )json", \ - R"json( "EXT_MPM_KA": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_MPM_KA": [0.0, 0.0, 0.0], "EXT_MPM_KA_T": [0.0, 1.14, 2.0], "EXT_MPM_KA_TT": [0.0, 0.0, 0.0], "EXT_MPM_KA_TTT": [0.0, 0.0, 0.0], @@ -274,8 +279,8 @@ CADET_BINDINGTEST("MOBILE_PHASE_MODULATOR", "EXT_MOBILE_PHASE_MODULATOR", (1,1,1 "EXT_MPM_BETA_TT": [0.0, 0.0, 0.0], "EXT_MPM_BETA_TTT": [0.0, 0.0, 0.0], "EXT_MPM_LINEAR_THRESHOLD": 1e-10 - )json", \ - R"json( "EXT_MPM_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_MPM_KA": [0.0, 0.0, 0.0, 0.0], "EXT_MPM_KA_T": [0.0, 1.14, 1.0, 2.0], "EXT_MPM_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_MPM_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -296,25 +301,26 @@ CADET_BINDINGTEST("MOBILE_PHASE_MODULATOR", "EXT_MOBILE_PHASE_MODULATOR", (1,1,1 "EXT_MPM_BETA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_MPM_BETA_TTT": [0.0, 0.0, 0.0, 0.0], "EXT_MPM_LINEAR_THRESHOLD": 1e-10 - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) - - CADET_BINDINGTEST_MULTI("MOBILE_PHASE_MODULATOR", "EXT_MOBILE_PHASE_MODULATOR", "_NEGATIVE", (1, 1, 1), (1, 1, 0, 1), (-1e-11, 1.5, 2.0, 0.5, 1.5, 1.8), (-1e-11, 1.5, 0.5, 2.0, 0.5, 1.5, 1.8), \ - R"json( "MPM_KA": [0.0, 1.14, 2.0], + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) + +CADET_BINDINGTEST_MULTI("MOBILE_PHASE_MODULATOR", "EXT_MOBILE_PHASE_MODULATOR", "_NEGATIVE", (1, 1, 1), (1, 1, 0, 1), + (-1e-11, 1.5, 2.0, 0.5, 1.5, 1.8), (-1e-11, 1.5, 0.5, 2.0, 0.5, 1.5, 1.8), + R"json( "MPM_KA": [0.0, 1.14, 2.0], "MPM_KD": [0.0, 0.004, 0.008], "MPM_QMAX": [0.0, 4.88, 3.5], "MPM_GAMMA": [0.0, 0.5, -1.0], "MPM_BETA": [0.0, 1.5, 2.0], "MPM_LINEAR_THRESHOLD": 1e-10 - )json", \ - R"json( "MPM_KA": [0.0, 1.14, 1.0, 2.0], + )json", + R"json( "MPM_KA": [0.0, 1.14, 1.0, 2.0], "MPM_KD": [0.0, 0.004, 2.0, 0.008], "MPM_QMAX": [0.0, 4.88, 3.0, 3.5], "MPM_GAMMA": [0.0, 0.5, 0.0, -1.0], "MPM_BETA": [0.0, 1.5, 0.0, 2.0], "MPM_LINEAR_THRESHOLD": 1e-10 - )json", \ - R"json( "EXT_MPM_KA": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_MPM_KA": [0.0, 0.0, 0.0], "EXT_MPM_KA_T": [0.0, 1.14, 2.0], "EXT_MPM_KA_TT": [0.0, 0.0, 0.0], "EXT_MPM_KA_TTT": [0.0, 0.0, 0.0], @@ -335,8 +341,8 @@ CADET_BINDINGTEST("MOBILE_PHASE_MODULATOR", "EXT_MOBILE_PHASE_MODULATOR", (1,1,1 "EXT_MPM_BETA_TT": [0.0, 0.0, 0.0], "EXT_MPM_BETA_TTT": [0.0, 0.0, 0.0], "EXT_MPM_LINEAR_THRESHOLD": 1e-10 - )json", \ - R"json( "EXT_MPM_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_MPM_KA": [0.0, 0.0, 0.0, 0.0], "EXT_MPM_KA_T": [0.0, 1.14, 1.0, 2.0], "EXT_MPM_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_MPM_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -357,26 +363,26 @@ CADET_BINDINGTEST("MOBILE_PHASE_MODULATOR", "EXT_MOBILE_PHASE_MODULATOR", (1,1,1 "EXT_MPM_BETA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_MPM_BETA_TTT": [0.0, 0.0, 0.0, 0.0], "EXT_MPM_LINEAR_THRESHOLD": 1e-10 - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) - + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) -CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_PHASE_MODULATOR", " all linear", (1,1,1), (1,1,0,1), (1.2, 1.5, 2.0, 0.5, 1.5, 1.8), (1.2, 1.5, 0.0, 2.0, 0.5, 1.5, 1.8), \ - R"json( "EMPM_KA": [0.0, 1.14, 2.0], +CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_PHASE_MODULATOR", " all linear", + (1, 1, 1), (1, 1, 0, 1), (1.2, 1.5, 2.0, 0.5, 1.5, 1.8), (1.2, 1.5, 0.0, 2.0, 0.5, 1.5, 1.8), + R"json( "EMPM_KA": [0.0, 1.14, 2.0], "EMPM_KD": [0.0, 0.004, 0.008], "EMPM_QMAX": [0.0, 4.88, 3.5], "EMPM_GAMMA": [0.0, 0.5, -1.0], "EMPM_BETA": [0.0, 1.5, 2.0], "EMPM_COMP_MODE": [1,1,1] - )json", \ - R"json( "EMPM_KA": [0.0, 1.14, 1.0, 2.0], + )json", + R"json( "EMPM_KA": [0.0, 1.14, 1.0, 2.0], "EMPM_KD": [0.0, 0.004, 2.0, 0.008], "EMPM_QMAX": [0.0, 4.88, 3.0, 3.5], "EMPM_GAMMA": [0.0, 0.5, 0.0, -1.0], "EMPM_BETA": [0.0, 1.5, 0.0, 2.0], "EMPM_COMP_MODE": [1,1,1,1] - )json", \ - R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0], "EXT_EMPM_KA_T": [0.0, 1.14, 2.0], "EXT_EMPM_KA_TT": [0.0, 0.0, 0.0], "EXT_EMPM_KA_TTT": [0.0, 0.0, 0.0], @@ -397,8 +403,8 @@ CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_ "EXT_EMPM_BETA_TT": [0.0, 0.0, 0.0], "EXT_EMPM_BETA_TTT": [0.0, 0.0, 0.0], "EMPM_COMP_MODE": [1,1,1] - )json", \ - R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0, 0.0], "EXT_EMPM_KA_T": [0.0, 1.14, 1.0, 2.0], "EXT_EMPM_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_EMPM_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -419,26 +425,26 @@ CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_ "EXT_EMPM_BETA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_EMPM_BETA_TTT": [0.0, 0.0, 0.0, 0.0], "EMPM_COMP_MODE": [1,1,1,1] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) - -CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_PHASE_MODULATOR", " all Langmuir", (1,1,1), (1,1,0,1), (1.2, 1.5, 2.0, 0.5, 1.5, 1.8), (1.2, 1.5, 0.0, 2.0, 0.5, 1.5, 1.8), \ - R"json( "EMPM_KA": [1.6, 1.14, 2.0], +CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_PHASE_MODULATOR", " all Langmuir", + (1, 1, 1), (1, 1, 0, 1), (1.2, 1.5, 2.0, 0.5, 1.5, 1.8), (1.2, 1.5, 0.0, 2.0, 0.5, 1.5, 1.8), + R"json( "EMPM_KA": [1.6, 1.14, 2.0], "EMPM_KD": [0.006, 0.004, 0.008], "EMPM_QMAX": [1.0, 4.88, 3.5], "EMPM_GAMMA": [1.5, 0.5, -1.0], "EMPM_BETA": [0.8, 1.5, 2.0], "EMPM_COMP_MODE": [2,2,2] - )json", \ - R"json( "EMPM_KA": [1.6, 1.14, 1.0, 2.0], + )json", + R"json( "EMPM_KA": [1.6, 1.14, 1.0, 2.0], "EMPM_KD": [0.006, 0.004, 2.0, 0.008], "EMPM_QMAX": [1.0, 4.88, 3.0, 3.5], "EMPM_GAMMA": [1.5, 0.5, 0.0, -1.0], "EMPM_BETA": [0.8, 1.5, 0.0, 2.0], "EMPM_COMP_MODE": [2,2,2,2] - )json", \ - R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0], "EXT_EMPM_KA_T": [1.6, 1.14, 2.0], "EXT_EMPM_KA_TT": [0.0, 0.0, 0.0], "EXT_EMPM_KA_TTT": [0.0, 0.0, 0.0], @@ -459,8 +465,8 @@ CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_ "EXT_EMPM_BETA_TT": [0.0, 0.0, 0.0], "EXT_EMPM_BETA_TTT": [0.0, 0.0, 0.0], "EMPM_COMP_MODE": [2,2,2] - )json", \ - R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0, 0.0], "EXT_EMPM_KA_T": [1.6, 1.14, 1.0, 2.0], "EXT_EMPM_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_EMPM_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -481,26 +487,26 @@ CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_ "EXT_EMPM_BETA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_EMPM_BETA_TTT": [0.0, 0.0, 0.0, 0.0], "EMPM_COMP_MODE": [2,2,2,2] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) - + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) -CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_PHASE_MODULATOR", " mixed", (1,1,1), (1,1,0,1), (1.2, 1.5, 2.0, 0.5, 1.5, 1.8), (1.2, 1.5, 0.0, 2.0, 0.5, 1.5, 1.8), \ - R"json( "EMPM_KA": [1.6, 1.14, 2.0], +CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_PHASE_MODULATOR", " mixed", (1, 1, 1), + (1, 1, 0, 1), (1.2, 1.5, 2.0, 0.5, 1.5, 1.8), (1.2, 1.5, 0.0, 2.0, 0.5, 1.5, 1.8), + R"json( "EMPM_KA": [1.6, 1.14, 2.0], "EMPM_KD": [0.006, 0.004, 0.008], "EMPM_QMAX": [1.0, 4.88, 3.5], "EMPM_GAMMA": [1.5, 0.5, -1.0], "EMPM_BETA": [0.8, 1.5, 2.0], "EMPM_COMP_MODE": [2,1,2] - )json", \ - R"json( "EMPM_KA": [1.6, 1.14, 1.0, 2.0], + )json", + R"json( "EMPM_KA": [1.6, 1.14, 1.0, 2.0], "EMPM_KD": [0.006, 0.004, 2.0, 0.008], "EMPM_QMAX": [1.0, 4.88, 3.0, 3.5], "EMPM_GAMMA": [1.5, 0.5, 0.0, -1.0], "EMPM_BETA": [0.8, 1.5, 0.0, 2.0], "EMPM_COMP_MODE": [2,1,2,2] - )json", \ - R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0], "EXT_EMPM_KA_T": [1.6, 1.14, 2.0], "EXT_EMPM_KA_TT": [0.0, 0.0, 0.0], "EXT_EMPM_KA_TTT": [0.0, 0.0, 0.0], @@ -521,8 +527,8 @@ CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_ "EXT_EMPM_BETA_TT": [0.0, 0.0, 0.0], "EXT_EMPM_BETA_TTT": [0.0, 0.0, 0.0], "EMPM_COMP_MODE": [2,1,2] - )json", \ - R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0, 0.0], "EXT_EMPM_KA_T": [1.6, 1.14, 1.0, 2.0], "EXT_EMPM_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_EMPM_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -543,26 +549,26 @@ CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_ "EXT_EMPM_BETA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_EMPM_BETA_TTT": [0.0, 0.0, 0.0, 0.0], "EMPM_COMP_MODE": [2,1,2,2] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) - -CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_PHASE_MODULATOR", " Langmuir modified", (1,1,1), (1,1,0,1), (1.2, 1.5, 2.0, 0.5, 1.5, 1.8), (1.2, 1.5, 0.0, 2.0, 0.5, 1.5, 1.8), \ - R"json( "EMPM_KA": [1.6, 1.14, 2.0], +CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_PHASE_MODULATOR", " Langmuir modified", + (1, 1, 1), (1, 1, 0, 1), (1.2, 1.5, 2.0, 0.5, 1.5, 1.8), (1.2, 1.5, 0.0, 2.0, 0.5, 1.5, 1.8), + R"json( "EMPM_KA": [1.6, 1.14, 2.0], "EMPM_KD": [0.006, 0.004, 0.008], "EMPM_QMAX": [1.0, 4.88, 3.5], "EMPM_GAMMA": [1.5, 0.5, -1.0], "EMPM_BETA": [0.8, 1.5, 2.0], "EMPM_COMP_MODE": [2,2,0] - )json", \ - R"json( "EMPM_KA": [1.6, 1.14, 1.0, 2.0], + )json", + R"json( "EMPM_KA": [1.6, 1.14, 1.0, 2.0], "EMPM_KD": [0.006, 0.004, 2.0, 0.008], "EMPM_QMAX": [1.0, 4.88, 3.0, 3.5], "EMPM_GAMMA": [1.5, 0.5, 0.0, -1.0], "EMPM_BETA": [0.8, 1.5, 0.0, 2.0], "EMPM_COMP_MODE": [2,2,2,0] - )json", \ - R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0], "EXT_EMPM_KA_T": [1.6, 1.14, 2.0], "EXT_EMPM_KA_TT": [0.0, 0.0, 0.0], "EXT_EMPM_KA_TTT": [0.0, 0.0, 0.0], @@ -583,8 +589,8 @@ CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_ "EXT_EMPM_BETA_TT": [0.0, 0.0, 0.0], "EXT_EMPM_BETA_TTT": [0.0, 0.0, 0.0], "EMPM_COMP_MODE": [2,2,0] - )json", \ - R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0, 0.0], "EXT_EMPM_KA_T": [1.6, 1.14, 1.0, 2.0], "EXT_EMPM_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_EMPM_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -605,26 +611,26 @@ CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_ "EXT_EMPM_BETA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_EMPM_BETA_TTT": [0.0, 0.0, 0.0, 0.0], "EMPM_COMP_MODE": [2,2,2,0] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) - + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) -CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_PHASE_MODULATOR", " mixed modified", (1,1,1), (1,1,0,1), (1.2, 1.5, 2.0, 0.5, 1.5, 1.8), (1.2, 1.5, 0.0, 2.0, 0.5, 1.5, 1.8), \ - R"json( "EMPM_KA": [1.6, 1.14, 2.0], +CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_PHASE_MODULATOR", " mixed modified", + (1, 1, 1), (1, 1, 0, 1), (1.2, 1.5, 2.0, 0.5, 1.5, 1.8), (1.2, 1.5, 0.0, 2.0, 0.5, 1.5, 1.8), + R"json( "EMPM_KA": [1.6, 1.14, 2.0], "EMPM_KD": [0.006, 0.004, 0.008], "EMPM_QMAX": [1.0, 4.88, 3.5], "EMPM_GAMMA": [1.5, 0.5, -1.0], "EMPM_BETA": [0.8, 1.5, 2.0], "EMPM_COMP_MODE": [2,1,0] - )json", \ - R"json( "EMPM_KA": [1.6, 1.14, 1.0, 2.0], + )json", + R"json( "EMPM_KA": [1.6, 1.14, 1.0, 2.0], "EMPM_KD": [0.006, 0.004, 2.0, 0.008], "EMPM_QMAX": [1.0, 4.88, 3.0, 3.5], "EMPM_GAMMA": [1.5, 0.5, 0.0, -1.0], "EMPM_BETA": [0.8, 1.5, 0.0, 2.0], "EMPM_COMP_MODE": [2,1,2,0] - )json", \ - R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0], "EXT_EMPM_KA_T": [1.6, 1.14, 2.0], "EXT_EMPM_KA_TT": [0.0, 0.0, 0.0], "EXT_EMPM_KA_TTT": [0.0, 0.0, 0.0], @@ -645,8 +651,8 @@ CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_ "EXT_EMPM_BETA_TT": [0.0, 0.0, 0.0], "EXT_EMPM_BETA_TTT": [0.0, 0.0, 0.0], "EMPM_COMP_MODE": [2,1,0] - )json", \ - R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_EMPM_KA": [0.0, 0.0, 0.0, 0.0], "EXT_EMPM_KA_T": [1.6, 1.14, 1.0, 2.0], "EXT_EMPM_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_EMPM_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -667,26 +673,26 @@ CADET_BINDINGTEST_MULTI("EXTENDED_MOBILE_PHASE_MODULATOR", "EXT_EXTENDED_MOBILE_ "EXT_EMPM_BETA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_EMPM_BETA_TTT": [0.0, 0.0, 0.0, 0.0], "EMPM_COMP_MODE": [2,1,2,0] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) - -CADET_BINDINGTEST("KUMAR_MULTI_COMPONENT_LANGMUIR", "EXT_KUMAR_MULTI_COMPONENT_LANGMUIR", (0,1,1), (0,1,0,1), (1.2, 1.5, 2.0, 0.75, 1.5), (1.2, 1.5, 0.0, 2.0, 0.75, 1.5), \ - R"json( "KMCL_KA": [0.0, 1.14, 2.0], +CADET_BINDINGTEST("KUMAR_MULTI_COMPONENT_LANGMUIR", "EXT_KUMAR_MULTI_COMPONENT_LANGMUIR", (0, 1, 1), (0, 1, 0, 1), + (1.2, 1.5, 2.0, 0.75, 1.5), (1.2, 1.5, 0.0, 2.0, 0.75, 1.5), + R"json( "KMCL_KA": [0.0, 1.14, 2.0], "KMCL_KD": [0.0, 0.004, 0.008], "KMCL_QMAX": [0.0, 4.88, 3.5], "KMCL_TEMP": 0.5, "KMCL_NU": [0.0, 1.2, 2.2], "KMCL_KACT": [0.0, 1.5, 2.0] - )json", \ - R"json( "KMCL_KA": [0.0, 1.14, 1.0, 2.0], + )json", + R"json( "KMCL_KA": [0.0, 1.14, 1.0, 2.0], "KMCL_KD": [0.0, 0.004, 2.0, 0.008], "KMCL_QMAX": [0.0, 4.88, 3.0, 3.5], "KMCL_TEMP": 0.5, "KMCL_NU": [0.0, 1.2, 0.0, 2.2], "KMCL_KACT": [0.0, 1.5, 0.0, 2.0] - )json", \ - R"json( "EXT_KMCL_KA": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_KMCL_KA": [0.0, 0.0, 0.0], "EXT_KMCL_KA_T": [0.0, 1.14, 2.0], "EXT_KMCL_KA_TT": [0.0, 0.0, 0.0], "EXT_KMCL_KA_TTT": [0.0, 0.0, 0.0], @@ -710,8 +716,8 @@ CADET_BINDINGTEST("KUMAR_MULTI_COMPONENT_LANGMUIR", "EXT_KUMAR_MULTI_COMPONENT_L "EXT_KMCL_KACT_T": [0.0, 1.5, 2.0], "EXT_KMCL_KACT_TT": [0.0, 0.0, 0.0], "EXT_KMCL_KACT_TTT": [0.0, 0.0, 0.0] - )json", \ - R"json( "EXT_KMCL_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_KMCL_KA": [0.0, 0.0, 0.0, 0.0], "EXT_KMCL_KA_T": [0.0, 1.14, 1.0, 2.0], "EXT_KMCL_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_KMCL_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -735,18 +741,18 @@ CADET_BINDINGTEST("KUMAR_MULTI_COMPONENT_LANGMUIR", "EXT_KUMAR_MULTI_COMPONENT_L "EXT_KMCL_KACT_T": [0.0, 1.5, 0.0, 2.0], "EXT_KMCL_KACT_TT": [0.0, 0.0, 0.0, 0.0], "EXT_KMCL_KACT_TTT": [0.0, 0.0, 0.0, 0.0] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_USED, CADET_DONT_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_USED, CADET_DONT_COMPARE_BINDING_VS_NONBINDING) // Note that we cannot enable binding vs non-binding test since the first component has to be non-binding, // the liquid phase component matters in the Jacobian is not a column to ignore. - -CADET_BINDINGTEST_ALLBINDING("MULTI_COMPONENT_BILANGMUIR", "EXT_MULTI_COMPONENT_BILANGMUIR", (2,2), (1.0, 2.0, 0.0, 0.0, 0.0, 0.0), \ - R"json( "MCBL_KA": [1.14, 2.0, 2.28, 4.0], +CADET_BINDINGTEST_ALLBINDING("MULTI_COMPONENT_BILANGMUIR", "EXT_MULTI_COMPONENT_BILANGMUIR", (2, 2), + (1.0, 2.0, 0.0, 0.0, 0.0, 0.0), + R"json( "MCBL_KA": [1.14, 2.0, 2.28, 4.0], "MCBL_KD": [0.004, 0.008, 0.002, 0.003], "MCBL_QMAX": [4.88, 3.5, 3.88, 2.5] - )json", \ - R"json( "EXT_MCBL_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_MCBL_KA": [0.0, 0.0, 0.0, 0.0], "EXT_MCBL_KA_T": [1.14, 2.0, 2.28, 4.0], "EXT_MCBL_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_MCBL_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -758,14 +764,15 @@ CADET_BINDINGTEST_ALLBINDING("MULTI_COMPONENT_BILANGMUIR", "EXT_MULTI_COMPONENT_ "EXT_MCBL_QMAX_T": [4.88, 3.5, 3.88, 2.5], "EXT_MCBL_QMAX_TT": [0.0, 0.0, 0.0, 0.0], "EXT_MCBL_QMAX_TTT": [0.0, 0.0, 0.0, 0.0] - )json", \ - 1e-10, 1e-10) -CADET_BINDINGTEST_ALLBINDING("MULTI_COMPONENT_BILANGMUIR_LDF", "EXT_MULTI_COMPONENT_BILANGMUIR_LDF", (2, 2), (1.0, 2.0, 0.0, 0.0, 0.0, 0.0), \ - R"json( "MCBLLDF_KEQ": [1.14, 2.0, 2.28, 4.0], + )json", + 1e-10, 1e-10) +CADET_BINDINGTEST_ALLBINDING("MULTI_COMPONENT_BILANGMUIR_LDF", "EXT_MULTI_COMPONENT_BILANGMUIR_LDF", (2, 2), + (1.0, 2.0, 0.0, 0.0, 0.0, 0.0), + R"json( "MCBLLDF_KEQ": [1.14, 2.0, 2.28, 4.0], "MCBLLDF_KKIN": [0.004, 0.008, 0.002, 0.003], "MCBLLDF_QMAX": [4.88, 3.5, 3.88, 2.5] - )json", \ - R"json( "EXT_MCBLLDF_KEQ": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_MCBLLDF_KEQ": [0.0, 0.0, 0.0, 0.0], "EXT_MCBLLDF_KEQ_T": [1.14, 2.0, 2.28, 4.0], "EXT_MCBLLDF_KEQ_TT": [0.0, 0.0, 0.0, 0.0], "EXT_MCBLLDF_KEQ_TTT": [0.0, 0.0, 0.0, 0.0], @@ -777,27 +784,28 @@ CADET_BINDINGTEST_ALLBINDING("MULTI_COMPONENT_BILANGMUIR_LDF", "EXT_MULTI_COMPON "EXT_MCBLLDF_QMAX_T": [4.88, 3.5, 3.88, 2.5], "EXT_MCBLLDF_QMAX_TT": [0.0, 0.0, 0.0, 0.0], "EXT_MCBLLDF_QMAX_TTT": [0.0, 0.0, 0.0, 0.0] - )json", \ - 1e-10, 1e-10) + )json", + 1e-10, 1e-10) -CADET_BINDINGTEST("STERIC_MASS_ACTION", "EXT_STERIC_MASS_ACTION", (1,1,1), (1,1,0,1), (1.2, 2.0, 1.5, 80.0, 3.5, 2.7), (1.2, 2.0, 3.0, 1.5, 80.0, 3.5, 2.7), \ - R"json( "SMA_KA": [0.0, 3.55, 1.59], +CADET_BINDINGTEST("STERIC_MASS_ACTION", "EXT_STERIC_MASS_ACTION", (1, 1, 1), (1, 1, 0, 1), + (1.2, 2.0, 1.5, 80.0, 3.5, 2.7), (1.2, 2.0, 3.0, 1.5, 80.0, 3.5, 2.7), + R"json( "SMA_KA": [0.0, 3.55, 1.59], "SMA_KD": [0.0, 10.0, 10.0], "SMA_NU": [1.5, 2.0, 1.5], "SMA_SIGMA": [0.0, 11.83, 10.6], "SMA_LAMBDA": 100.0, "SMA_REFC0": 2.0, "SMA_REFQ": 1.1 - )json", \ - R"json( "SMA_KA": [0.0, 3.55, 7.7, 1.59], + )json", + R"json( "SMA_KA": [0.0, 3.55, 7.7, 1.59], "SMA_KD": [0.0, 10.0, 10.0, 10.0], "SMA_NU": [1.5, 2.0, 3.7, 1.5], "SMA_SIGMA": [0.0, 11.83, 10.0, 10.6], "SMA_LAMBDA": 100.0, "SMA_REFC0": 2.0, "SMA_REFQ": 1.1 - )json", \ - R"json( "EXT_SMA_KA": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_SMA_KA": [0.0, 0.0, 0.0], "EXT_SMA_KA_T": [0.0, 3.55, 1.59], "EXT_SMA_KA_TT": [0.0, 0.0, 0.0], "EXT_SMA_KA_TTT": [0.0, 0.0, 0.0], @@ -819,8 +827,8 @@ CADET_BINDINGTEST("STERIC_MASS_ACTION", "EXT_STERIC_MASS_ACTION", (1,1,1), (1,1, "EXT_SMA_LAMBDA_TTT": 0.0, "EXT_SMA_REFC0": 2.0, "EXT_SMA_REFQ": 1.1 - )json", \ - R"json( "EXT_SMA_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_SMA_KA": [0.0, 0.0, 0.0, 0.0], "EXT_SMA_KA_T": [0.0, 3.55, 7.7, 1.59], "EXT_SMA_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_SMA_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -842,18 +850,18 @@ CADET_BINDINGTEST("STERIC_MASS_ACTION", "EXT_STERIC_MASS_ACTION", (1,1,1), (1,1, "EXT_SMA_LAMBDA_TTT": 0.0, "EXT_SMA_REFC0": 2.0, "EXT_SMA_REFQ": 1.1 - )json", \ - 1e-10, 1e-8, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 1e-8, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) - -CADET_BINDINGTEST_ALLBINDING("BI_STERIC_MASS_ACTION", "EXT_BI_STERIC_MASS_ACTION", (2,2,2), (1.2, 2.0, 1.5, 80.0, 80.0, 3.5, 3.5, 2.7, 2.7), \ - R"json( "BISMA_KA": [0.0, 3.55, 1.59, 0.0, 3.55, 1.59], +CADET_BINDINGTEST_ALLBINDING("BI_STERIC_MASS_ACTION", "EXT_BI_STERIC_MASS_ACTION", (2, 2, 2), + (1.2, 2.0, 1.5, 80.0, 80.0, 3.5, 3.5, 2.7, 2.7), + R"json( "BISMA_KA": [0.0, 3.55, 1.59, 0.0, 3.55, 1.59], "BISMA_KD": [0.0, 10.0, 10.0, 0.0, 10.0, 10.0], "BISMA_NU": [1.2, 2.0, 1.5, 1.5, 2.0, 1.5], "BISMA_SIGMA": [0.0, 11.83, 10.6, 0.0, 11.83, 10.6], "BISMA_LAMBDA": [100.0, 100.0] - )json", \ - R"json( "EXT_BISMA_KA": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_BISMA_KA": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "EXT_BISMA_KA_T": [0.0, 3.55, 1.59, 0.0, 3.55, 1.59], "EXT_BISMA_KA_TT": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "EXT_BISMA_KA_TTT": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], @@ -873,26 +881,26 @@ CADET_BINDINGTEST_ALLBINDING("BI_STERIC_MASS_ACTION", "EXT_BI_STERIC_MASS_ACTION "EXT_BISMA_LAMBDA_T": [100.0, 100.0], "EXT_BISMA_LAMBDA_TT": [0.0, 0.0], "EXT_BISMA_LAMBDA_TTT": [0.0, 0.0] - )json", \ - 1e-10, 1e-8) - + )json", + 1e-10, 1e-8) -CADET_BINDINGTEST("SELF_ASSOCIATION", "EXT_SELF_ASSOCIATION", (1,1,1), (1,1,0,1), (1.2, 2.0, 1.5, 80.0, 3.5, 2.7), (1.2, 2.0, 3.0, 1.5, 80.0, 3.5, 2.7), \ - R"json( "SAI_KA1": [0.0, 3.55, 1.59], +CADET_BINDINGTEST("SELF_ASSOCIATION", "EXT_SELF_ASSOCIATION", (1, 1, 1), (1, 1, 0, 1), (1.2, 2.0, 1.5, 80.0, 3.5, 2.7), + (1.2, 2.0, 3.0, 1.5, 80.0, 3.5, 2.7), + R"json( "SAI_KA1": [0.0, 3.55, 1.59], "SAI_KA2": [0.0, 1.5, 2.5], "SAI_KD": [0.0, 10.0, 10.0], "SAI_NU": [1.5, 2.0, 1.5], "SAI_SIGMA": [0.0, 11.83, 10.6], "SAI_LAMBDA": 100.0 - )json", \ - R"json( "SAI_KA1": [0.0, 3.55, 7.7, 1.59], + )json", + R"json( "SAI_KA1": [0.0, 3.55, 7.7, 1.59], "SAI_KA2": [0.0, 1.5, 2.0, 2.5], "SAI_KD": [0.0, 10.0, 10.0, 10.0], "SAI_NU": [1.5, 2.0, 3.7, 1.5], "SAI_SIGMA": [0.0, 11.83, 10.0, 10.6], "SAI_LAMBDA": 100.0 - )json", \ - R"json( "EXT_SAI_KA1": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_SAI_KA1": [0.0, 0.0, 0.0], "EXT_SAI_KA1_T": [0.0, 3.55, 1.59], "EXT_SAI_KA1_TT": [0.0, 0.0, 0.0], "EXT_SAI_KA1_TTT": [0.0, 0.0, 0.0], @@ -916,8 +924,8 @@ CADET_BINDINGTEST("SELF_ASSOCIATION", "EXT_SELF_ASSOCIATION", (1,1,1), (1,1,0,1) "EXT_SAI_LAMBDA_T": 100.0, "EXT_SAI_LAMBDA_TT": 0.0, "EXT_SAI_LAMBDA_TTT": 0.0 - )json", \ - R"json( "EXT_SAI_KA1": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_SAI_KA1": [0.0, 0.0, 0.0, 0.0], "EXT_SAI_KA1_T": [0.0, 3.55, 7.7, 1.59], "EXT_SAI_KA1_TT": [0.0, 0.0, 0.0, 0.0], "EXT_SAI_KA1_TTT": [0.0, 0.0, 0.0, 0.0], @@ -941,18 +949,17 @@ CADET_BINDINGTEST("SELF_ASSOCIATION", "EXT_SELF_ASSOCIATION", (1,1,1), (1,1,0,1) "EXT_SAI_LAMBDA_T": 100.0, "EXT_SAI_LAMBDA_TT": 0.0, "EXT_SAI_LAMBDA_TTT": 0.0 - )json", \ - 1e-10, 1e-4, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) - + )json", + 1e-10, 1e-4, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) -CADET_BINDINGTEST("SASKA", "EXT_SASKA", (1,1), (1,0,1), (1.0, 2.0, 0.0, 0.0), (1.0, 3.0, 2.0, 0.0, 0.0), \ - R"json( "SASKA_H": [1.5, 2.0], +CADET_BINDINGTEST("SASKA", "EXT_SASKA", (1, 1), (1, 0, 1), (1.0, 2.0, 0.0, 0.0), (1.0, 3.0, 2.0, 0.0, 0.0), + R"json( "SASKA_H": [1.5, 2.0], "SASKA_K": [0.1, 0.3, 0.4, 0.5] - )json", \ - R"json( "SASKA_H": [1.5, 3.0, 2.0], + )json", + R"json( "SASKA_H": [1.5, 3.0, 2.0], "SASKA_K": [0.1, 0.0, 0.3, 0.0, 0.0, 0.0, 0.4, 0.0, 0.5] - )json", \ - R"json( "EXT_SASKA_H": [0.0, 0.0], + )json", + R"json( "EXT_SASKA_H": [0.0, 0.0], "EXT_SASKA_H_T": [1.5, 2.0], "EXT_SASKA_H_TT": [0.0, 0.0], "EXT_SASKA_H_TTT": [0.0, 0.0], @@ -960,8 +967,8 @@ CADET_BINDINGTEST("SASKA", "EXT_SASKA", (1,1), (1,0,1), (1.0, 2.0, 0.0, 0.0), (1 "EXT_SASKA_K_T": [0.1, 0.3, 0.4, 0.5], "EXT_SASKA_K_TT": [0.0, 0.0, 0.0, 0.0], "EXT_SASKA_K_TTT": [0.0, 0.0, 0.0, 0.0] - )json", \ - R"json( "EXT_SASKA_H": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_SASKA_H": [0.0, 0.0, 0.0], "EXT_SASKA_H_T": [1.5, 3.0, 2.0], "EXT_SASKA_H_TT": [0.0, 0.0, 0.0], "EXT_SASKA_H_TTT": [0.0, 0.0, 0.0], @@ -969,24 +976,24 @@ CADET_BINDINGTEST("SASKA", "EXT_SASKA", (1,1), (1,0,1), (1.0, 2.0, 0.0, 0.0), (1 "EXT_SASKA_K_T": [0.1, 0.0, 0.3, 0.0, 0.0, 0.0, 0.4, 0.0, 0.5], "EXT_SASKA_K_TT": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "EXT_SASKA_K_TTT": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_USED, CADET_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_USED, CADET_COMPARE_BINDING_VS_NONBINDING) - -CADET_BINDINGTEST("MULTI_COMPONENT_SPREADING", "EXT_MULTI_COMPONENT_SPREADING", (2,2), (2,0,2), (1.2, 1.5, 0.1, 0.2, 0.3, 0.4), (1.2, 0.5, 1.5, 0.1, 0.2, 0.3, 0.4), \ - R"json( "MCSPR_KA": [1.14, 2.0, 1.5, 1.9], +CADET_BINDINGTEST("MULTI_COMPONENT_SPREADING", "EXT_MULTI_COMPONENT_SPREADING", (2, 2), (2, 0, 2), + (1.2, 1.5, 0.1, 0.2, 0.3, 0.4), (1.2, 0.5, 1.5, 0.1, 0.2, 0.3, 0.4), + R"json( "MCSPR_KA": [1.14, 2.0, 1.5, 1.9], "MCSPR_KD": [0.004, 0.008, 0.006, 0.002], "MCSPR_QMAX": [4.88, 3.5, 4.5, 3.7], "MCSPR_K12": [0.5, 1.1, 0.7, 1.2], "MCSPR_K21": [0.6, 1.5, 2.0, 0.8] - )json", \ - R"json( "MCSPR_KA": [1.14, 0.0, 2.0, 1.5, 0.0, 1.9], + )json", + R"json( "MCSPR_KA": [1.14, 0.0, 2.0, 1.5, 0.0, 1.9], "MCSPR_KD": [0.004, 0.0, 0.008, 0.006, 0.0, 0.002], "MCSPR_QMAX": [4.88, 0.0, 3.5, 4.5, 0.0, 3.7], "MCSPR_K12": [0.5, 0.0, 1.1, 0.7, 0.0, 1.2], "MCSPR_K21": [0.6, 0.0, 1.5, 2.0, 0.0, 0.8] - )json", \ - R"json( "EXT_MCSPR_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_MCSPR_KA": [0.0, 0.0, 0.0, 0.0], "EXT_MCSPR_KA_T": [1.14, 2.0, 1.5, 1.9], "EXT_MCSPR_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_MCSPR_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -1006,8 +1013,8 @@ CADET_BINDINGTEST("MULTI_COMPONENT_SPREADING", "EXT_MULTI_COMPONENT_SPREADING", "EXT_MCSPR_K21_T": [0.6, 1.5, 2.0, 0.8], "EXT_MCSPR_K21_TT": [0.0, 0.0, 0.0, 0.0], "EXT_MCSPR_K21_TTT": [0.0, 0.0, 0.0, 0.0] - )json", \ - R"json( "EXT_MCSPR_KA": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_MCSPR_KA": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "EXT_MCSPR_KA_T": [1.14, 0.0, 2.0, 1.5, 0.0, 1.9], "EXT_MCSPR_KA_TT": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "EXT_MCSPR_KA_TTT": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], @@ -1027,27 +1034,27 @@ CADET_BINDINGTEST("MULTI_COMPONENT_SPREADING", "EXT_MULTI_COMPONENT_SPREADING", "EXT_MCSPR_K21_T": [0.6, 0.0, 1.5, 2.0, 0.0, 0.8], "EXT_MCSPR_K21_TT": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "EXT_MCSPR_K21_TTT": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) - + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) // @todo Find a better starting point for consistent initialization -CADET_BINDINGTEST("MULTISTATE_STERIC_MASS_ACTION", "EXT_MULTISTATE_STERIC_MASS_ACTION", (1,2,1), (1,2,0,1), (1.2, 2.0, 1.5, 80.0, 2.5, 2.7, 1.8), (1.2, 2.0, 3.0, 1.5, 80.0, 2.5, 2.7, 1.8), \ - R"json( "MSSMA_KA": [0.0, 3.55, 1.59, 4.0], +CADET_BINDINGTEST("MULTISTATE_STERIC_MASS_ACTION", "EXT_MULTISTATE_STERIC_MASS_ACTION", (1, 2, 1), (1, 2, 0, 1), + (1.2, 2.0, 1.5, 80.0, 2.5, 2.7, 1.8), (1.2, 2.0, 3.0, 1.5, 80.0, 2.5, 2.7, 1.8), + R"json( "MSSMA_KA": [0.0, 3.55, 1.59, 4.0], "MSSMA_KD": [0.0, 10.0, 10.0, 10.0], "MSSMA_NU": [1.2, 1.5, 2.0, 1.9], "MSSMA_SIGMA": [0.0, 11.83, 10.6, 9.8], "MSSMA_RATES": [0.0, 0.9, 0.8, 1.2, 1.1, 1.4], "MSSMA_LAMBDA": 100.0 - )json", \ - R"json( "MSSMA_KA": [0.0, 3.55, 1.59, 4.0], + )json", + R"json( "MSSMA_KA": [0.0, 3.55, 1.59, 4.0], "MSSMA_KD": [0.0, 10.0, 10.0, 10.0], "MSSMA_NU": [1.2, 1.5, 2.0, 1.9], "MSSMA_SIGMA": [0.0, 11.83, 10.6, 9.8], "MSSMA_RATES": [0.0, 0.9, 0.8, 1.2, 1.1, 1.4], "MSSMA_LAMBDA": 100.0 - )json", \ - R"json( "EXT_MSSMA_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_MSSMA_KA": [0.0, 0.0, 0.0, 0.0], "EXT_MSSMA_KA_T": [0.0, 3.55, 1.59, 4.0], "EXT_MSSMA_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_MSSMA_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -1071,8 +1078,8 @@ CADET_BINDINGTEST("MULTISTATE_STERIC_MASS_ACTION", "EXT_MULTISTATE_STERIC_MASS_A "EXT_MSSMA_LAMBDA_T": 100.0, "EXT_MSSMA_LAMBDA_TT": 0.0, "EXT_MSSMA_LAMBDA_TTT": 0.0 - )json", \ - R"json( "EXT_MSSMA_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_MSSMA_KA": [0.0, 0.0, 0.0, 0.0], "EXT_MSSMA_KA_T": [0.0, 3.55, 1.59, 4.0], "EXT_MSSMA_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_MSSMA_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -1096,12 +1103,12 @@ CADET_BINDINGTEST("MULTISTATE_STERIC_MASS_ACTION", "EXT_MULTISTATE_STERIC_MASS_A "EXT_MSSMA_LAMBDA_T": 100.0, "EXT_MSSMA_LAMBDA_TT": 0.0, "EXT_MSSMA_LAMBDA_TTT": 0.0 - )json", \ - 1e-10, 5e-2, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 5e-2, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) - -CADET_BINDINGTEST_SINGLE("SIMPLIFIED_MULTISTATE_STERIC_MASS_ACTION", (1,2,1), (1,2,0,1), (1.2, 2.0, 1.5, 80.0, 2.5, 2.7, 1.8), (1.2, 2.0, 3.0, 1.5, 80.0, 2.5, 2.7, 1.8), \ - R"json( "SMSSMA_KA": [0.0, 3.55, 1.59, 4.0], +CADET_BINDINGTEST_SINGLE("SIMPLIFIED_MULTISTATE_STERIC_MASS_ACTION", (1, 2, 1), (1, 2, 0, 1), + (1.2, 2.0, 1.5, 80.0, 2.5, 2.7, 1.8), (1.2, 2.0, 3.0, 1.5, 80.0, 2.5, 2.7, 1.8), + R"json( "SMSSMA_KA": [0.0, 3.55, 1.59, 4.0], "SMSSMA_KD": [0.0, 10.0, 10.0, 10.0], "SMSSMA_NU_MIN": [0.0, 1.5, 1.9], "SMSSMA_NU_MAX": [0.0, 2.0, 2.1], @@ -1117,8 +1124,8 @@ CADET_BINDINGTEST_SINGLE("SIMPLIFIED_MULTISTATE_STERIC_MASS_ACTION", (1,2,1), (1 "SMSSMA_KWS_QUAD": [0.0, -0.1, -0.1], "SMSSMA_LAMBDA": 100.0, "SMSSMA_NU_SALT": 1.2 - )json", \ - R"json( "SMSSMA_KA": [0.0, 3.55, 1.59, 4.0], + )json", + R"json( "SMSSMA_KA": [0.0, 3.55, 1.59, 4.0], "SMSSMA_KD": [0.0, 10.0, 10.0, 10.0], "SMSSMA_NU_MIN": [0.0, 1.5, 0.0, 1.9], "SMSSMA_NU_MAX": [0.0, 2.0, 0.0, 2.1], @@ -1134,11 +1141,12 @@ CADET_BINDINGTEST_SINGLE("SIMPLIFIED_MULTISTATE_STERIC_MASS_ACTION", (1,2,1), (1 "SMSSMA_KWS_QUAD": [0.0, -0.1, 0.0, -0.1], "SMSSMA_LAMBDA": 100.0, "SMSSMA_NU_SALT": 1.2 - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) -CADET_BINDINGTEST("GENERALIZED_ION_EXCHANGE", "EXT_GENERALIZED_ION_EXCHANGE", (1,0,1,1), (1,0,1,0,1), (1.2, 0.5, 2.0, 1.5, 80.0, 3.5, 2.7), (1.2, 0.5, 2.0, 3.0, 1.5, 80.0, 3.5, 2.7), \ - R"json( "GIEX_KA": [0.0, 0.0, 3.55, 1.59], +CADET_BINDINGTEST("GENERALIZED_ION_EXCHANGE", "EXT_GENERALIZED_ION_EXCHANGE", (1, 0, 1, 1), (1, 0, 1, 0, 1), + (1.2, 0.5, 2.0, 1.5, 80.0, 3.5, 2.7), (1.2, 0.5, 2.0, 3.0, 1.5, 80.0, 3.5, 2.7), + R"json( "GIEX_KA": [0.0, 0.0, 3.55, 1.59], "GIEX_KA_LIN": [0.0, 0.0, 0.8, 0.9], "GIEX_KA_QUAD": [0.0, 0.0, 0.5, 0.6], "GIEX_KA_SALT": [0.0, 0.0, 1.2, 0.8], @@ -1157,8 +1165,8 @@ CADET_BINDINGTEST("GENERALIZED_ION_EXCHANGE", "EXT_GENERALIZED_ION_EXCHANGE", (1 "GIEX_REFQ": 50.0, "GIEX_PHREFC0": 2.5, "GIEX_PHREFQ": 45.0 - )json", \ - R"json( "GIEX_KA": [0.0, 0.0, 3.55, 7.7, 1.59], + )json", + R"json( "GIEX_KA": [0.0, 0.0, 3.55, 7.7, 1.59], "GIEX_KA_LIN": [0.0, 0.0, 0.8, 1.2, 0.9], "GIEX_KA_QUAD": [0.0, 0.0, 0.5, 3.2, 0.6], "GIEX_KA_SALT": [0.0, 0.0, 1.2, 4.1, 0.8], @@ -1177,8 +1185,8 @@ CADET_BINDINGTEST("GENERALIZED_ION_EXCHANGE", "EXT_GENERALIZED_ION_EXCHANGE", (1 "GIEX_REFQ": 50.0, "GIEX_PHREFC0": 2.5, "GIEX_PHREFQ": 45.0 - )json", \ - R"json( "EXT_GIEX_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_GIEX_KA": [0.0, 0.0, 0.0, 0.0], "EXT_GIEX_KA_T": [0.0, 0.0, 3.55, 1.59], "EXT_GIEX_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_GIEX_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -1242,8 +1250,8 @@ CADET_BINDINGTEST("GENERALIZED_ION_EXCHANGE", "EXT_GENERALIZED_ION_EXCHANGE", (1 "EXT_GIEX_REFQ": 50.0, "EXT_GIEX_PHREFC0": 2.5, "EXT_GIEX_PHREFQ": 45.0 - )json", \ - R"json( "EXT_GIEX_KA": [0.0, 0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_GIEX_KA": [0.0, 0.0, 0.0, 0.0, 0.0], "EXT_GIEX_KA_T": [0.0, 0.0, 3.55, 7.7, 1.59], "EXT_GIEX_KA_TT": [0.0, 0.0, 0.0, 0.0, 0.0], "EXT_GIEX_KA_TTT": [0.0, 0.0, 0.0, 0.0, 0.0], @@ -1307,14 +1315,15 @@ CADET_BINDINGTEST("GENERALIZED_ION_EXCHANGE", "EXT_GENERALIZED_ION_EXCHANGE", (1 "EXT_GIEX_REFQ": 50.0, "EXT_GIEX_PHREFC0": 2.5, "EXT_GIEX_PHREFQ": 45.0 - )json", \ - 1e-10, 1e-8, CADET_NONBINDING_LIQUIDPHASE_COMP_USED, CADET_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 1e-8, CADET_NONBINDING_LIQUIDPHASE_COMP_USED, CADET_COMPARE_BINDING_VS_NONBINDING) - TEST_CASE("MULTI_COMPONENT_COLLOIDAL binding model analytic Jacobian vs AD with PH single protein", "[Jacobian],[AD],[BindingModel],[MULTI_COMPONENT_COLLOIDAL]") - { - const unsigned int nBound[] = { 0, 0, 1 }; - const double state[] = { 0.9, 1.1, 1.5e-2, 3.2e-2 }; - char const* const config = R"json({ +TEST_CASE("MULTI_COMPONENT_COLLOIDAL binding model analytic Jacobian vs AD with PH single protein", + "[Jacobian],[AD],[BindingModel],[MULTI_COMPONENT_COLLOIDAL]") +{ + const unsigned int nBound[] = {0, 0, 1}; + const double state[] = {0.9, 1.1, 1.5e-2, 3.2e-2}; + char const* const config = R"json({ "COL_PHI": 49232983.6522396, "COL_KAPPA_EXP": 1.8, "COL_KAPPA_FACT": 1e7, @@ -1335,22 +1344,23 @@ CADET_BINDINGTEST("GENERALIZED_ION_EXCHANGE", "EXT_GENERALIZED_ION_EXCHANGE", (1 "COL_LINEAR_THRESHOLD": 1e-12, "COL_USE_PH": 1 })json"; - for (int bindMode = 0; bindMode < 2; ++bindMode) - { - const bool isKinetic = bindMode; - SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) - { - cadet::test::binding::testJacobianAD("MULTI_COMPONENT_COLLOIDAL", sizeof(nBound) / sizeof(unsigned int), nBound, isKinetic, config, state); - } - } - } - - - TEST_CASE("MULTI_COMPONENT_COLLOIDAL binding model analytic Jacobian vs AD with PH multi protein", "[Jacobian],[AD],[BindingModel],[MULTI_COMPONENT_COLLOIDAL]") + for (int bindMode = 0; bindMode < 2; ++bindMode) { - const unsigned int nBound[] = { 0, 0, 1, 1, 1}; - const double state[] = { 0.9, 1.1, 1.5e-2, 3.2e-3, 1.5e-5, 3.2e-5, 1.5e-5, 3.2e-5 }; - char const* const config = R"json({ + const bool isKinetic = bindMode; + SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) + { + cadet::test::binding::testJacobianAD("MULTI_COMPONENT_COLLOIDAL", sizeof(nBound) / sizeof(unsigned int), + nBound, isKinetic, config, state); + } + } +} + +TEST_CASE("MULTI_COMPONENT_COLLOIDAL binding model analytic Jacobian vs AD with PH multi protein", + "[Jacobian],[AD],[BindingModel],[MULTI_COMPONENT_COLLOIDAL]") +{ + const unsigned int nBound[] = {0, 0, 1, 1, 1}; + const double state[] = {0.9, 1.1, 1.5e-2, 3.2e-3, 1.5e-5, 3.2e-5, 1.5e-5, 3.2e-5}; + char const* const config = R"json({ "COL_PHI": 49232983.6522396, "COL_KAPPA_EXP": 1.8, "COL_KAPPA_FACT": 1e7, @@ -1371,17 +1381,19 @@ CADET_BINDINGTEST("GENERALIZED_ION_EXCHANGE", "EXT_GENERALIZED_ION_EXCHANGE", (1 "COL_LINEAR_THRESHOLD": 1e-12, "COL_USE_PH": 1 })json"; - for (int bindMode = 0; bindMode < 2; ++bindMode) + for (int bindMode = 0; bindMode < 2; ++bindMode) + { + const bool isKinetic = bindMode; + SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) { - const bool isKinetic = bindMode; - SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) - { - cadet::test::binding::testJacobianAD("MULTI_COMPONENT_COLLOIDAL", sizeof(nBound) / sizeof(unsigned int), nBound, isKinetic, config, state); - } + cadet::test::binding::testJacobianAD("MULTI_COMPONENT_COLLOIDAL", sizeof(nBound) / sizeof(unsigned int), + nBound, isKinetic, config, state); } } +} -TEST_CASE("MULTI_COMPONENT_COLLOIDAL binding model analytic Jacobian vs AD without PH", "[Jacobian],[AD],[BindingModel],[MULTI_COMPONENT_COLLOIDAL]") +TEST_CASE("MULTI_COMPONENT_COLLOIDAL binding model analytic Jacobian vs AD without PH", + "[Jacobian],[AD],[BindingModel],[MULTI_COMPONENT_COLLOIDAL]") { const unsigned int nBound[] = {0, 1, 1, 1}; const double state[] = {0.9, 1.1e-1, 3.2e-3, 1e-2, 2e-3, 1e-4, 1e-3}; @@ -1411,12 +1423,14 @@ TEST_CASE("MULTI_COMPONENT_COLLOIDAL binding model analytic Jacobian vs AD witho const bool isKinetic = bindMode; SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) { - cadet::test::binding::testJacobianAD("MULTI_COMPONENT_COLLOIDAL", sizeof(nBound) / sizeof(unsigned int), nBound, isKinetic, config, state); + cadet::test::binding::testJacobianAD("MULTI_COMPONENT_COLLOIDAL", sizeof(nBound) / sizeof(unsigned int), + nBound, isKinetic, config, state); } } } -TEST_CASE("MULTI_COMPONENT_COLLOIDAL binding model analytic Jacobian vs AD with PH low conc", "[Jacobian],[AD],[BindingModel],[MULTI_COMPONENT_COLLOIDAL]") +TEST_CASE("MULTI_COMPONENT_COLLOIDAL binding model analytic Jacobian vs AD with PH low conc", + "[Jacobian],[AD],[BindingModel],[MULTI_COMPONENT_COLLOIDAL]") { const unsigned int nBound[] = {0, 0, 1, 1, 1}; const double state[] = {0.9, 1.1, 1.5, 2.3, 2.9, 1e-7, 2e-7, 5e-7}; @@ -1446,12 +1460,14 @@ TEST_CASE("MULTI_COMPONENT_COLLOIDAL binding model analytic Jacobian vs AD with const bool isKinetic = bindMode; SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) { - cadet::test::binding::testJacobianAD("MULTI_COMPONENT_COLLOIDAL", sizeof(nBound) / sizeof(unsigned int), nBound, isKinetic, config, state, true); + cadet::test::binding::testJacobianAD("MULTI_COMPONENT_COLLOIDAL", sizeof(nBound) / sizeof(unsigned int), + nBound, isKinetic, config, state, true); } } } -TEST_CASE("MULTI_COMPONENT_COLLOIDAL binding model analytic Jacobian vs AD without PH low conc", "[Jacobian],[AD],[BindingModel],[MULTI_COMPONENT_COLLOIDAL]") +TEST_CASE("MULTI_COMPONENT_COLLOIDAL binding model analytic Jacobian vs AD without PH low conc", + "[Jacobian],[AD],[BindingModel],[MULTI_COMPONENT_COLLOIDAL]") { const unsigned int nBound[] = {0, 1, 1, 1}; const double state[] = {0.9, 1.1, 2.3, 2.9, 1e-7, 2e-7, 5e-7}; @@ -1481,27 +1497,29 @@ TEST_CASE("MULTI_COMPONENT_COLLOIDAL binding model analytic Jacobian vs AD witho const bool isKinetic = bindMode; SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) { - cadet::test::binding::testJacobianAD("MULTI_COMPONENT_COLLOIDAL", sizeof(nBound) / sizeof(unsigned int), nBound, isKinetic, config, state, true); + cadet::test::binding::testJacobianAD("MULTI_COMPONENT_COLLOIDAL", sizeof(nBound) / sizeof(unsigned int), + nBound, isKinetic, config, state, true); } } } -CADET_BINDINGTEST("HIC_WATER_ON_HYDROPHOBIC_SURFACES", "EXT_HIC_WATER_ON_HYDROPHOBIC_SURFACES", (0,1,1), (0,1,1,0), (1.0, 2.0, 3.0, 0.0, 0.0), (1.0, 2.0, 3.0, 4.0, 0.0, 0.0), \ - R"json( "HICWHS_KA": [0.0, 0.872767843365959, 1.74553568673192], +CADET_BINDINGTEST("HIC_WATER_ON_HYDROPHOBIC_SURFACES", "EXT_HIC_WATER_ON_HYDROPHOBIC_SURFACES", (0, 1, 1), (0, 1, 1, 0), + (1.0, 2.0, 3.0, 0.0, 0.0), (1.0, 2.0, 3.0, 4.0, 0.0, 0.0), + R"json( "HICWHS_KA": [0.0, 0.872767843365959, 1.74553568673192], "HICWHS_KD": [0.0, 44.9707701873943, 89.9415403747886], "HICWHS_BETA0": 0.0184390384521496, "HICWHS_BETA1": 0.000797098469630127, "HICWHS_NU": [0.0, 10, 20], "HICWHS_QMAX": [0.0, 1000, 2000] - )json", \ - R"json( "HICWHS_KA": [0.0, 0.872767843365959, 1.74553568673192, 2.61830353009788], + )json", + R"json( "HICWHS_KA": [0.0, 0.872767843365959, 1.74553568673192, 2.61830353009788], "HICWHS_KD": [0.0, 44.9707701873943, 89.9415403747886, 134.912310562183], "HICWHS_BETA0": 0.0184390384521496, "HICWHS_BETA1": 0.000797098469630127, "HICWHS_NU": [0.0, 10, 20, 30], "HICWHS_QMAX": [0.0, 1000, 2000, 3000] - )json", \ - R"json( "EXT_HICWHS_KA": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_HICWHS_KA": [0.0, 0.0, 0.0], "EXT_HICWHS_KA_T": [0.0, 0.872767843365959, 1.74553568673192], "EXT_HICWHS_KA_TT": [0.0, 0.0, 0.0], "EXT_HICWHS_KA_TTT": [0.0, 0.0, 0.0], @@ -1525,8 +1543,8 @@ CADET_BINDINGTEST("HIC_WATER_ON_HYDROPHOBIC_SURFACES", "EXT_HIC_WATER_ON_HYDROPH "EXT_HICWHS_QMAX_T": [0.0, 1000, 2000], "EXT_HICWHS_QMAX_TT": [0.0, 0.0, 0.0], "EXT_HICWHS_QMAX_TTT": [0.0, 0.0, 0.0] - )json", \ - R"json( "EXT_HICWHS_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_HICWHS_KA": [0.0, 0.0, 0.0, 0.0], "EXT_HICWHS_KA_T": [0.0, 0.872767843365959, 1.74553568673192, 2.61830353009788], "EXT_HICWHS_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_HICWHS_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -1550,25 +1568,26 @@ CADET_BINDINGTEST("HIC_WATER_ON_HYDROPHOBIC_SURFACES", "EXT_HIC_WATER_ON_HYDROPH "EXT_HICWHS_QMAX_T": [0.0, 1000, 2000, 3000], "EXT_HICWHS_QMAX_TT": [0.0, 0.0, 0.0, 0.0], "EXT_HICWHS_QMAX_TTT": [0.0, 0.0, 0.0, 0.0] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED, CADET_COMPARE_BINDING_VS_NONBINDING) -CADET_BINDINGTEST("HIC_CONSTANT_WATER_ACTIVITY", "EXT_HIC_CONSTANT_WATER_ACTIVITY", (0,1,1), (0,1,1,0), (1.0, 2.0, 3.0, 1.0, 1.0), (1.0, 2.0, 3.0, 4.0, 1.0, 1.0), \ - R"json( "HICCWA_KA": [0.0, 0.474361388031419, 0.948722776062837], +CADET_BINDINGTEST("HIC_CONSTANT_WATER_ACTIVITY", "EXT_HIC_CONSTANT_WATER_ACTIVITY", (0, 1, 1), (0, 1, 1, 0), + (1.0, 2.0, 3.0, 1.0, 1.0), (1.0, 2.0, 3.0, 4.0, 1.0, 1.0), + R"json( "HICCWA_KA": [0.0, 0.474361388031419, 0.948722776062837], "HICCWA_KD": [0.0, 2045.88163309217, 4091.763266184343], "HICCWA_BETA0": 0.326934960685602, "HICCWA_BETA1": 0.000221461823367185, "HICCWA_NU": [0.0, 10, 20], "HICCWA_QMAX": [0.0, 10, 20] - )json", \ - R"json( "HICCWA_KA": [0.0, 0.474361388031419, 0.948722776062837, 1.42308416409426], + )json", + R"json( "HICCWA_KA": [0.0, 0.474361388031419, 0.948722776062837, 1.42308416409426], "HICCWA_KD": [0.0, 2045.88163309217, 4091.763266184343, 6137.64489927651], "HICCWA_BETA0": 0.326934960685602, "HICCWA_BETA1": 0.000221461823367185, "HICCWA_NU": [0.0, 10, 20, 30], "HICCWA_QMAX": [0.0, 10, 20, 30] - )json", \ - R"json( "EXT_HICCWA_KA": [0.0, 0.0, 0.0], + )json", + R"json( "EXT_HICCWA_KA": [0.0, 0.0, 0.0], "EXT_HICCWA_KA_T": [0.0, 0.474361388031419, 0.948722776062837], "EXT_HICCWA_KA_TT": [0.0, 0.0, 0.0], "EXT_HICCWA_KA_TTT": [0.0, 0.0, 0.0], @@ -1592,8 +1611,8 @@ CADET_BINDINGTEST("HIC_CONSTANT_WATER_ACTIVITY", "EXT_HIC_CONSTANT_WATER_ACTIVIT "EXT_HICCWA_QMAX_T": [0.0, 10, 20], "EXT_HICCWA_QMAX_TT": [0.0, 0.0, 0.0], "EXT_HICCWA_QMAX_TTT": [0.0, 0.0, 0.0] - )json", \ - R"json( "EXT_HICCWA_KA": [0.0, 0.0, 0.0, 0.0], + )json", + R"json( "EXT_HICCWA_KA": [0.0, 0.0, 0.0, 0.0], "EXT_HICCWA_KA_T": [0.0, 0.474361388031419, 0.948722776062837, 1.42308416409426], "EXT_HICCWA_KA_TT": [0.0, 0.0, 0.0, 0.0], "EXT_HICCWA_KA_TTT": [0.0, 0.0, 0.0, 0.0], @@ -1617,6 +1636,5 @@ CADET_BINDINGTEST("HIC_CONSTANT_WATER_ACTIVITY", "EXT_HIC_CONSTANT_WATER_ACTIVIT "EXT_HICCWA_QMAX_T": [0.0, 10, 20, 30], "EXT_HICCWA_QMAX_TT": [0.0, 0.0, 0.0, 0.0], "EXT_HICCWA_QMAX_TTT": [0.0, 0.0, 0.0, 0.0] - )json", \ - 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_USED, CADET_COMPARE_BINDING_VS_NONBINDING) - + )json", + 1e-10, 1e-10, CADET_NONBINDING_LIQUIDPHASE_COMP_USED, CADET_COMPARE_BINDING_VS_NONBINDING) diff --git a/test/BindingModels.hpp b/test/BindingModels.hpp index 24adca59a..7c3fba8b1 100644 --- a/test/BindingModels.hpp +++ b/test/BindingModels.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines tests for binding models. */ @@ -25,32 +25,39 @@ #define BRACED_INIT_LIST(...) {__VA_ARGS__} /** - * @brief Emits code for a test that checks whether Jacobian columns belonging to non-binding liquid phase components are all zero - * @details The correct macro, CADET_BINDINGTEST_SINGLE_IMPL_NONBNDJACCONST_false or CADET_BINDINGTEST_SINGLE_IMPL_NONBNDJACCONST_true, - * is selected by concatentation with a variable: CADET_BINDINGTEST_SINGLE_IMPL_NONBNDJACCONST_##SomeVar(...) + * @brief Emits code for a test that checks whether Jacobian columns belonging to non-binding liquid phase components + * are all zero + * @details The correct macro, CADET_BINDINGTEST_SINGLE_IMPL_NONBNDJACCONST_false or + * CADET_BINDINGTEST_SINGLE_IMPL_NONBNDJACCONST_true, is selected by concatentation with a variable: + * CADET_BINDINGTEST_SINGLE_IMPL_NONBNDJACCONST_##SomeVar(...) */ -#define CADET_BINDINGTEST_SINGLE_IMPL_NONBNDJACCONST_false(modelName, tagName, postFix, someNonBinding, stateSomeNon, configSomeNon) +#define CADET_BINDINGTEST_SINGLE_IMPL_NONBNDJACCONST_false(modelName, tagName, postFix, someNonBinding, stateSomeNon, \ + configSomeNon) -#define CADET_BINDINGTEST_SINGLE_IMPL_NONBNDJACCONST_true(modelName, tagName, postFix, someNonBinding, stateSomeNon, configSomeNon) \ - TEST_CASE(modelName " binding model consistency of non-binding Jacobian columns" postFix, "[BindingModel],[Jacobian]," tagName) \ - { \ - const unsigned int nBound3[] = BRACED_INIT_LIST someNonBinding; \ - const double state3[] = BRACED_INIT_LIST stateSomeNon; \ - for (int bindMode = 0; bindMode < 2; ++bindMode) \ - { \ - const bool isKinetic = bindMode; \ - SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) \ - { \ - for (int adMode = 0; adMode < 2; ++adMode) \ - { \ - const bool useAD = adMode; \ - SECTION(std::string("Use AD ") + (useAD ? "yes" : "no")) \ - { \ - cadet::test::binding::testNonBindingConsistency(modelName, sizeof(nBound3) / sizeof(unsigned int), nBound3, isKinetic, "{" configSomeNon "}", useAD, state3); \ - } \ - } \ - } \ - } \ +#define CADET_BINDINGTEST_SINGLE_IMPL_NONBNDJACCONST_true(modelName, tagName, postFix, someNonBinding, stateSomeNon, \ + configSomeNon) \ + TEST_CASE(modelName " binding model consistency of non-binding Jacobian columns" postFix, \ + "[BindingModel],[Jacobian]," tagName) \ + { \ + const unsigned int nBound3[] = BRACED_INIT_LIST someNonBinding; \ + const double state3[] = BRACED_INIT_LIST stateSomeNon; \ + for (int bindMode = 0; bindMode < 2; ++bindMode) \ + { \ + const bool isKinetic = bindMode; \ + SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) \ + { \ + for (int adMode = 0; adMode < 2; ++adMode) \ + { \ + const bool useAD = adMode; \ + SECTION(std::string("Use AD ") + (useAD ? "yes" : "no")) \ + { \ + cadet::test::binding::testNonBindingConsistency( \ + modelName, sizeof(nBound3) / sizeof(unsigned int), nBound3, isKinetic, \ + "{" configSomeNon "}", useAD, state3); \ + } \ + } \ + } \ + } \ } /** @@ -59,36 +66,42 @@ #define CADET_NONBINDING_LIQUIDPHASE_COMP_UNUSED true #define CADET_NONBINDING_LIQUIDPHASE_COMP_USED false - /** * @brief Emits code for a test that checks residual and Jacobian of non-binding model variants against all-binding ones - * @details The correct macro, CADET_BINDINGTEST_SINGLE_IMPL_BNDVSNONBND_false or CADET_BINDINGTEST_SINGLE_IMPL_BNDVSNONBND_true, - * is selected by concatentation with a variable: CADET_BINDINGTEST_SINGLE_IMPL_BNDVSNONBND_##SomeVar(...) + * @details The correct macro, CADET_BINDINGTEST_SINGLE_IMPL_BNDVSNONBND_false or + * CADET_BINDINGTEST_SINGLE_IMPL_BNDVSNONBND_true, is selected by concatentation with a variable: + * CADET_BINDINGTEST_SINGLE_IMPL_BNDVSNONBND_##SomeVar(...) */ -#define CADET_BINDINGTEST_SINGLE_IMPL_BNDVSNONBND_false(modelName, tagName, postFix, allBinding, someNonBinding, stateAll, stateSomeNon, configAll, configSomeNon) +#define CADET_BINDINGTEST_SINGLE_IMPL_BNDVSNONBND_false(modelName, tagName, postFix, allBinding, someNonBinding, \ + stateAll, stateSomeNon, configAll, configSomeNon) -#define CADET_BINDINGTEST_SINGLE_IMPL_BNDVSNONBND_true(modelName, tagName, postFix, allBinding, someNonBinding, stateAll, stateSomeNon, configAll, configSomeNon) \ - TEST_CASE(modelName " binding model consistency of non-binding vs binding" postFix, "[BindingModel],[Jacobian]," tagName) \ - { \ - const unsigned int nBound2[] = BRACED_INIT_LIST allBinding; \ - const unsigned int nBound3[] = BRACED_INIT_LIST someNonBinding; \ - const double state2[] = BRACED_INIT_LIST stateAll; \ - const double state3[] = BRACED_INIT_LIST stateSomeNon; \ - for (int bindMode = 0; bindMode < 2; ++bindMode) \ - { \ - const bool isKinetic = bindMode; \ - SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) \ - { \ - for (int adMode = 0; adMode < 2; ++adMode) \ - { \ - const bool useAD = adMode; \ - SECTION(std::string("Use AD ") + (useAD ? "yes" : "no")) \ - { \ - cadet::test::binding::testNonbindingBindingConsistency(modelName, sizeof(nBound2) / sizeof(unsigned int), sizeof(nBound3) / sizeof(unsigned int), nBound2, nBound3, isKinetic, "{" configAll "}", "{" configSomeNon "}", useAD, state2, state3); \ - } \ - } \ - } \ - } \ +#define CADET_BINDINGTEST_SINGLE_IMPL_BNDVSNONBND_true(modelName, tagName, postFix, allBinding, someNonBinding, \ + stateAll, stateSomeNon, configAll, configSomeNon) \ + TEST_CASE(modelName " binding model consistency of non-binding vs binding" postFix, \ + "[BindingModel],[Jacobian]," tagName) \ + { \ + const unsigned int nBound2[] = BRACED_INIT_LIST allBinding; \ + const unsigned int nBound3[] = BRACED_INIT_LIST someNonBinding; \ + const double state2[] = BRACED_INIT_LIST stateAll; \ + const double state3[] = BRACED_INIT_LIST stateSomeNon; \ + for (int bindMode = 0; bindMode < 2; ++bindMode) \ + { \ + const bool isKinetic = bindMode; \ + SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) \ + { \ + for (int adMode = 0; adMode < 2; ++adMode) \ + { \ + const bool useAD = adMode; \ + SECTION(std::string("Use AD ") + (useAD ? "yes" : "no")) \ + { \ + cadet::test::binding::testNonbindingBindingConsistency( \ + modelName, sizeof(nBound2) / sizeof(unsigned int), sizeof(nBound3) / sizeof(unsigned int), \ + nBound2, nBound3, isKinetic, "{" configAll "}", "{" configSomeNon "}", useAD, state2, \ + state3); \ + } \ + } \ + } \ + } \ } /** @@ -97,7 +110,6 @@ #define CADET_COMPARE_BINDING_VS_NONBINDING true #define CADET_DONT_COMPARE_BINDING_VS_NONBINDING false - /** * @brief Emits tests for a binding model having a non-binding and an all-binding variant * @param modelName Identifier of the model as string (e.g. "LINEAR") @@ -111,35 +123,42 @@ * @param configSomeNon Interior of a JSON object block with parameters for the non-binding variant * @param consInitTol Error tolerance for nonlinear solvers in consistent initialization * @param consInitCheckTol Error tolerance for residual check in consistent initialization - * @param usesNonBindingLiquidPhase Determines whether a test for all-zero Jacobian columns belonging to non-binding components is created - * @param cmpBndVsNonbnd Determines whether a test for all-binding vs non-binding variant (Jacobian and residual) is created + * @param usesNonBindingLiquidPhase Determines whether a test for all-zero Jacobian columns belonging to non-binding + * components is created + * @param cmpBndVsNonbnd Determines whether a test for all-binding vs non-binding variant (Jacobian and residual) is + * created */ -#define CADET_BINDINGTEST_SINGLE_IMPL(modelName, tagName, postFix, allBinding, someNonBinding, stateAll, stateSomeNon, configAll, configSomeNon, consInitTol, consInitCheckTol, usesNonBindingLiquidPhase, cmpBndVsNonbnd) \ - TEST_CASE(modelName " binding model analytic Jacobian vs AD" postFix, "[Jacobian],[AD],[BindingModel]," tagName) \ - { \ - const unsigned int nBound2[] = BRACED_INIT_LIST allBinding; \ - const unsigned int nBound3[] = BRACED_INIT_LIST someNonBinding; \ - const double state2[] = BRACED_INIT_LIST stateAll; \ - const double state3[] = BRACED_INIT_LIST stateSomeNon; \ - for (int bindMode = 0; bindMode < 2; ++bindMode) \ - { \ - const bool isKinetic = bindMode; \ - SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) \ - { \ - SECTION("Without nonbinding components") \ - { \ - cadet::test::binding::testJacobianAD(modelName, sizeof(nBound2) / sizeof(unsigned int), nBound2, isKinetic, "{" configAll "}", state2); \ - } \ - SECTION("With nonbinding components") \ - { \ - cadet::test::binding::testJacobianAD(modelName, sizeof(nBound3) / sizeof(unsigned int), nBound3, isKinetic, "{" configSomeNon "}", state3); \ - } \ - } \ - } \ - } \ - CADET_BINDINGTEST_SINGLE_IMPL_NONBNDJACCONST_##usesNonBindingLiquidPhase(modelName, tagName, postFix, someNonBinding, stateSomeNon, configSomeNon) \ - CADET_BINDINGTEST_SINGLE_IMPL_BNDVSNONBND_##cmpBndVsNonbnd(modelName, tagName, postFix, allBinding, someNonBinding, stateAll, stateSomeNon, configAll, configSomeNon) - +#define CADET_BINDINGTEST_SINGLE_IMPL(modelName, tagName, postFix, allBinding, someNonBinding, stateAll, stateSomeNon, \ + configAll, configSomeNon, consInitTol, consInitCheckTol, \ + usesNonBindingLiquidPhase, cmpBndVsNonbnd) \ + TEST_CASE(modelName " binding model analytic Jacobian vs AD" postFix, "[Jacobian],[AD],[BindingModel]," tagName) \ + { \ + const unsigned int nBound2[] = BRACED_INIT_LIST allBinding; \ + const unsigned int nBound3[] = BRACED_INIT_LIST someNonBinding; \ + const double state2[] = BRACED_INIT_LIST stateAll; \ + const double state3[] = BRACED_INIT_LIST stateSomeNon; \ + for (int bindMode = 0; bindMode < 2; ++bindMode) \ + { \ + const bool isKinetic = bindMode; \ + SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) \ + { \ + SECTION("Without nonbinding components") \ + { \ + cadet::test::binding::testJacobianAD(modelName, sizeof(nBound2) / sizeof(unsigned int), nBound2, \ + isKinetic, "{" configAll "}", state2); \ + } \ + SECTION("With nonbinding components") \ + { \ + cadet::test::binding::testJacobianAD(modelName, sizeof(nBound3) / sizeof(unsigned int), nBound3, \ + isKinetic, "{" configSomeNon "}", state3); \ + } \ + } \ + } \ + } \ + CADET_BINDINGTEST_SINGLE_IMPL_NONBNDJACCONST_##usesNonBindingLiquidPhase( \ + modelName, tagName, postFix, someNonBinding, stateSomeNon, configSomeNon) \ + CADET_BINDINGTEST_SINGLE_IMPL_BNDVSNONBND_##cmpBndVsNonbnd( \ + modelName, tagName, postFix, allBinding, someNonBinding, stateAll, stateSomeNon, configAll, configSomeNon) /** * @brief Emits tests for a binding model having a non-binding and an all-binding variant @@ -152,15 +171,21 @@ * @param configSomeNon Interior of a JSON object block with parameters for the non-binding variant * @param consInitTol Error tolerance for nonlinear solvers in consistent initialization * @param consInitCheckTol Error tolerance for residual check in consistent initialization - * @param usesNonBindingLiquidPhase Determines whether a test for all-zero Jacobian columns belonging to non-binding components is created - * @param cmpBndVsNonbnd Determines whether a test for all-binding vs non-binding variant (Jacobian and residual) is created + * @param usesNonBindingLiquidPhase Determines whether a test for all-zero Jacobian columns belonging to non-binding + * components is created + * @param cmpBndVsNonbnd Determines whether a test for all-binding vs non-binding variant (Jacobian and residual) is + * created */ -#define CADET_BINDINGTEST_SINGLE(modelName, allBinding, someNonBinding, stateAll, stateSomeNon, configAll, configSomeNon, consInitTol, consInitCheckTol, usesNonBindingLiquidPhase, cmpBndVsNonbnd) \ - CADET_BINDINGTEST_SINGLE_IMPL(modelName, "[" modelName "]", "", allBinding, someNonBinding, stateAll, stateSomeNon, configAll, configSomeNon, consInitTol, consInitCheckTol, usesNonBindingLiquidPhase, cmpBndVsNonbnd) - +#define CADET_BINDINGTEST_SINGLE(modelName, allBinding, someNonBinding, stateAll, stateSomeNon, configAll, \ + configSomeNon, consInitTol, consInitCheckTol, usesNonBindingLiquidPhase, \ + cmpBndVsNonbnd) \ + CADET_BINDINGTEST_SINGLE_IMPL(modelName, "[" modelName "]", "", allBinding, someNonBinding, stateAll, \ + stateSomeNon, configAll, configSomeNon, consInitTol, consInitCheckTol, \ + usesNonBindingLiquidPhase, cmpBndVsNonbnd) /** - * @brief Emits tests for a binding model that can have external function dependence and has a non-binding and an all-binding variant + * @brief Emits tests for a binding model that can have external function dependence and has a non-binding and an + * all-binding variant * @param modelName Identifier of the model as string (e.g. "LINEAR") * @param extModelName Identifier of the externally dependent model as string (e.g. "EXT_LINEAR") * @param postFix String appended to the test description / name @@ -171,41 +196,55 @@ * @param configAll Interior of a JSON object block with parameters for the all-binding variant * @param configSomeNon Interior of a JSON object block with parameters for the non-binding variant * @param extConfigAll Interior of a JSON object block with parameters for the externally dependent all-binding variant - * @param extConfigSomeNon Interior of a JSON object block with parameters for the externally dependent non-binding variant + * @param extConfigSomeNon Interior of a JSON object block with parameters for the externally dependent non-binding + * variant * @param consInitTol Error tolerance for nonlinear solvers in consistent initialization * @param consInitCheckTol Error tolerance for residual check in consistent initialization - * @param usesNonBindingLiquidPhase Determines whether a test for all-zero Jacobian columns belonging to non-binding components is created - * @param cmpBndVsNonbnd Determines whether a test for all-binding vs non-binding variant (Jacobian and residual) is created + * @param usesNonBindingLiquidPhase Determines whether a test for all-zero Jacobian columns belonging to non-binding + * components is created + * @param cmpBndVsNonbnd Determines whether a test for all-binding vs non-binding variant (Jacobian and residual) is + * created */ -#define CADET_BINDINGTEST_MULTI(modelName, extModelName, postFix, allBinding, someNonBinding, stateAll, stateSomeNon, configAll, configSomeNon, extConfigAll, extConfigSomeNon, consInitTol, consInitCheckTol, usesNonBindingLiquidPhase, cmpBndVsNonbnd) \ - CADET_BINDINGTEST_SINGLE_IMPL(modelName, "[" modelName "]", postFix, allBinding, someNonBinding, stateAll, stateSomeNon, configAll, configSomeNon, consInitTol, consInitCheckTol, usesNonBindingLiquidPhase, cmpBndVsNonbnd) \ - CADET_BINDINGTEST_SINGLE_IMPL(extModelName, "[ExternalFunction],[" extModelName "]", postFix, allBinding, someNonBinding, stateAll, stateSomeNon, extConfigAll, extConfigSomeNon, consInitTol, consInitCheckTol, usesNonBindingLiquidPhase, cmpBndVsNonbnd) \ - TEST_CASE(modelName " binding model consistent with externally dependent variant" postFix, "[BindingModel],[ExternalFunction],[" modelName "],[" extModelName "]") \ - { \ - const unsigned int nBound2[] = BRACED_INIT_LIST allBinding; \ - const unsigned int nBound3[] = BRACED_INIT_LIST someNonBinding; \ - const double state2[] = BRACED_INIT_LIST stateAll; \ - const double state3[] = BRACED_INIT_LIST stateSomeNon; \ - for (int bindMode = 0; bindMode < 2; ++bindMode) \ - { \ - const bool isKinetic = bindMode; \ - SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) \ - { \ - SECTION("Without nonbinding components") \ - { \ - cadet::test::binding::testNormalExternalConsistency(modelName, extModelName, sizeof(nBound2) / sizeof(unsigned int), nBound2, isKinetic, "{" configAll "," extConfigAll "}", state2); \ - } \ - SECTION("With nonbinding components") \ - { \ - cadet::test::binding::testNormalExternalConsistency(modelName, extModelName, sizeof(nBound3) / sizeof(unsigned int), nBound3, isKinetic, "{" configSomeNon "," extConfigSomeNon "}", state3); \ - } \ - } \ - } \ +#define CADET_BINDINGTEST_MULTI(modelName, extModelName, postFix, allBinding, someNonBinding, stateAll, stateSomeNon, \ + configAll, configSomeNon, extConfigAll, extConfigSomeNon, consInitTol, \ + consInitCheckTol, usesNonBindingLiquidPhase, cmpBndVsNonbnd) \ + CADET_BINDINGTEST_SINGLE_IMPL(modelName, "[" modelName "]", postFix, allBinding, someNonBinding, stateAll, \ + stateSomeNon, configAll, configSomeNon, consInitTol, consInitCheckTol, \ + usesNonBindingLiquidPhase, cmpBndVsNonbnd) \ + CADET_BINDINGTEST_SINGLE_IMPL(extModelName, "[ExternalFunction],[" extModelName "]", postFix, allBinding, \ + someNonBinding, stateAll, stateSomeNon, extConfigAll, extConfigSomeNon, consInitTol, \ + consInitCheckTol, usesNonBindingLiquidPhase, cmpBndVsNonbnd) \ + TEST_CASE(modelName " binding model consistent with externally dependent variant" postFix, \ + "[BindingModel],[ExternalFunction],[" modelName "],[" extModelName "]") \ + { \ + const unsigned int nBound2[] = BRACED_INIT_LIST allBinding; \ + const unsigned int nBound3[] = BRACED_INIT_LIST someNonBinding; \ + const double state2[] = BRACED_INIT_LIST stateAll; \ + const double state3[] = BRACED_INIT_LIST stateSomeNon; \ + for (int bindMode = 0; bindMode < 2; ++bindMode) \ + { \ + const bool isKinetic = bindMode; \ + SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) \ + { \ + SECTION("Without nonbinding components") \ + { \ + cadet::test::binding::testNormalExternalConsistency( \ + modelName, extModelName, sizeof(nBound2) / sizeof(unsigned int), nBound2, isKinetic, \ + "{" configAll "," extConfigAll "}", state2); \ + } \ + SECTION("With nonbinding components") \ + { \ + cadet::test::binding::testNormalExternalConsistency( \ + modelName, extModelName, sizeof(nBound3) / sizeof(unsigned int), nBound3, isKinetic, \ + "{" configSomeNon "," extConfigSomeNon "}", state3); \ + } \ + } \ + } \ } - /** - * @brief Emits tests for a binding model that can have external function dependence and has a non-binding and an all-binding variant + * @brief Emits tests for a binding model that can have external function dependence and has a non-binding and an + * all-binding variant * @param modelName Identifier of the model as string (e.g. "LINEAR") * @param extModelName Identifier of the externally dependent model as string (e.g. "EXT_LINEAR") * @param allBinding Array with number of bound states of the all-binding variant in parentheses (e.g., (1, 1, 2)) @@ -215,14 +254,21 @@ * @param configAll Interior of a JSON object block with parameters for the all-binding variant * @param configSomeNon Interior of a JSON object block with parameters for the non-binding variant * @param extConfigAll Interior of a JSON object block with parameters for the externally dependent all-binding variant - * @param extConfigSomeNon Interior of a JSON object block with parameters for the externally dependent non-binding variant + * @param extConfigSomeNon Interior of a JSON object block with parameters for the externally dependent non-binding + * variant * @param consInitTol Error tolerance for nonlinear solvers in consistent initialization * @param consInitCheckTol Error tolerance for residual check in consistent initialization - * @param usesNonBindingLiquidPhase Determines whether a test for all-zero Jacobian columns belonging to non-binding components is created - * @param cmpBndVsNonbnd Determines whether a test for all-binding vs non-binding variant (Jacobian and residual) is created + * @param usesNonBindingLiquidPhase Determines whether a test for all-zero Jacobian columns belonging to non-binding + * components is created + * @param cmpBndVsNonbnd Determines whether a test for all-binding vs non-binding variant (Jacobian and residual) is + * created */ -#define CADET_BINDINGTEST(modelName, extModelName, allBinding, someNonBinding, stateAll, stateSomeNon, configAll, configSomeNon, extConfigAll, extConfigSomeNon, consInitTol, consInitCheckTol, usesNonBindingLiquidPhase, cmpBndVsNonbnd) \ - CADET_BINDINGTEST_MULTI(modelName, extModelName, "", allBinding, someNonBinding, stateAll, stateSomeNon, configAll, configSomeNon, extConfigAll, extConfigSomeNon, consInitTol, consInitCheckTol, usesNonBindingLiquidPhase, cmpBndVsNonbnd) +#define CADET_BINDINGTEST(modelName, extModelName, allBinding, someNonBinding, stateAll, stateSomeNon, configAll, \ + configSomeNon, extConfigAll, extConfigSomeNon, consInitTol, consInitCheckTol, \ + usesNonBindingLiquidPhase, cmpBndVsNonbnd) \ + CADET_BINDINGTEST_MULTI(modelName, extModelName, "", allBinding, someNonBinding, stateAll, stateSomeNon, \ + configAll, configSomeNon, extConfigAll, extConfigSomeNon, consInitTol, consInitCheckTol, \ + usesNonBindingLiquidPhase, cmpBndVsNonbnd) /** * @brief Emits tests for a binding model that does not allow non-binding components @@ -230,41 +276,43 @@ * @param tagName Tags added to the tests as string (e.g., "[SOMETAG]") * @param allBinding Array with number of bound states in parentheses (e.g., (1, 1, 2)) * @param stateAll Array with full state vector (liquid and solid phase) in parentheses - * @param configAll Interior of a JSON object block with parameters for the + * @param configAll Interior of a JSON object block with parameters for the * @param consInitTol Error tolerance for nonlinear solvers in consistent initialization * @param consInitCheckTol Error tolerance for residual check in consistent initialization */ -#define CADET_BINDINGTEST_ALLBINDING_SINGLE_IMPL(modelName, tagName, allBinding, stateAll, configAll, consInitTol, consInitCheckTol) \ - TEST_CASE(modelName " binding model analytic Jacobian vs AD", "[Jacobian],[AD],[BindingModel]," tagName) \ - { \ - const unsigned int nBound2[] = BRACED_INIT_LIST allBinding; \ - const double state2[] = BRACED_INIT_LIST stateAll; \ - for (int bindMode = 0; bindMode < 2; ++bindMode) \ - { \ - const bool isKinetic = bindMode; \ - SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) \ - { \ - cadet::test::binding::testJacobianAD(modelName, sizeof(nBound2) / sizeof(unsigned int), nBound2, isKinetic, "{" configAll "}", state2); \ - } \ - } \ +#define CADET_BINDINGTEST_ALLBINDING_SINGLE_IMPL(modelName, tagName, allBinding, stateAll, configAll, consInitTol, \ + consInitCheckTol) \ + TEST_CASE(modelName " binding model analytic Jacobian vs AD", "[Jacobian],[AD],[BindingModel]," tagName) \ + { \ + const unsigned int nBound2[] = BRACED_INIT_LIST allBinding; \ + const double state2[] = BRACED_INIT_LIST stateAll; \ + for (int bindMode = 0; bindMode < 2; ++bindMode) \ + { \ + const bool isKinetic = bindMode; \ + SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) \ + { \ + cadet::test::binding::testJacobianAD(modelName, sizeof(nBound2) / sizeof(unsigned int), nBound2, \ + isKinetic, "{" configAll "}", state2); \ + } \ + } \ } - /** * @brief Emits tests for a binding model that does not allow non-binding components * @param modelName Identifier of the model as string (e.g. "LINEAR") * @param allBinding Array with number of bound states in parentheses (e.g., (1, 1, 2)) * @param stateAll Array with full state vector (liquid and solid phase) in parentheses - * @param configAll Interior of a JSON object block with parameters for the + * @param configAll Interior of a JSON object block with parameters for the * @param consInitTol Error tolerance for nonlinear solvers in consistent initialization * @param consInitCheckTol Error tolerance for residual check in consistent initialization */ #define CADET_BINDINGTEST_ALLBINDING_SINGLE(modelName, allBinding, stateAll, configAll, consInitTol, consInitCheckTol) \ - CADET_BINDINGTEST_ALLBINDING_SINGLE_IMPL(modelName, "[" modelName "]", allBinding, stateAll, configAll, consInitTol, consInitCheckTol) - + CADET_BINDINGTEST_ALLBINDING_SINGLE_IMPL(modelName, "[" modelName "]", allBinding, stateAll, configAll, \ + consInitTol, consInitCheckTol) /** - * @brief Emits tests for a binding model that can have external function dependence and does not allow non-binding components + * @brief Emits tests for a binding model that can have external function dependence and does not allow non-binding + * components * @param modelName Identifier of the model as string (e.g. "LINEAR") * @param extModelName Identifier of the externally dependent model as string (e.g. "EXT_LINEAR") * @param allBinding Array with number of bound states in parentheses (e.g., (1, 1, 2)) @@ -274,22 +322,27 @@ * @param consInitTol Error tolerance for nonlinear solvers in consistent initialization * @param consInitCheckTol Error tolerance for residual check in consistent initialization */ -#define CADET_BINDINGTEST_ALLBINDING(modelName, extModelName, allBinding, stateAll, configAll, extConfigAll, consInitTol, consInitCheckTol) \ - CADET_BINDINGTEST_ALLBINDING_SINGLE_IMPL(modelName, "[" modelName "]", allBinding, stateAll, configAll, consInitTol, consInitCheckTol) \ - CADET_BINDINGTEST_ALLBINDING_SINGLE_IMPL(extModelName, "[ExternalFunction],[" extModelName "]", allBinding, stateAll, extConfigAll, consInitTol, consInitCheckTol) \ - TEST_CASE(modelName " binding model consistent with externally dependent variant", "[BindingModel],[ExternalFunction],[" modelName "],[" extModelName "]") \ - { \ - const unsigned int nBound2[] = BRACED_INIT_LIST allBinding; \ - const double state2[] = BRACED_INIT_LIST stateAll; \ - for (int bindMode = 0; bindMode < 2; ++bindMode) \ - { \ - const bool isKinetic = bindMode; \ - SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) \ - { \ - cadet::test::binding::testNormalExternalConsistency(modelName, extModelName, sizeof(nBound2) / sizeof(unsigned int), nBound2, isKinetic, "{" configAll "," extConfigAll "}", state2); \ - } \ - } \ +#define CADET_BINDINGTEST_ALLBINDING(modelName, extModelName, allBinding, stateAll, configAll, extConfigAll, \ + consInitTol, consInitCheckTol) \ + CADET_BINDINGTEST_ALLBINDING_SINGLE_IMPL(modelName, "[" modelName "]", allBinding, stateAll, configAll, \ + consInitTol, consInitCheckTol) \ + CADET_BINDINGTEST_ALLBINDING_SINGLE_IMPL(extModelName, "[ExternalFunction],[" extModelName "]", allBinding, \ + stateAll, extConfigAll, consInitTol, consInitCheckTol) \ + TEST_CASE(modelName " binding model consistent with externally dependent variant", \ + "[BindingModel],[ExternalFunction],[" modelName "],[" extModelName "]") \ + { \ + const unsigned int nBound2[] = BRACED_INIT_LIST allBinding; \ + const double state2[] = BRACED_INIT_LIST stateAll; \ + for (int bindMode = 0; bindMode < 2; ++bindMode) \ + { \ + const bool isKinetic = bindMode; \ + SECTION(std::string("Binding mode ") + (isKinetic ? "dynamic" : "quasi-stationary")) \ + { \ + cadet::test::binding::testNormalExternalConsistency( \ + modelName, extModelName, sizeof(nBound2) / sizeof(unsigned int), nBound2, isKinetic, \ + "{" configAll "," extConfigAll "}", state2); \ + } \ + } \ } - -#endif // CADETTEST_BINDINGMODELS_HPP_ +#endif // CADETTEST_BINDINGMODELS_HPP_ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 58be03222..10540fa5c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,9 +1,9 @@ # ============================================================================= # CADET -# +# # Copyright © The CADET Authors # Please see the CONTRIBUTORS.md file. -# +# # All rights reserved. This program and the accompanying materials # are made available under the terms of the GNU Public License v3.0 (or, at # your option, any later version) which accompanies this distribution, and @@ -136,4 +136,3 @@ unset(TEST_ADDITIONAL_SOURCES) # Info message message(STATUS "Added tests") - diff --git a/test/CSTR-Residual.cpp b/test/CSTR-Residual.cpp index 9fcb0843a..148162391 100644 --- a/test/CSTR-Residual.cpp +++ b/test/CSTR-Residual.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -83,7 +83,8 @@ inline cadet::JsonParameterProvider createTwoComponentLinearThreeParticleTypesTe return jpp; } -inline void checkJacobianAD(double flowRateIn, double flowRateOut, double flowRateFilter, std::function modelRefiner) +inline void checkJacobianAD(double flowRateIn, double flowRateOut, double flowRateFilter, + std::function modelRefiner) { cadet::IModelBuilder* const mb = cadet::createModelBuilder(); REQUIRE(nullptr != mb); @@ -126,16 +127,20 @@ inline void checkJacobianAD(double flowRateIn, double flowRateOut, double flowRa tls.resize(cstrAna->threadLocalMemorySize()); // Fill state vectors with some values - cadet::test::util::populate(y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); - cadet::test::util::populate(yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); // Setup matrices cstrAna->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, noParams); cstrAD->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, adParams); // Compute state Jacobian - cstrAna->residualWithJacobian(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), nullptr}, jacDir.data(), noParams, tls); - cstrAD->residualWithJacobian(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), nullptr}, jacDir.data(), adParams, tls); + cstrAna->residualWithJacobian(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), nullptr}, + jacDir.data(), noParams, tls); + cstrAD->residualWithJacobian(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), nullptr}, + jacDir.data(), adParams, tls); std::fill(jacDir.begin(), jacDir.end(), 0.0); // Compare Jacobians @@ -145,10 +150,11 @@ inline void checkJacobianAD(double flowRateIn, double flowRateOut, double flowRa delete[] adY; mb->destroyUnitOperation(cstrAna); mb->destroyUnitOperation(cstrAD); - destroyModelBuilder(mb); + destroyModelBuilder(mb); } -inline void checkJacobianFD(double flowRateIn, double flowRateOut, double flowRateFilter, std::function modelRefiner) +inline void checkJacobianFD(double flowRateIn, double flowRateOut, double flowRateFilter, + std::function modelRefiner) { cadet::IModelBuilder* const mb = cadet::createModelBuilder(); REQUIRE(nullptr != mb); @@ -175,25 +181,32 @@ inline void checkJacobianFD(double flowRateIn, double flowRateOut, double flowRa tls.resize(cstr->threadLocalMemorySize()); // Fill state vectors with some values - cadet::test::util::populate(y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); - cadet::test::util::populate(yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); // Setup matrices - cstr->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, cadet::AdJacobianParams{nullptr, nullptr, 0u}); + cstr->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, + cadet::AdJacobianParams{nullptr, nullptr, 0u}); // Compute state Jacobian - cstr->residualWithJacobian(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), yDot.data()}, jacDir.data(), cadet::AdJacobianParams{nullptr, nullptr, 0u}, tls); + cstr->residualWithJacobian(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), yDot.data()}, + jacDir.data(), cadet::AdJacobianParams{nullptr, nullptr, 0u}, tls); std::fill(jacDir.begin(), jacDir.end(), 0.0); // Compare Jacobians -// cadet::test::checkJacobianPatternFD(cstr, cstr, y.data(), yDot.data(), jacDir.data(), jacCol1.data(), jacCol2.data()); - cadet::test::compareJacobianFD(cstr, cstr, y.data(), yDot.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), tls); + // cadet::test::checkJacobianPatternFD(cstr, cstr, y.data(), yDot.data(), jacDir.data(), jacCol1.data(), + // jacCol2.data()); + cadet::test::compareJacobianFD(cstr, cstr, y.data(), yDot.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), + tls); mb->destroyUnitOperation(cstr); - destroyModelBuilder(mb); + destroyModelBuilder(mb); } -inline void checkTimeDerivativeJacobianFD(double flowRateIn, double flowRateOut, double flowRateFilter, std::function modelRefiner) +inline void checkTimeDerivativeJacobianFD(double flowRateIn, double flowRateOut, double flowRateFilter, + std::function modelRefiner) { cadet::IModelBuilder* const mb = cadet::createModelBuilder(); REQUIRE(nullptr != mb); @@ -220,20 +233,26 @@ inline void checkTimeDerivativeJacobianFD(double flowRateIn, double flowRateOut, tls.resize(cstr->threadLocalMemorySize()); // Fill state vectors with some values - cadet::test::util::populate(y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); - cadet::test::util::populate(yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); // Setup matrices - cstr->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, cadet::AdJacobianParams{nullptr, nullptr, 0u}); + cstr->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, + cadet::AdJacobianParams{nullptr, nullptr, 0u}); // Compare Jacobians - cadet::test::compareTimeDerivativeJacobianFD(cstr, cstr, y.data(), yDot.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), tls); + cadet::test::compareTimeDerivativeJacobianFD(cstr, cstr, y.data(), yDot.data(), jacDir.data(), jacCol1.data(), + jacCol2.data(), tls); mb->destroyUnitOperation(cstr); destroyModelBuilder(mb); } -inline void checkConsistentInitialization(const std::function& modelCreator, double const* initState, double consTol, double absTol) +inline void checkConsistentInitialization( + const std::function& modelCreator, double const* initState, + double consTol, double absTol) { cadet::IModelBuilder* const mb = cadet::createModelBuilder(); REQUIRE(nullptr != mb); @@ -244,7 +263,8 @@ inline void checkConsistentInitialization(const std::function 0); - SECTION(std::string(isKinetic ? " Kinetic binding" : " Quasi-stationary binding") + " with AD " + (adEnabled ? "enabled" : "disabled")) + SECTION(std::string(isKinetic ? " Kinetic binding" : " Quasi-stationary binding") + " with AD " + + (adEnabled ? "enabled" : "disabled")) { cadet::model::CSTRModel* const cstr = modelCreator(*mb, isKinetic); std::vector y(initState, initState + cstr->numDofs()); @@ -256,7 +276,9 @@ inline void checkConsistentInitialization(const std::function& modelCreator, double const* y, double const* yDot, double absTol) +inline void checkSensitivityConsistentInitialization( + const std::function& modelCreator, double const* y, + double const* yDot, double absTol) { cadet::IModelBuilder* const mb = cadet::createModelBuilder(); REQUIRE(nullptr != mb); @@ -267,7 +289,8 @@ inline void checkSensitivityConsistentInitialization(const std::function 0); - SECTION(std::string(isKinetic ? " Kinetic binding" : " Quasi-stationary binding") + " with AD " + (adEnabled ? "enabled" : "disabled")) + SECTION(std::string(isKinetic ? " Kinetic binding" : " Quasi-stationary binding") + " with AD " + + (adEnabled ? "enabled" : "disabled")) { cadet::model::CSTRModel* const cstr = modelCreator(*mb, isKinetic); cadet::test::unitoperation::testConsistentInitializationSensitivity(cstr, adEnabled, y, yDot, absTol); @@ -280,107 +303,58 @@ inline void checkSensitivityConsistentInitialization(const std::function y(2 + 2 * 2 + 1, 0.0); - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, y.size()); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, y.size()); - checkConsistentInitialization([](cadet::IModelBuilder& mb, bool dynamic) -> cadet::model::CSTRModel* { - cadet::JsonParameterProvider jpp = createCSTR(2); - cadet::test::addBoundStates(jpp, {1, 1}, 0.5); - cadet::test::addLinearBindingModel(jpp, dynamic, {5.0, 4.0}, {2.0, 3.0}); + checkConsistentInitialization( + [](cadet::IModelBuilder& mb, bool dynamic) -> cadet::model::CSTRModel* { + cadet::JsonParameterProvider jpp = createCSTR(2); + cadet::test::addBoundStates(jpp, {1, 1}, 0.5); + cadet::test::addLinearBindingModel(jpp, dynamic, {5.0, 4.0}, {2.0, 3.0}); - cadet::model::CSTRModel* const cstr = createAndConfigureCSTR(mb, jpp); - cstr->setFlowRates(1.0, 1.0); - return cstr; - }, y.data(), 1e-14, 1e-14); + cadet::model::CSTRModel* const cstr = createAndConfigureCSTR(mb, jpp); + cstr->setFlowRates(1.0, 1.0); + return cstr; + }, + y.data(), 1e-14, 1e-14); } TEST_CASE("StirredTankModel consistent initialization with SMA binding", "[CSTR],[ConsistentInit]") { -// Optimal values: -// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153}; + // Optimal values: + // const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153}; const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 860.0, 66.0, 3.5, 2.5}; // Fill state vector with initial values @@ -500,45 +456,62 @@ TEST_CASE("StirredTankModel consistent initialization with SMA binding", "[CSTR] std::copy(bindingCell, bindingCell + 8, y.data() + 4); y[4 + 8] = 1.0; - checkConsistentInitialization([](cadet::IModelBuilder& mb, bool dynamic) -> cadet::model::CSTRModel* { - cadet::JsonParameterProvider jpp = createCSTR(4); - cadet::test::addBoundStates(jpp, {1, 1, 1, 1}, 0.5); - cadet::test::addSMABindingModel(jpp, dynamic, 1.2e3, {0.0, 35.5, 1.59, 7.7}, {0.0, 1000.0, 1000.0, 1000.0}, {0.0, 4.7, 5.29, 3.7}, {0.0, 11.83, 10.6, 10.0}); + checkConsistentInitialization( + [](cadet::IModelBuilder& mb, bool dynamic) -> cadet::model::CSTRModel* { + cadet::JsonParameterProvider jpp = createCSTR(4); + cadet::test::addBoundStates(jpp, {1, 1, 1, 1}, 0.5); + cadet::test::addSMABindingModel(jpp, dynamic, 1.2e3, {0.0, 35.5, 1.59, 7.7}, {0.0, 1000.0, 1000.0, 1000.0}, + {0.0, 4.7, 5.29, 3.7}, {0.0, 11.83, 10.6, 10.0}); - cadet::model::CSTRModel* const cstr = createAndConfigureCSTR(mb, jpp); - cstr->setFlowRates(1.0, 1.0); - return cstr; - }, y.data(), 1e-14, 1e-5); + cadet::model::CSTRModel* const cstr = createAndConfigureCSTR(mb, jpp); + cstr->setFlowRates(1.0, 1.0); + return cstr; + }, + y.data(), 1e-14, 1e-5); } -TEST_CASE("StirredTankModel consistent sensitivity initialization with linear binding", "[CSTR],[ConsistentInit],[Sensitivity]") +TEST_CASE("StirredTankModel consistent sensitivity initialization with linear binding", + "[CSTR],[ConsistentInit],[Sensitivity]") { // Fill state vector with initial values std::vector y(2 + 2 * 2 + 1, 0.0); std::vector yDot(2 + 2 * 2 + 1, 0.0); - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, y.size()); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, yDot.size()); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, y.size()); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, yDot.size()); // Note that checkSensitivityConsistentInitialization() applies non-zero initial values for the whole // sensitivity state vector. Hence, this is more strict than usual as most initial sensitivity state // vectors are all zero (only sensitivities wrt. initial conditions produce non-zero initial values). - checkSensitivityConsistentInitialization([](cadet::IModelBuilder& mb, bool dynamic) -> cadet::model::CSTRModel* { - cadet::JsonParameterProvider jpp = createCSTR(2); - cadet::test::addBoundStates(jpp, {1, 1}, 0.5); - cadet::test::addLinearBindingModel(jpp, dynamic, {5.0, 4.0}, {2.0, 3.0}); - - cadet::model::CSTRModel* const cstr = createAndConfigureCSTR(mb, jpp); - cstr->setFlowRates(1.0, 1.0); - cstr->setSensitiveParameter(cadet::makeParamId("INIT_VOLUME", 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), 0, 1.0); - cstr->setSensitiveParameter(cadet::makeParamId("LIN_KA", 0, 0, cadet::ParTypeIndep, 0, cadet::ReactionIndep, cadet::SectionIndep), 1, 1.0); - cstr->setSensitiveParameter(cadet::makeParamId("POROSITY", 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), 2, 1.0); - - return cstr; - }, y.data(), yDot.data(), 1e-14); -} - -TEST_CASE("StirredTankModel consistent sensitivity initialization with SMA binding", "[CSTR],[ConsistentInit],[Sensitivity]") + checkSensitivityConsistentInitialization( + [](cadet::IModelBuilder& mb, bool dynamic) -> cadet::model::CSTRModel* { + cadet::JsonParameterProvider jpp = createCSTR(2); + cadet::test::addBoundStates(jpp, {1, 1}, 0.5); + cadet::test::addLinearBindingModel(jpp, dynamic, {5.0, 4.0}, {2.0, 3.0}); + + cadet::model::CSTRModel* const cstr = createAndConfigureCSTR(mb, jpp); + cstr->setFlowRates(1.0, 1.0); + cstr->setSensitiveParameter(cadet::makeParamId("INIT_VOLUME", 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, + cadet::SectionIndep), + 0, 1.0); + cstr->setSensitiveParameter( + cadet::makeParamId("LIN_KA", 0, 0, cadet::ParTypeIndep, 0, cadet::ReactionIndep, cadet::SectionIndep), + 1, 1.0); + cstr->setSensitiveParameter(cadet::makeParamId("POROSITY", 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, + cadet::SectionIndep), + 2, 1.0); + + return cstr; + }, + y.data(), yDot.data(), 1e-14); +} + +TEST_CASE("StirredTankModel consistent sensitivity initialization with SMA binding", + "[CSTR],[ConsistentInit],[Sensitivity]") { // Fill state vector with initial values std::vector y(4 + 2 * 4 + 1, 0.0); @@ -549,25 +522,37 @@ TEST_CASE("StirredTankModel consistent sensitivity initialization with SMA bindi std::copy(bindingCell, bindingCell + 8, y.data() + 4); y[4 + 8] = 1.0; - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, yDot.size()); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, yDot.size()); // Note that checkSensitivityConsistentInitialization() applies non-zero initial values for the whole // sensitivity state vector. Hence, this is more strict than usual as most initial sensitivity state // vectors are all zero (only sensitivities wrt. initial conditions produce non-zero initial values). - checkSensitivityConsistentInitialization([](cadet::IModelBuilder& mb, bool dynamic) -> cadet::model::CSTRModel* { - cadet::JsonParameterProvider jpp = createCSTR(4); - cadet::test::addBoundStates(jpp, {1, 1, 1, 1}, 0.5); - cadet::test::addSMABindingModel(jpp, dynamic, 1.2e3, {0.0, 35.5, 1.59, 7.7}, {0.0, 1000.0, 1000.0, 1000.0}, {0.0, 4.7, 5.29, 3.7}, {0.0, 11.83, 10.6, 10.0}); - - cadet::model::CSTRModel* const cstr = createAndConfigureCSTR(mb, jpp); - cstr->setFlowRates(1.0, 1.0); - cstr->setSensitiveParameter(cadet::makeParamId("INIT_VOLUME", 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), 0, 1.0); - cstr->setSensitiveParameter(cadet::makeParamId("SMA_NU", 0, 1, cadet::ParTypeIndep, 0, cadet::ReactionIndep, cadet::SectionIndep), 1, 1.0); - cstr->setSensitiveParameter(cadet::makeParamId("POROSITY", 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), 2, 1.0); - - return cstr; - }, y.data(), yDot.data(), 1e-8); + checkSensitivityConsistentInitialization( + [](cadet::IModelBuilder& mb, bool dynamic) -> cadet::model::CSTRModel* { + cadet::JsonParameterProvider jpp = createCSTR(4); + cadet::test::addBoundStates(jpp, {1, 1, 1, 1}, 0.5); + cadet::test::addSMABindingModel(jpp, dynamic, 1.2e3, {0.0, 35.5, 1.59, 7.7}, {0.0, 1000.0, 1000.0, 1000.0}, + {0.0, 4.7, 5.29, 3.7}, {0.0, 11.83, 10.6, 10.0}); + + cadet::model::CSTRModel* const cstr = createAndConfigureCSTR(mb, jpp); + cstr->setFlowRates(1.0, 1.0); + cstr->setSensitiveParameter(cadet::makeParamId("INIT_VOLUME", 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, + cadet::SectionIndep), + 0, 1.0); + cstr->setSensitiveParameter( + cadet::makeParamId("SMA_NU", 0, 1, cadet::ParTypeIndep, 0, cadet::ReactionIndep, cadet::SectionIndep), + 1, 1.0); + cstr->setSensitiveParameter(cadet::makeParamId("POROSITY", 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, + cadet::SectionIndep), + 2, 1.0); + + return cstr; + }, + y.data(), yDot.data(), 1e-8); } TEST_CASE("StirredTankModel inlet DOF Jacobian", "[CSTR],[UnitOp],[Jacobian],[Inlet]") @@ -595,7 +580,8 @@ TEST_CASE("CSTR multiple particle types Jacobian analytic vs AD", "[CSTR],[Jacob cadet::test::particle::testJacobianMixedParticleTypes(jpp); } -TEST_CASE("CSTR multiple particle types time derivative Jacobian vs FD", "[CSTR],[UnitOp],[Residual],[Jacobian],[ParticleType]") +TEST_CASE("CSTR multiple particle types time derivative Jacobian vs FD", + "[CSTR],[UnitOp],[Residual],[Jacobian],[ParticleType]") { cadet::JsonParameterProvider jpp = createTwoComponentLinearTestCase(); cadet::test::particle::testTimeDerivativeJacobianMixedParticleTypesFD(jpp, 1e-6, 0.0, 9e-4); @@ -637,85 +623,99 @@ TEST_CASE("CSTR dynamic reactions time derivative Jacobian vs FD bulk", "[CSTR], cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, false, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("CSTR dynamic reactions time derivative Jacobian vs FD particle", "[CSTR],[Jacobian],[Residual],[ReactionModel]") +TEST_CASE("CSTR dynamic reactions time derivative Jacobian vs FD particle", + "[CSTR],[Jacobian],[Residual],[ReactionModel]") { cadet::JsonParameterProvider jpp = createTwoComponentLinearTestCase(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("CSTR dynamic reactions time derivative Jacobian vs FD modified particle", "[CSTR],[Jacobian],[Residual],[ReactionModel]") +TEST_CASE("CSTR dynamic reactions time derivative Jacobian vs FD modified particle", + "[CSTR],[Jacobian],[Residual],[ReactionModel]") { cadet::JsonParameterProvider jpp = createTwoComponentLinearTestCase(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, true, 1e-6, 1e-14, 8e-4); } -TEST_CASE("CSTR dynamic reactions time derivative Jacobian vs FD bulk and particle", "[CSTR],[Jacobian],[Residual],[ReactionModel]") +TEST_CASE("CSTR dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[CSTR],[Jacobian],[Residual],[ReactionModel]") { cadet::JsonParameterProvider jpp = createTwoComponentLinearTestCase(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("CSTR dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[CSTR],[Jacobian],[Residual],[ReactionModel]") +TEST_CASE("CSTR dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[CSTR],[Jacobian],[Residual],[ReactionModel]") { cadet::JsonParameterProvider jpp = createTwoComponentLinearTestCase(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, true, 1e-6, 1e-14, 8e-4); } -TEST_CASE("CSTR multi particle types dynamic reactions Jacobian vs AD bulk", "[CSTR],[Jacobian],[AD],[ReactionModel],[ParticleType]") +TEST_CASE("CSTR multi particle types dynamic reactions Jacobian vs AD bulk", + "[CSTR],[Jacobian],[AD],[ReactionModel],[ParticleType]") { cadet::JsonParameterProvider jpp = createTwoComponentLinearThreeParticleTypesTestCase(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, false, false); } -TEST_CASE("CSTR multi particle types dynamic reactions Jacobian vs AD particle", "[CSTR],[Jacobian],[AD],[ReactionModel],[ParticleType]") +TEST_CASE("CSTR multi particle types dynamic reactions Jacobian vs AD particle", + "[CSTR],[Jacobian],[AD],[ReactionModel],[ParticleType]") { cadet::JsonParameterProvider jpp = createTwoComponentLinearThreeParticleTypesTestCase(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, false, true, false); } -TEST_CASE("CSTR multi particle types dynamic reactions Jacobian vs AD modified particle", "[CSTR],[Jacobian],[AD],[ReactionModel],[ParticleType]") +TEST_CASE("CSTR multi particle types dynamic reactions Jacobian vs AD modified particle", + "[CSTR],[Jacobian],[AD],[ReactionModel],[ParticleType]") { cadet::JsonParameterProvider jpp = createTwoComponentLinearThreeParticleTypesTestCase(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, false, true, true); } -TEST_CASE("CSTR multi particle types dynamic reactions Jacobian vs AD bulk and particle", "[CSTR],[Jacobian],[AD],[ReactionModel],[ParticleType]") +TEST_CASE("CSTR multi particle types dynamic reactions Jacobian vs AD bulk and particle", + "[CSTR],[Jacobian],[AD],[ReactionModel],[ParticleType]") { cadet::JsonParameterProvider jpp = createTwoComponentLinearThreeParticleTypesTestCase(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, true, false); } -TEST_CASE("CSTR multi particle types dynamic reactions Jacobian vs AD bulk and modified particle", "[CSTR],[Jacobian],[AD],[ReactionModel],[ParticleType]") +TEST_CASE("CSTR multi particle types dynamic reactions Jacobian vs AD bulk and modified particle", + "[CSTR],[Jacobian],[AD],[ReactionModel],[ParticleType]") { cadet::JsonParameterProvider jpp = createTwoComponentLinearThreeParticleTypesTestCase(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, true, true); } -TEST_CASE("CSTR multi particle types dynamic reactions time derivative Jacobian vs FD bulk", "[CSTR],[Jacobian],[Residual],[ReactionModel],[ParticleType]") +TEST_CASE("CSTR multi particle types dynamic reactions time derivative Jacobian vs FD bulk", + "[CSTR],[Jacobian],[Residual],[ReactionModel],[ParticleType]") { cadet::JsonParameterProvider jpp = createTwoComponentLinearThreeParticleTypesTestCase(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, false, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("CSTR multi particle types dynamic reactions time derivative Jacobian vs FD particle", "[CSTR],[Jacobian],[Residual],[ReactionModel],[ParticleType]") +TEST_CASE("CSTR multi particle types dynamic reactions time derivative Jacobian vs FD particle", + "[CSTR],[Jacobian],[Residual],[ReactionModel],[ParticleType]") { cadet::JsonParameterProvider jpp = createTwoComponentLinearThreeParticleTypesTestCase(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("CSTR multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", "[CSTR],[Jacobian],[Residual],[ReactionModel],[ParticleType]") +TEST_CASE("CSTR multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", + "[CSTR],[Jacobian],[Residual],[ReactionModel],[ParticleType]") { cadet::JsonParameterProvider jpp = createTwoComponentLinearThreeParticleTypesTestCase(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, true, 1e-6, 1e-14, 8e-4); } -TEST_CASE("CSTR multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", "[CSTR],[Jacobian],[Residual],[ReactionModel],[ParticleType]") +TEST_CASE("CSTR multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[CSTR],[Jacobian],[Residual],[ReactionModel],[ParticleType]") { cadet::JsonParameterProvider jpp = createTwoComponentLinearThreeParticleTypesTestCase(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("CSTR multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[CSTR],[Jacobian],[Residual],[ReactionModel],[ParticleType]") +TEST_CASE("CSTR multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[CSTR],[Jacobian],[Residual],[ReactionModel],[ParticleType]") { cadet::JsonParameterProvider jpp = createTwoComponentLinearThreeParticleTypesTestCase(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, true, 1e-6, 1e-14, 8e-4); diff --git a/test/CSTR-Simulation.cpp b/test/CSTR-Simulation.cpp index 092f08b0b..24d6399d8 100644 --- a/test/CSTR-Simulation.cpp +++ b/test/CSTR-Simulation.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -61,7 +61,8 @@ inline cadet::JsonParameterProvider createMultiParticleTypesTestCase() return jpp; } -inline void runSim(cadet::JsonParameterProvider& jpp, std::function solC, std::function solV) +inline void runSim(cadet::JsonParameterProvider& jpp, std::function solC, + std::function solV) { // Run simulation cadet::Driver drv; @@ -84,7 +85,9 @@ inline void runSim(cadet::JsonParameterProvider& jpp, std::function solC, std::function solQ, std::function solV, double absTol = 4e-5, double relTol = 1e-6) +inline void runSim(cadet::JsonParameterProvider& jpp, std::function solC, + std::function solQ, std::function solV, double absTol = 4e-5, + double relTol = 1e-6) { // Run simulation cadet::Driver drv; @@ -108,7 +111,8 @@ inline void runSim(cadet::JsonParameterProvider& jpp, std::function solC, std::function solV, double absTol = 1e-5, double relTol = 1e-8) +inline void runSensSim(cadet::JsonParameterProvider& jpp, std::function solC, + std::function solV, double absTol = 1e-5, double relTol = 1e-8) { // Run simulation cadet::Driver drv; @@ -146,17 +150,17 @@ TEST_CASE("CSTR vs analytic solution (V constant) w/o binding model", "[CSTR],[S const double temp = 10.0 * (9.0 + 2.0 * std::sqrt(std::exp(1.0))); const double temp2 = 2.0 / 9.0 * (-9.0 - 2.0 * std::sqrt(std::exp(1.0)) + 2 * std::exp(5)); - runSim(jpp, [=](double t) { + runSim( + jpp, + [=](double t) { if (t <= 10.0) return -2.0 * std::expm1(-t / 20.0); else if (t <= 100.0) - return (120.0 - temp * std::exp(-t / 20.0) - t) / 45.0; + return (120.0 - temp * std::exp(-t / 20.0) - t) / 45.0; else return std::exp(-5.0 - (t - 100.0) / 20.0) * temp2; - }, - [](double t) { - return 10.0; - }); + }, + [](double t) { return 10.0; }); } TEST_CASE("CSTR vs analytic solution (V increasing) w/o binding model", "[CSTR],[Simulation]") @@ -167,12 +171,9 @@ TEST_CASE("CSTR vs analytic solution (V increasing) w/o binding model", "[CSTR], cadet::test::setInletProfile(jpp, 0, 0, 1.0, 0.0, 0.0, 0.0); cadet::test::setFlowRates(jpp, 0, 2.0, 1.0, 0.5); - runSim(jpp, [=](double t) { - return 4.0 * (6000.0 + t * (1200.0 + t * (60.0 + t))) / (3.0 * std::pow(20.0 + t, 3.0)); - }, - [](double t) { - return 10.0 + 0.5 * t; - }); + runSim( + jpp, [=](double t) { return 4.0 * (6000.0 + t * (1200.0 + t * (60.0 + t))) / (3.0 * std::pow(20.0 + t, 3.0)); }, + [](double t) { return 10.0 + 0.5 * t; }); } TEST_CASE("CSTR vs analytic solution (V decreasing) w/o binding model", "[CSTR],[Simulation]") @@ -183,12 +184,7 @@ TEST_CASE("CSTR vs analytic solution (V decreasing) w/o binding model", "[CSTR], cadet::test::setInletProfile(jpp, 0, 0, 1.0, 0.0, 0.0, 0.0); cadet::test::setFlowRates(jpp, 0, 1.5, 1.5, 0.5); - runSim(jpp, [=](double t) { - return 1.0 + t * (1.0 / 20.0 - t / 800.0); - }, - [](double t) { - return 10.0 - 0.5 * t; - }); + runSim(jpp, [=](double t) { return 1.0 + t * (1.0 / 20.0 - t / 800.0); }, [](double t) { return 10.0 - 0.5 * t; }); } TEST_CASE("CSTR vs analytic solution (V constant) with dynamic linear binding", "[CSTR],[Simulation]") @@ -202,15 +198,21 @@ TEST_CASE("CSTR vs analytic solution (V constant) with dynamic linear binding", cadet::test::addLinearBindingModel(jpp, true, {0.1}, {10.0}); const double sqrt2501 = std::sqrt(2501.0); - runSim(jpp, [=](double t) { - return 1.0 - std::exp(-5.1 * t) * (2501.0 * std::cosh(sqrt2501 * t / 10.0) + 50.0 * sqrt2501 * std::sinh(sqrt2501 * t / 10.0)) / 2501.0; - }, + runSim( + jpp, + [=](double t) { + return 1.0 - + std::exp(-5.1 * t) * + (2501.0 * std::cosh(sqrt2501 * t / 10.0) + 50.0 * sqrt2501 * std::sinh(sqrt2501 * t / 10.0)) / + 2501.0; + }, [=](double t) { - return 0.01 - std::exp(-5.1 * t) * (2501.0 * std::cosh(sqrt2501 * t / 10.0) + 51.0 * sqrt2501 * std::sinh(sqrt2501 * t / 10.0)) / 250100.0; - }, - [](double t) { - return 1.0; - }); + return 0.01 - + std::exp(-5.1 * t) * + (2501.0 * std::cosh(sqrt2501 * t / 10.0) + 51.0 * sqrt2501 * std::sinh(sqrt2501 * t / 10.0)) / + 250100.0; + }, + [](double t) { return 1.0; }); } TEST_CASE("CSTR vs analytic solution (V constant) with quasi-stationary linear binding", "[CSTR],[Simulation]") @@ -224,18 +226,13 @@ TEST_CASE("CSTR vs analytic solution (V constant) with quasi-stationary linear b cadet::test::addLinearBindingModel(jpp, false, {0.1}, {10.0}); const double sqrt2501 = std::sqrt(2501.0); - runSim(jpp, [=](double t) { - return -std::expm1(-10.0 / 101.0 * t); - }, - [=](double t) { - return -std::expm1(-10.0 / 101.0 * t) * 0.01; - }, - [](double t) { - return 1.0; - }); + runSim( + jpp, [=](double t) { return -std::expm1(-10.0 / 101.0 * t); }, + [=](double t) { return -std::expm1(-10.0 / 101.0 * t) * 0.01; }, [](double t) { return 1.0; }); } -TEST_CASE("CSTR filter flowrate sensitivity vs analytic solution (V constant) w/o binding model", "[CSTR],[Simulation],[AD],[Sensitivity]") +TEST_CASE("CSTR filter flowrate sensitivity vs analytic solution (V constant) w/o binding model", + "[CSTR],[Simulation],[AD],[Sensitivity]") { cadet::JsonParameterProvider jpp = createCSTRBenchmark(3, 119.0, 1.0); cadet::test::setSectionTimes(jpp, {0.0, 10.0, 100.0, 119.0}); @@ -247,37 +244,52 @@ TEST_CASE("CSTR filter flowrate sensitivity vs analytic solution (V constant) w/ cadet::test::setFlowRates(jpp, 1, 1.0, 0.5, 0.5); cadet::test::setFlowRates(jpp, 2, 1.0, 0.5, 0.5); setFlowRateFilter(jpp, 0.5); - cadet::test::addSensitivity(jpp, "FLOWRATE_FILTER", cadet::makeParamId("FLOWRATE_FILTER", 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), 1e-6); + cadet::test::addSensitivity(jpp, "FLOWRATE_FILTER", + cadet::makeParamId("FLOWRATE_FILTER", 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), + 1e-6); cadet::test::returnSensitivities(jpp, 0); const double sqrtE = std::sqrt(std::exp(1.0)); - runSensSim(jpp, [=](double t) { + runSensSim( + jpp, + [=](double t) { if (t <= 10.0) - return 4.0 + 1.0/200.0 * std::exp(-t / 20.0) * (-800.0 + (-40.0 + t) * t); + return 4.0 + 1.0 / 200.0 * std::exp(-t / 20.0) * (-800.0 + (-40.0 + t) * t); else if (t <= 100.0) - return -160.0 * (-80.0 + t) / 1800.0 + std::exp(-t / 20.0) * (-4.0 + (9.0 * (-40.0 + t) * t + 2 * sqrtE * (-1700.0 + (-40.0 + t) * t)) / 1800.0); + return -160.0 * (-80.0 + t) / 1800.0 + + std::exp(-t / 20.0) * + (-4.0 + (9.0 * (-40.0 + t) * t + 2 * sqrtE * (-1700.0 + (-40.0 + t) * t)) / 1800.0); else - return std::exp(-t / 20.0) * (-4.0 + (9.0 * (-40.0 + t) * t + std::exp(5.0) * (8800.0 - 2.0 * (-40.0 + t) * t) + 2.0 * sqrtE * (-1700.0 + (-40.0 + t) * t)) / 1800.0); - }, - [](double t) { - return -t; - }); + return std::exp(-t / 20.0) * + (-4.0 + (9.0 * (-40.0 + t) * t + std::exp(5.0) * (8800.0 - 2.0 * (-40.0 + t) * t) + + 2.0 * sqrtE * (-1700.0 + (-40.0 + t) * t)) / + 1800.0); + }, + [](double t) { return -t; }); } -TEST_CASE("CSTR LIN_COEFF sensitivity vs analytic solution (V constant) w/o binding model", "[CSTR],[Simulation],[AD],[Sensitivity]") +TEST_CASE("CSTR LIN_COEFF sensitivity vs analytic solution (V constant) w/o binding model", + "[CSTR],[Simulation],[AD],[Sensitivity]") { cadet::JsonParameterProvider jpp = createCSTRBenchmark(1, 100.0, 1.0); cadet::test::setSectionTimes(jpp, {0.0, 100.0}); cadet::test::setInitialConditions(jpp, {0.0}, {}, 10.0); cadet::test::setInletProfile(jpp, 0, 0, 1.0, 1.0, 0.0, 0.0); cadet::test::setFlowRates(jpp, 0, 1.0, 0.5, 0.5); - cadet::test::addSensitivity(jpp, "LIN_COEFF", cadet::makeParamId("LIN_COEFF", 1, 0, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, 0), 1e-6); + cadet::test::addSensitivity( + jpp, "LIN_COEFF", + cadet::makeParamId("LIN_COEFF", 1, 0, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, 0), + 1e-6); cadet::test::returnSensitivities(jpp, 0); - runSensSim(jpp, [=](double t) { return 2.0 * (20.0 * std::expm1(-t / 20.0) + t); }, [](double t) { return 0.0; }, 2e-5, 6e-7); + runSensSim( + jpp, [=](double t) { return 2.0 * (20.0 * std::expm1(-t / 20.0) + t); }, [](double t) { return 0.0; }, 2e-5, + 6e-7); } -TEST_CASE("CSTR initial volume sensitivity vs analytic solution (V constant) w/o binding model", "[CSTR],[Simulation],[Sensitivity]") +TEST_CASE("CSTR initial volume sensitivity vs analytic solution (V constant) w/o binding model", + "[CSTR],[Simulation],[Sensitivity]") { const double V = 5.0; @@ -287,20 +299,23 @@ TEST_CASE("CSTR initial volume sensitivity vs analytic solution (V constant) w/o cadet::test::setInletProfile(jpp, 0, 0, 1.0, 0.0, 0.0, 0.0); cadet::test::setInletProfile(jpp, 1, 0, 0.0, 0.0, 0.0, 0.0); cadet::test::setFlowRates(jpp, 0, 1.0, 1.0, 0.0); - cadet::test::addSensitivity(jpp, "INIT_VOLUME", cadet::makeParamId("INIT_VOLUME", 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), 1e-6); + cadet::test::addSensitivity(jpp, "INIT_VOLUME", + cadet::makeParamId("INIT_VOLUME", 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), + 1e-6); cadet::test::returnSensitivities(jpp, 0); const double invV2 = 1.0 / (V * V); const double e50overV = std::exp(50.0 / V); - runSensSim(jpp, [=](double t) { + runSensSim( + jpp, + [=](double t) { if (t <= 50.0) return -std::exp(-t / V) * t * invV2; else return std::exp(-t / V) * invV2 * (e50overV * (t - 50.0) - t); - }, - [](double t) { - return 1.0; - }); + }, + [](double t) { return 1.0; }); } TEST_CASE("CSTR initial condition read and apply correctly", "[CSTR],[InitialConditions]") @@ -424,20 +439,33 @@ TEST_CASE("CSTR initial condition behave like standard parameters", "[CSTR],[Ini CHECK(vecStateY[7] == 6.0); // Get parameter values - CHECK(uo->getParameterDouble(cadet::makeParamId("INIT_C", 0, 0, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep)) == 1.0); - CHECK(uo->getParameterDouble(cadet::makeParamId("INIT_C", 0, 1, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep)) == 2.0); - CHECK(uo->getParameterDouble(cadet::makeParamId("INIT_Q", 0, 0, 0, 0, cadet::ReactionIndep, cadet::SectionIndep)) == 3.0); - CHECK(uo->getParameterDouble(cadet::makeParamId("INIT_Q", 0, 1, 0, 0, cadet::ReactionIndep, cadet::SectionIndep)) == 4.0); - CHECK(uo->getParameterDouble(cadet::makeParamId("INIT_Q", 0, 1, 0, 1, cadet::ReactionIndep, cadet::SectionIndep)) == 5.0); - CHECK(uo->getParameterDouble(cadet::makeParamId("INIT_VOLUME", 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep)) == 6.0); + CHECK(uo->getParameterDouble(cadet::makeParamId("INIT_C", 0, 0, cadet::ParTypeIndep, cadet::BoundStateIndep, + cadet::ReactionIndep, cadet::SectionIndep)) == 1.0); + CHECK(uo->getParameterDouble(cadet::makeParamId("INIT_C", 0, 1, cadet::ParTypeIndep, cadet::BoundStateIndep, + cadet::ReactionIndep, cadet::SectionIndep)) == 2.0); + CHECK(uo->getParameterDouble(cadet::makeParamId("INIT_Q", 0, 0, 0, 0, cadet::ReactionIndep, cadet::SectionIndep)) == + 3.0); + CHECK(uo->getParameterDouble(cadet::makeParamId("INIT_Q", 0, 1, 0, 0, cadet::ReactionIndep, cadet::SectionIndep)) == + 4.0); + CHECK(uo->getParameterDouble(cadet::makeParamId("INIT_Q", 0, 1, 0, 1, cadet::ReactionIndep, cadet::SectionIndep)) == + 5.0); + CHECK(uo->getParameterDouble(cadet::makeParamId("INIT_VOLUME", 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, + cadet::SectionIndep)) == 6.0); // Set parameter values - uo->setParameter(cadet::makeParamId("INIT_C", 0, 0, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), -1.0); - uo->setParameter(cadet::makeParamId("INIT_C", 0, 1, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), -2.0); + uo->setParameter(cadet::makeParamId("INIT_C", 0, 0, cadet::ParTypeIndep, cadet::BoundStateIndep, + cadet::ReactionIndep, cadet::SectionIndep), + -1.0); + uo->setParameter(cadet::makeParamId("INIT_C", 0, 1, cadet::ParTypeIndep, cadet::BoundStateIndep, + cadet::ReactionIndep, cadet::SectionIndep), + -2.0); uo->setParameter(cadet::makeParamId("INIT_Q", 0, 0, 0, 0, cadet::ReactionIndep, cadet::SectionIndep), -3.0); uo->setParameter(cadet::makeParamId("INIT_Q", 0, 1, 0, 0, cadet::ReactionIndep, cadet::SectionIndep), -4.0); uo->setParameter(cadet::makeParamId("INIT_Q", 0, 1, 0, 1, cadet::ReactionIndep, cadet::SectionIndep), -5.0); - uo->setParameter(cadet::makeParamId("INIT_VOLUME", 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), -6.0); + uo->setParameter(cadet::makeParamId("INIT_VOLUME", 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, + cadet::ReactionIndep, cadet::SectionIndep), + -6.0); // Apply initial conditions to state vector std::fill(vecStateY.begin(), vecStateY.end(), 0.0); diff --git a/test/CellKernelTests.cpp b/test/CellKernelTests.cpp index cb0097408..71c8d2515 100644 --- a/test/CellKernelTests.cpp +++ b/test/CellKernelTests.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -34,35 +34,37 @@ namespace { - inline void recoverTimeDerivativeMatrixFromColumns(const std::function& multiplier, cadet::linalg::DenseMatrix& mat, double* vec, double* rhs) +inline void recoverTimeDerivativeMatrixFromColumns(const std::function& multiplier, + cadet::linalg::DenseMatrix& mat, double* vec, double* rhs) +{ + std::fill_n(vec, mat.columns(), 0.0); + for (int i = 0; i < mat.columns(); ++i) { - std::fill_n(vec, mat.columns(), 0.0); - for (int i = 0; i < mat.columns(); ++i) - { - vec[i] = 1.0; + vec[i] = 1.0; - multiplier(vec, rhs); + multiplier(vec, rhs); - for (int j = 0; j < mat.rows(); ++j) - mat.native(j, i) = rhs[j]; + for (int j = 0; j < mat.rows(); ++j) + mat.native(j, i) = rhs[j]; - vec[i] = 0.0; - } + vec[i] = 0.0; } +} - inline void compareMatrix(const cadet::linalg::DenseMatrix& matA, const cadet::linalg::DenseMatrix& matB, double relTol, double absTol) +inline void compareMatrix(const cadet::linalg::DenseMatrix& matA, const cadet::linalg::DenseMatrix& matB, double relTol, + double absTol) +{ + for (int row = 0; row < matA.rows(); ++row) { - for (int row = 0; row < matA.rows(); ++row) + for (int col = 0; col < matA.columns(); ++col) { - for (int col = 0; col < matA.columns(); ++col) - { - CAPTURE(row); - CAPTURE(col); - CHECK(matA.native(row, col) == cadet::test::makeApprox(matB.native(row, col), relTol, absTol)); - } + CAPTURE(row); + CAPTURE(col); + CHECK(matA.native(row, col) == cadet::test::makeApprox(matB.native(row, col), relTol, absTol)); } } } +} // namespace TEST_CASE("CellKernel time derivative Jacobian multiplier vs matrix", "[CellKernel],[Jacobian]") { @@ -72,7 +74,7 @@ TEST_CASE("CellKernel time derivative Jacobian multiplier vs matrix", "[CellKern const unsigned int nBound[] = {1, 0, 2, 1, 1}; const unsigned int boundOffset[] = {0, 1, 1, 3, 4}; const unsigned int nTotalBound = std::accumulate(nBound, nBound + nComp, 0u); - + std::vector poreAccessFactor(nComp, 1.0); std::vector y(nComp + nTotalBound, 0.0); std::vector res(nComp + nTotalBound, 0.0); @@ -83,21 +85,22 @@ TEST_CASE("CellKernel time derivative Jacobian multiplier vs matrix", "[CellKern cadet::linalg::DenseMatrix matDirect; matDirect.resize(nComp + nTotalBound, nComp + nTotalBound); - const std::function testFun = [=, &matMult, &matDirect, &poreAccessFactor, &y, &res](int const* qsReaction) - { - recoverTimeDerivativeMatrixFromColumns([=](double* a, double* b) - { - cadet::model::parts::cell::multiplyWithDerivativeJacobianKernel(a, b, nComp, - nBound, boundOffset, nTotalBound, qsReaction, 1.0, 3.0); - }, - matMult, y.data(), res.data() - ); + const std::function testFun = [=, &matMult, &matDirect, &poreAccessFactor, &y, + &res](int const* qsReaction) { + recoverTimeDerivativeMatrixFromColumns( + [=](double* a, double* b) { + cadet::model::parts::cell::multiplyWithDerivativeJacobianKernel( + a, b, nComp, nBound, boundOffset, nTotalBound, qsReaction, 1.0, 3.0); + }, + matMult, y.data(), res.data()); - cadet::linalg::DenseMatrix::RowIterator jac = matDirect.row(0); - cadet::model::parts::cell::addTimeDerivativeToJacobianParticleShell(jac, 1.0, 0.25, nComp, nBound, poreAccessFactor.data(), nTotalBound, boundOffset, qsReaction); + cadet::linalg::DenseMatrix::RowIterator jac = matDirect.row(0); + cadet::model::parts::cell::addTimeDerivativeToJacobianParticleShell( + jac, 1.0, 0.25, nComp, nBound, poreAccessFactor.data(), nTotalBound, boundOffset, qsReaction); - compareMatrix(matMult, matDirect, relTol, absTol); - }; + compareMatrix(matMult, matDirect, relTol, absTol); + }; SECTION("Multi bound state is quasi-stationary") { @@ -139,11 +142,7 @@ TEST_CASE("CellKernel time derivative Jacobian analytic vs FD", "[CellKernel],[J std::vector poreAccessFactor(nComp, 1.0); const cadet::active porosity = 0.25; - const int qsReaction[] = {0, 1, 1, 1, 0, - 1, 0, 0, 0, 1, - 1, 0, 1, 0, 1, - 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0}; + const int qsReaction[] = {0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}; for (int mode = 0; mode < 5; ++mode) { @@ -158,8 +157,7 @@ TEST_CASE("CellKernel time derivative Jacobian analytic vs FD", "[CellKernel],[J "MSSMA_NU": [0.0, 1.5, 2.0, 1.9, 2.3], "MSSMA_SIGMA": [0.0, 2.83, 3.6, 5.8, 6.5], "MSSMA_RATES": [0.0, 0.9, 0.8, 1.2, 1.1, 1.4, 1.3], - "MSSMA_LAMBDA": 100.0})json" - ); + "MSSMA_LAMBDA": 100.0})json"); // Obtain memory for state, Jacobian multiply direction, Jacobian column const unsigned int nDof = nComp + nTotalBound; @@ -170,35 +168,30 @@ TEST_CASE("CellKernel time derivative Jacobian analytic vs FD", "[CellKernel],[J std::vector jacCol2(nDof, 0.0); // Fill state vectors with some values - cadet::test::util::populate(y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); - cadet::test::util::populate(yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); y[nComp] = 90.0; const cadet::ColumnPosition colPos{0.0, 0.0, 0.0}; - const cadet::model::parts::cell::CellParameters params - { - nComp, - nBound, - cbm.boundOffset(), - nTotalBound, - localQSreaction, - porosity, - poreAccessFactor.data(), - &cbm.model(), - nullptr - }; + const cadet::model::parts::cell::CellParameters params{ + nComp, nBound, cbm.boundOffset(), nTotalBound, + localQSreaction, porosity, poreAccessFactor.data(), &cbm.model(), + nullptr}; // Compare Jacobians cadet::test::compareJacobianFD( - [=, &cbm, &colPos, &y, ¶ms](double const* fx, double* fy) - { + [=, &cbm, &colPos, &y, ¶ms](double const* fx, double* fy) { cadet::linalg::DenseMatrix::RowIterator jac; - cadet::model::parts::cell::residualKernel(0.0, 0u, colPos, y.data(), fx, fy, jac, params, cbm.buffer()); + cadet::model::parts::cell::residualKernel( + 0.0, 0u, colPos, y.data(), fx, fy, jac, params, cbm.buffer()); }, - [=, &cbm](double const* fx, double* fy) - { - cadet::model::parts::cell::multiplyWithDerivativeJacobianKernel(fx, fy, nComp, - nBound, cbm.boundOffset(), nTotalBound, localQSreaction, 1.0, 3.0); + [=, &cbm](double const* fx, double* fy) { + cadet::model::parts::cell::multiplyWithDerivativeJacobianKernel( + fx, fy, nComp, nBound, cbm.boundOffset(), nTotalBound, localQSreaction, 1.0, 3.0); }, yDot.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), nDof, 1e-7, 1e-14, 1e-4); } @@ -216,11 +209,7 @@ TEST_CASE("CellKernel Jacobian analytic vs AD", "[CellKernel],[Jacobian],[AD]") std::vector poreAccessFactor(nComp, 1.0); const cadet::active porosity = 0.25; - const int qsReaction[] = {0, 1, 1, 1, 0, - 1, 0, 0, 0, 1, - 1, 0, 1, 0, 1, - 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0}; + const int qsReaction[] = {0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}; for (int mode = 0; mode < 5; ++mode) { @@ -235,8 +224,7 @@ TEST_CASE("CellKernel Jacobian analytic vs AD", "[CellKernel],[Jacobian],[AD]") "MSSMA_NU": [0.0, 1.5, 2.0, 1.9, 2.3], "MSSMA_SIGMA": [0.0, 2.83, 3.6, 5.8, 6.5], "MSSMA_RATES": [0.0, 0.9, 0.8, 1.2, 1.1, 1.4, 1.3], - "MSSMA_LAMBDA": 100.0})json" - ); + "MSSMA_LAMBDA": 100.0})json"); // Obtain memory for state, Jacobian multiply direction, Jacobian column const unsigned int nDof = nComp + nTotalBound; @@ -244,8 +232,10 @@ TEST_CASE("CellKernel Jacobian analytic vs AD", "[CellKernel],[Jacobian],[AD]") std::vector yDot(nDof, 0.0); // Fill state vectors with some values - cadet::test::util::populate(y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); - cadet::test::util::populate(yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); y[nComp] = 90.0; // Enable AD @@ -257,22 +247,17 @@ TEST_CASE("CellKernel Jacobian analytic vs AD", "[CellKernel],[Jacobian],[AD]") cadet::ad::prepareAdVectorSeedsForDenseMatrix(adY, 0u, nDof); const cadet::ColumnPosition colPos{0.0, 0.0, 0.0}; - const cadet::model::parts::cell::CellParameters params - { - nComp, - nBound, - cbm.boundOffset(), - nTotalBound, - localQSreaction, - porosity, - poreAccessFactor.data(), - &cbm.model(), - nullptr - }; + const cadet::model::parts::cell::CellParameters params{ + nComp, nBound, cbm.boundOffset(), nTotalBound, + localQSreaction, porosity, poreAccessFactor.data(), &cbm.model(), + nullptr}; // Calculate Jacobian via AD cadet::linalg::DenseMatrix::RowIterator jac; - cadet::model::parts::cell::residualKernel(0.0, 0u, colPos, adY, yDot.data(), adRes, jac, params, cbm.buffer()); + cadet::model::parts::cell::residualKernel( + 0.0, 0u, colPos, adY, yDot.data(), adRes, jac, params, cbm.buffer()); cadet::linalg::DenseMatrix matAd; matAd.resize(nDof, nDof); cadet::ad::extractDenseJacobianFromAd(adRes, 0u, matAd); @@ -282,7 +267,9 @@ TEST_CASE("CellKernel Jacobian analytic vs AD", "[CellKernel],[Jacobian],[AD]") cadet::linalg::DenseMatrix matAna; matAna.resize(nDof, nDof); jac = matAna.row(0); - cadet::model::parts::cell::residualKernel(0.0, 0u, colPos, y.data(), yDot.data(), res.data(), jac, params, cbm.buffer()); + cadet::model::parts::cell::residualKernel( + 0.0, 0u, colPos, y.data(), yDot.data(), res.data(), jac, params, cbm.buffer()); for (unsigned int i = 0; i < nDof; ++i) CHECK(res[i] == static_cast(adRes[i])); @@ -305,11 +292,7 @@ TEST_CASE("CellKernel time derivative Jacobian analytic vs FD with dummy reactio std::vector poreAccessFactor(nComp, 1.0); const cadet::active porosity = 0.25; - const int qsReaction[] = {0, 1, 1, 1, 0, - 1, 0, 0, 0, 1, - 1, 0, 1, 0, 1, - 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0}; + const int qsReaction[] = {0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}; for (int mode = 0; mode < 5; ++mode) { @@ -318,20 +301,19 @@ TEST_CASE("CellKernel time derivative Jacobian analytic vs FD with dummy reactio int const* const localQSreaction = qsReaction + nTotalBound * mode; cadet::test::binding::ConfiguredBindingModel cbm = cadet::test::binding::ConfiguredBindingModel::create( - "MULTISTATE_STERIC_MASS_ACTION", nComp, nBound, localQSreaction, - R"json({"MSSMA_KA": [0.0, 3.55, 1.59, 4.0, 5.0], + "MULTISTATE_STERIC_MASS_ACTION", nComp, nBound, localQSreaction, + R"json({"MSSMA_KA": [0.0, 3.55, 1.59, 4.0, 5.0], "MSSMA_KD": [0.0, 10.0, 10.0, 10.0, 10.0], "MSSMA_NU": [0.0, 1.5, 2.0, 1.9, 2.3], "MSSMA_SIGMA": [0.0, 2.83, 3.6, 5.8, 6.5], "MSSMA_RATES": [0.0, 0.9, 0.8, 1.2, 1.1, 1.4, 1.3], - "MSSMA_LAMBDA": 100.0})json" - ); + "MSSMA_LAMBDA": 100.0})json"); - cadet::test::reaction::ConfiguredDynamicReactionModel cdrm = cadet::test::reaction::ConfiguredDynamicReactionModel::create( - "NONE", nComp, nBound, R"json({})json" - ); + cadet::test::reaction::ConfiguredDynamicReactionModel cdrm = + cadet::test::reaction::ConfiguredDynamicReactionModel::create("NONE", nComp, nBound, R"json({})json"); - cbm.increaseBufferSize(nTotalBound * (nTotalBound + nComp + 1) * sizeof(cadet::active) + cdrm.requiredBufferSize()); + cbm.increaseBufferSize(nTotalBound * (nTotalBound + nComp + 1) * sizeof(cadet::active) + + cdrm.requiredBufferSize()); // Obtain memory for state, Jacobian multiply direction, Jacobian column const unsigned int nDof = nComp + nTotalBound; @@ -342,37 +324,32 @@ TEST_CASE("CellKernel time derivative Jacobian analytic vs FD with dummy reactio std::vector jacCol2(nDof, 0.0); // Fill state vectors with some values - cadet::test::util::populate(y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); - cadet::test::util::populate(yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); y[nComp] = 90.0; const cadet::ColumnPosition colPos{0.0, 0.0, 0.0}; - const cadet::model::parts::cell::CellParameters params - { - nComp, - nBound, - cbm.boundOffset(), - nTotalBound, - localQSreaction, - porosity, - poreAccessFactor.data(), - &cbm.model(), - &cdrm.model() - }; + const cadet::model::parts::cell::CellParameters params{ + nComp, nBound, cbm.boundOffset(), nTotalBound, + localQSreaction, porosity, poreAccessFactor.data(), &cbm.model(), + &cdrm.model()}; // Compare Jacobians cadet::test::compareJacobianFD( - [=, &cbm, &colPos, &y, ¶ms](double const* fx, double* fy) - { - cadet::linalg::DenseMatrix::RowIterator jac; - cadet::model::parts::cell::residualKernel(0.0, 0u, colPos, y.data(), fx, fy, jac, params, cbm.buffer()); - }, - [=, &cbm](double const* fx, double* fy) - { - cadet::model::parts::cell::multiplyWithDerivativeJacobianKernel(fx, fy, nComp, - nBound, cbm.boundOffset(), nTotalBound, localQSreaction, 1.0, 3.0); - }, - yDot.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), nDof, 1e-7, 1e-14, 1e-4); + [=, &cbm, &colPos, &y, ¶ms](double const* fx, double* fy) { + cadet::linalg::DenseMatrix::RowIterator jac; + cadet::model::parts::cell::residualKernel( + 0.0, 0u, colPos, y.data(), fx, fy, jac, params, cbm.buffer()); + }, + [=, &cbm](double const* fx, double* fy) { + cadet::model::parts::cell::multiplyWithDerivativeJacobianKernel( + fx, fy, nComp, nBound, cbm.boundOffset(), nTotalBound, localQSreaction, 1.0, 3.0); + }, + yDot.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), nDof, 1e-7, 1e-14, 1e-4); } } } @@ -388,11 +365,7 @@ TEST_CASE("CellKernel Jacobian analytic vs AD with dummy reaction", "[CellKernel std::vector poreAccessFactor(nComp, 1.0); const cadet::active porosity = 0.25; - const int qsReaction[] = {0, 1, 1, 1, 0, - 1, 0, 0, 0, 1, - 1, 0, 1, 0, 1, - 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0}; + const int qsReaction[] = {0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}; for (int mode = 0; mode < 5; ++mode) { @@ -401,20 +374,19 @@ TEST_CASE("CellKernel Jacobian analytic vs AD with dummy reaction", "[CellKernel int const* const localQSreaction = qsReaction + nTotalBound * mode; cadet::test::binding::ConfiguredBindingModel cbm = cadet::test::binding::ConfiguredBindingModel::create( - "MULTISTATE_STERIC_MASS_ACTION", nComp, nBound, localQSreaction, - R"json({"MSSMA_KA": [0.0, 3.55, 1.59, 4.0, 5.0], + "MULTISTATE_STERIC_MASS_ACTION", nComp, nBound, localQSreaction, + R"json({"MSSMA_KA": [0.0, 3.55, 1.59, 4.0, 5.0], "MSSMA_KD": [0.0, 10.0, 10.0, 10.0, 10.0], "MSSMA_NU": [0.0, 1.5, 2.0, 1.9, 2.3], "MSSMA_SIGMA": [0.0, 2.83, 3.6, 5.8, 6.5], "MSSMA_RATES": [0.0, 0.9, 0.8, 1.2, 1.1, 1.4, 1.3], - "MSSMA_LAMBDA": 100.0})json" - ); + "MSSMA_LAMBDA": 100.0})json"); - cadet::test::reaction::ConfiguredDynamicReactionModel cdrm = cadet::test::reaction::ConfiguredDynamicReactionModel::create( - "NONE", nComp, nBound, R"json({})json" - ); + cadet::test::reaction::ConfiguredDynamicReactionModel cdrm = + cadet::test::reaction::ConfiguredDynamicReactionModel::create("NONE", nComp, nBound, R"json({})json"); - cbm.increaseBufferSize(nTotalBound * (nTotalBound + nComp + 1) * sizeof(cadet::active) + cdrm.requiredBufferSize()); + cbm.increaseBufferSize(nTotalBound * (nTotalBound + nComp + 1) * sizeof(cadet::active) + + cdrm.requiredBufferSize()); // Obtain memory for state, Jacobian multiply direction, Jacobian column const unsigned int nDof = nComp + nTotalBound; @@ -422,8 +394,10 @@ TEST_CASE("CellKernel Jacobian analytic vs AD with dummy reaction", "[CellKernel std::vector yDot(nDof, 0.0); // Fill state vectors with some values - cadet::test::util::populate(y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); - cadet::test::util::populate(yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); y[nComp] = 90.0; // Enable AD @@ -435,22 +409,17 @@ TEST_CASE("CellKernel Jacobian analytic vs AD with dummy reaction", "[CellKernel cadet::ad::prepareAdVectorSeedsForDenseMatrix(adY, 0u, nDof); const cadet::ColumnPosition colPos{0.0, 0.0, 0.0}; - const cadet::model::parts::cell::CellParameters params - { - nComp, - nBound, - cbm.boundOffset(), - nTotalBound, - localQSreaction, - porosity, - poreAccessFactor.data(), - &cbm.model(), - &cdrm.model() - }; + const cadet::model::parts::cell::CellParameters params{ + nComp, nBound, cbm.boundOffset(), nTotalBound, + localQSreaction, porosity, poreAccessFactor.data(), &cbm.model(), + &cdrm.model()}; // Calculate Jacobian via AD cadet::linalg::DenseMatrix::RowIterator jac; - cadet::model::parts::cell::residualKernel(0.0, 0u, colPos, adY, yDot.data(), adRes, jac, params, cbm.buffer()); + cadet::model::parts::cell::residualKernel( + 0.0, 0u, colPos, adY, yDot.data(), adRes, jac, params, cbm.buffer()); cadet::linalg::DenseMatrix matAd; matAd.resize(nDof, nDof); cadet::ad::extractDenseJacobianFromAd(adRes, 0u, matAd); @@ -460,7 +429,9 @@ TEST_CASE("CellKernel Jacobian analytic vs AD with dummy reaction", "[CellKernel cadet::linalg::DenseMatrix matAna; matAna.resize(nDof, nDof); jac = matAna.row(0); - cadet::model::parts::cell::residualKernel(0.0, 0u, colPos, y.data(), yDot.data(), res.data(), jac, params, cbm.buffer()); + cadet::model::parts::cell::residualKernel( + 0.0, 0u, colPos, y.data(), yDot.data(), res.data(), jac, params, cbm.buffer()); for (unsigned int i = 0; i < nDof; ++i) CHECK(res[i] == static_cast(adRes[i])); diff --git a/test/ColumnTests.cpp b/test/ColumnTests.cpp index ebb390c66..6f45166bb 100644 --- a/test/ColumnTests.cpp +++ b/test/ColumnTests.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -47,85 +47,125 @@ const char* getTestDirectory(); namespace { - /** - * @brief Creates a runnable column model - * @details Creates a column model and configures it using the given IParameterProvider @p jpp. - * @param [in] mb ModelBuilder - * @param [in] jpp Configuration of the model - * @return Runnable column model - */ - inline cadet::IUnitOperation* createAndConfigureUnit(cadet::IModelBuilder& mb, cadet::JsonParameterProvider& jpp) - { - // Create a unit - cadet::IModel* const iUnit = mb.createUnitOperation(jpp, 0); - REQUIRE(nullptr != iUnit); - - cadet::IUnitOperation* const unit = reinterpret_cast(iUnit); - - // Configure - cadet::ModelBuilder& temp = *reinterpret_cast(&mb); - REQUIRE(unit->configureModelDiscretization(jpp, temp)); - REQUIRE(unit->configure(jpp)); - - // Do some checks - const unsigned int nComp = jpp.getInt("NCOMP"); - REQUIRE(unit->numComponents() == nComp); +/** + * @brief Creates a runnable column model + * @details Creates a column model and configures it using the given IParameterProvider @p jpp. + * @param [in] mb ModelBuilder + * @param [in] jpp Configuration of the model + * @return Runnable column model + */ +inline cadet::IUnitOperation* createAndConfigureUnit(cadet::IModelBuilder& mb, cadet::JsonParameterProvider& jpp) +{ + // Create a unit + cadet::IModel* const iUnit = mb.createUnitOperation(jpp, 0); + REQUIRE(nullptr != iUnit); + + cadet::IUnitOperation* const unit = reinterpret_cast(iUnit); + + // Configure + cadet::ModelBuilder& temp = *reinterpret_cast(&mb); + REQUIRE(unit->configureModelDiscretization(jpp, temp)); + REQUIRE(unit->configure(jpp)); + + // Do some checks + const unsigned int nComp = jpp.getInt("NCOMP"); + REQUIRE(unit->numComponents() == nComp); + + return unit; +} +/** + * @brief Creates a runnable column model with given discretization parameters + * @details Creates a column model and configures it using the given IParameterProvider @p jpp. + * @param [in] mb ModelBuilder + * @param [in] jpp Configuration of the model + * @param [in] disc discretization parameters + * @return Runnable column model + */ +inline cadet::IUnitOperation* createAndConfigureUnit(cadet::IModelBuilder& mb, cadet::JsonParameterProvider& jpp, + cadet::test::column::DiscParams& disc) +{ + // Set discretization parameters + disc.setDisc(jpp); + return createAndConfigureUnit(mb, jpp); +} - return unit; +class FluxOffsetExtractionRecorder : public cadet::ISolutionRecorder +{ +public: + FluxOffsetExtractionRecorder() : _fluxOffset(0) + { } - /** - * @brief Creates a runnable column model with given discretization parameters - * @details Creates a column model and configures it using the given IParameterProvider @p jpp. - * @param [in] mb ModelBuilder - * @param [in] jpp Configuration of the model - * @param [in] disc discretization parameters - * @return Runnable column model - */ - inline cadet::IUnitOperation* createAndConfigureUnit(cadet::IModelBuilder& mb, cadet::JsonParameterProvider& jpp, cadet::test::column::DiscParams& disc) + virtual void clear() { - // Set discretization parameters - disc.setDisc(jpp); - return createAndConfigureUnit(mb, jpp); - } - - class FluxOffsetExtractionRecorder : public cadet::ISolutionRecorder - { - public: - FluxOffsetExtractionRecorder() : _fluxOffset(0) { } - virtual void clear() { } - virtual void prepare(unsigned int numDofs, unsigned int numSens, unsigned int numTimesteps) { } - virtual void notifyIntegrationStart(unsigned int numDofs, unsigned int numSens, unsigned int numTimesteps) { } - virtual void beginTimestep(double t) { } - virtual void beginUnitOperation(cadet::UnitOpIdx idx, const cadet::IModel& model, const cadet::ISolutionExporter& exporter) { } - virtual void endUnitOperation() { } - virtual void endTimestep() { } - virtual void beginSolution() { } - virtual void endSolution() { } - virtual void beginSolutionDerivative() { } - virtual void endSolutionDerivative() { } - virtual void beginSensitivity(const cadet::ParameterId& pId, unsigned int sensIdx) { } - virtual void endSensitivity(const cadet::ParameterId& pId, unsigned int sensIdx) { } - virtual void beginSensitivityDerivative(const cadet::ParameterId& pId, unsigned int sensIdx) { } - virtual void endSensitivityDerivative(const cadet::ParameterId& pId, unsigned int sensIdx) { } - - virtual void unitOperationStructure(cadet::UnitOpIdx idx, const cadet::IModel& model, const cadet::ISolutionExporter& exporter) - { - _fluxOffset = exporter.numComponents() * exporter.numInletPorts() + exporter.numMobilePhaseDofs(); + } + virtual void prepare(unsigned int numDofs, unsigned int numSens, unsigned int numTimesteps) + { + } + virtual void notifyIntegrationStart(unsigned int numDofs, unsigned int numSens, unsigned int numTimesteps) + { + } + virtual void beginTimestep(double t) + { + } + virtual void beginUnitOperation(cadet::UnitOpIdx idx, const cadet::IModel& model, + const cadet::ISolutionExporter& exporter) + { + } + virtual void endUnitOperation() + { + } + virtual void endTimestep() + { + } + virtual void beginSolution() + { + } + virtual void endSolution() + { + } + virtual void beginSolutionDerivative() + { + } + virtual void endSolutionDerivative() + { + } + virtual void beginSensitivity(const cadet::ParameterId& pId, unsigned int sensIdx) + { + } + virtual void endSensitivity(const cadet::ParameterId& pId, unsigned int sensIdx) + { + } + virtual void beginSensitivityDerivative(const cadet::ParameterId& pId, unsigned int sensIdx) + { + } + virtual void endSensitivityDerivative(const cadet::ParameterId& pId, unsigned int sensIdx) + { + } - const unsigned int nParType = exporter.numParticleTypes(); - for (unsigned int i = 0; i < nParType; ++i) - _fluxOffset += exporter.numParticleMobilePhaseDofs(i) + exporter.numSolidPhaseDofs(i); + virtual void unitOperationStructure(cadet::UnitOpIdx idx, const cadet::IModel& model, + const cadet::ISolutionExporter& exporter) + { + _fluxOffset = exporter.numComponents() * exporter.numInletPorts() + exporter.numMobilePhaseDofs(); - // Make sure this is correct - const unsigned int nFluxes = exporter.numComponents() * exporter.numPrimaryCoordinates() * nParType * std::max(exporter.numSecondaryCoordinates(), 1u); - REQUIRE(reinterpret_cast(&model)->numDofs() - nFluxes == _fluxOffset); - } + const unsigned int nParType = exporter.numParticleTypes(); + for (unsigned int i = 0; i < nParType; ++i) + _fluxOffset += exporter.numParticleMobilePhaseDofs(i) + exporter.numSolidPhaseDofs(i); - inline unsigned int fluxOffset() const CADET_NOEXCEPT { return _fluxOffset; } - protected: - unsigned int _fluxOffset; - }; -} + // Make sure this is correct + const unsigned int nFluxes = exporter.numComponents() * exporter.numPrimaryCoordinates() * nParType * + std::max(exporter.numSecondaryCoordinates(), 1u); + REQUIRE(reinterpret_cast(&model)->numDofs() - nFluxes == _fluxOffset); + } + + inline unsigned int fluxOffset() const CADET_NOEXCEPT + { + return _fluxOffset; + } + +protected: + unsigned int _fluxOffset; +}; +} // namespace namespace cadet { @@ -136,601 +176,679 @@ namespace test namespace column { - void FVparams::setDisc(JsonParameterProvider& jpp, const std::string unitID) const { +void FVparams::setDisc(JsonParameterProvider& jpp, const std::string unitID) const +{ - int level = 0; + int level = 0; - if (jpp.exists("model")) - { - jpp.pushScope("model"); - ++level; - } - if (jpp.exists("unit_" + unitID)) - { - jpp.pushScope("unit_" + unitID); - ++level; - } + if (jpp.exists("model")) + { + jpp.pushScope("model"); + ++level; + } + if (jpp.exists("unit_" + unitID)) + { + jpp.pushScope("unit_" + unitID); + ++level; + } - std::string unitType = jpp.getString("UNIT_TYPE"); + std::string unitType = jpp.getString("UNIT_TYPE"); - jpp.pushScope("discretization"); - jpp.set("SPATIAL_METHOD", "FV"); + jpp.pushScope("discretization"); + jpp.set("SPATIAL_METHOD", "FV"); - if (nAxCells) - jpp.set("NCOL", nAxCells); + if (nAxCells) + jpp.set("NCOL", nAxCells); - if (nParCells) - jpp.set("NPAR", nParCells); + if (nParCells) + jpp.set("NPAR", nParCells); - if (nRadCells) + if (nRadCells) + { + if (unitType == "MULTI_CHANNEL_TRANSPORT") { - if (unitType == "MULTI_CHANNEL_TRANSPORT") - { - jpp.popScope(); - jpp.set("NCHANNEL", nRadCells); - jpp.pushScope("discretization"); - } - else - jpp.set("NRAD", nRadCells); + jpp.popScope(); + jpp.set("NCHANNEL", nRadCells); + jpp.pushScope("discretization"); } + else + jpp.set("NRAD", nRadCells); + } - if (jpp.exists("weno")) - { - jpp.pushScope("weno"); - if (wenoOrder) - jpp.set("WENO_ORDER", wenoOrder); + if (jpp.exists("weno")) + { + jpp.pushScope("weno"); + if (wenoOrder) + jpp.set("WENO_ORDER", wenoOrder); - jpp.popScope(); - } jpp.popScope(); - - for (int l = 0; l < level; ++l) - jpp.popScope(); } + jpp.popScope(); - void DGparams::setDisc(JsonParameterProvider& jpp, const std::string unitID) const { + for (int l = 0; l < level; ++l) + jpp.popScope(); +} - int level = 0; +void DGparams::setDisc(JsonParameterProvider& jpp, const std::string unitID) const +{ - if (jpp.exists("model")) - { - jpp.pushScope("model"); - ++level; - } - if (jpp.exists("unit_" + unitID)) - { - jpp.pushScope("unit_" + unitID); - ++level; - } + int level = 0; - jpp.pushScope("discretization"); - jpp.set("SPATIAL_METHOD", "DG"); - - if (exactIntegration > -1) - jpp.set("EXACT_INTEGRATION", exactIntegration); - if (polyDeg) - jpp.set("POLYDEG", polyDeg); - if (nElem) - jpp.set("NELEM", nElem); - if (parNelem) - jpp.set("PAR_NELEM", parNelem); - if (parPolyDeg) - jpp.set("PAR_POLYDEG", parPolyDeg); + if (jpp.exists("model")) + { + jpp.pushScope("model"); + ++level; + } + if (jpp.exists("unit_" + unitID)) + { + jpp.pushScope("unit_" + unitID); + ++level; + } + + jpp.pushScope("discretization"); + jpp.set("SPATIAL_METHOD", "DG"); + + if (exactIntegration > -1) + jpp.set("EXACT_INTEGRATION", exactIntegration); + if (polyDeg) + jpp.set("POLYDEG", polyDeg); + if (nElem) + jpp.set("NELEM", nElem); + if (parNelem) + jpp.set("PAR_NELEM", parNelem); + if (parPolyDeg) + jpp.set("PAR_POLYDEG", parPolyDeg); + jpp.popScope(); + + for (int l = 0; l < level; ++l) jpp.popScope(); +} - for (int l = 0; l < level; ++l) - jpp.popScope(); +/** + * @brief Reads reference chromatograms from a test data file + * @details The file format is as follows: + * Number of data points (uint32) + * Time points (array of doubles) + * Chromatogram for dynamic binding (array of doubles) + * Chromatogram for quasi-stationary binding (array of doubles) + */ +class ReferenceDataReader +{ +public: + ReferenceDataReader(const char* fileName) : _f(nullptr) + { + _f = std::fopen(fileName, "rb"); + std::fread(&_numElements, 4, 1, _f); } - /** - * @brief Reads reference chromatograms from a test data file - * @details The file format is as follows: - * Number of data points (uint32) - * Time points (array of doubles) - * Chromatogram for dynamic binding (array of doubles) - * Chromatogram for quasi-stationary binding (array of doubles) - */ - class ReferenceDataReader + ~ReferenceDataReader() { - public: - ReferenceDataReader(const char* fileName) : _f(nullptr) - { - _f = std::fopen(fileName, "rb"); - std::fread(&_numElements, 4, 1, _f); - } + std::fclose(_f); + } - ~ReferenceDataReader() - { - std::fclose(_f); - } + std::vector time() + { + std::vector v(_numElements, 0.0); + std::fseek(_f, 4, SEEK_SET); + std::fread(v.data(), 8, _numElements, _f); + return v; + } - std::vector time() - { - std::vector v(_numElements, 0.0); - std::fseek(_f, 4, SEEK_SET); - std::fread(v.data(), 8, _numElements, _f); - return v; - } + std::vector analyticDynamic() + { + std::vector v(_numElements, 0.0); + std::fseek(_f, 4 + _numElements * 8, SEEK_SET); + std::fread(v.data(), 8, _numElements, _f); + return v; + } - std::vector analyticDynamic() - { - std::vector v(_numElements, 0.0); - std::fseek(_f, 4 + _numElements * 8, SEEK_SET); - std::fread(v.data(), 8, _numElements, _f); - return v; - } + std::vector analyticQuasiStationary() + { + std::vector v(_numElements, 0.0); + std::fseek(_f, 4 + 2 * _numElements * 8, SEEK_SET); + std::fread(v.data(), 8, _numElements, _f); + return v; + } - std::vector analyticQuasiStationary() - { - std::vector v(_numElements, 0.0); - std::fseek(_f, 4 + 2 * _numElements * 8, SEEK_SET); - std::fread(v.data(), 8, _numElements, _f); - return v; - } +private: + std::FILE* _f; + uint32_t _numElements; +}; - private: - std::FILE* _f; - uint32_t _numElements; - }; +void setNumAxialCells(cadet::JsonParameterProvider& jpp, unsigned int nCol, std::string unitID) +{ + int level = 0; - void setNumAxialCells(cadet::JsonParameterProvider& jpp, unsigned int nCol, std::string unitID) + if (jpp.exists("model")) { - int level = 0; + jpp.pushScope("model"); + ++level; + } + if (jpp.exists("unit_" + unitID)) + { + jpp.pushScope("unit_" + unitID); + ++level; + } - if (jpp.exists("model")) - { - jpp.pushScope("model"); - ++level; - } - if (jpp.exists("unit_"+ unitID)) - { - jpp.pushScope("unit_" + unitID); - ++level; - } + jpp.pushScope("discretization"); - jpp.pushScope("discretization"); + jpp.set("NCOL", static_cast(nCol)); - jpp.set("NCOL", static_cast(nCol)); + jpp.popScope(); + for (int l = 0; l < level; ++l) jpp.popScope(); - - for (int l = 0; l < level; ++l) - jpp.popScope(); - } +} + +void setDG(cadet::JsonParameterProvider& jpp, std::string basis, unsigned int polyDeg, unsigned int nCol) +{ + int level = 0; - void setDG(cadet::JsonParameterProvider& jpp, std::string basis, unsigned int polyDeg, unsigned int nCol) + if (jpp.exists("model")) { - int level = 0; + jpp.pushScope("model"); + ++level; + } + if (jpp.exists("unit_000")) + { + jpp.pushScope("unit_000"); + ++level; + } - if (jpp.exists("model")) - { - jpp.pushScope("model"); - ++level; - } - if (jpp.exists("unit_000")) - { - jpp.pushScope("unit_000"); - ++level; - } + jpp.pushScope("discretization"); - jpp.pushScope("discretization"); + // Set discretization parameters + jpp.set("NCOL", static_cast(nCol)); + jpp.set("NNODES", static_cast(polyDeg + 1)); + jpp.set("POLYNOMIAL_BASIS", basis); - // Set discretization parameters - jpp.set("NCOL", static_cast(nCol)); - jpp.set("NNODES", static_cast(polyDeg + 1)); - jpp.set("POLYNOMIAL_BASIS", basis); + jpp.popScope(); + for (int l = 0; l < level; ++l) jpp.popScope(); +} - for (int l = 0; l < level; ++l) - jpp.popScope(); - } +void setNumParCells(cadet::JsonParameterProvider& jpp, unsigned int nPar, std::string unitID) +{ + int level = 0; - void setNumParCells(cadet::JsonParameterProvider& jpp, unsigned int nPar, std::string unitID) + if (jpp.exists("model")) { - int level = 0; + jpp.pushScope("model"); + ++level; + } + if (jpp.exists("unit_" + unitID)) + { + jpp.pushScope("unit_" + unitID); + ++level; + } - if (jpp.exists("model")) - { - jpp.pushScope("model"); - ++level; - } - if (jpp.exists("unit_" + unitID)) - { - jpp.pushScope("unit_" + unitID); - ++level; - } + jpp.pushScope("discretization"); - jpp.pushScope("discretization"); + jpp.set("NPAR", static_cast(nPar)); - jpp.set("NPAR", static_cast(nPar)); + jpp.popScope(); + for (int l = 0; l < level; ++l) jpp.popScope(); +} - for (int l = 0; l < level; ++l) - jpp.popScope(); - } +void setNumRadCells(cadet::JsonParameterProvider& jpp, unsigned int nRad, std::string unitID, + const bool mctModel = false) +{ + int level = 0; - void setNumRadCells(cadet::JsonParameterProvider& jpp, unsigned int nRad, std::string unitID, const bool mctModel = false) + if (jpp.exists("model")) { - int level = 0; + jpp.pushScope("model"); + ++level; + } + if (jpp.exists("unit_" + unitID)) + { + jpp.pushScope("unit_" + unitID); + ++level; + } - if (jpp.exists("model")) - { - jpp.pushScope("model"); - ++level; - } - if (jpp.exists("unit_" + unitID)) - { - jpp.pushScope("unit_" + unitID); - ++level; - } + jpp.pushScope("discretization"); + if (mctModel) + { + jpp.popScope(); + jpp.set("NCHANNEL", static_cast(nRad)); jpp.pushScope("discretization"); + } + else + jpp.set("NRAD", static_cast(nRad)); - if (mctModel) - { - jpp.popScope(); - jpp.set("NCHANNEL", static_cast(nRad)); - jpp.pushScope("discretization"); - } - else - jpp.set("NRAD", static_cast(nRad)); + jpp.popScope(); + for (int l = 0; l < level; ++l) jpp.popScope(); +} - for (int l = 0; l < level; ++l) - jpp.popScope(); - } +void setWenoOrder(cadet::JsonParameterProvider& jpp, int order) +{ + int level = 0; - void setWenoOrder(cadet::JsonParameterProvider& jpp, int order) + if (jpp.exists("model")) + { + jpp.pushScope("model"); + ++level; + } + if (jpp.exists("unit_000")) { - int level = 0; + jpp.pushScope("unit_000"); + ++level; + } - if (jpp.exists("model")) - { - jpp.pushScope("model"); - ++level; - } - if (jpp.exists("unit_000")) - { - jpp.pushScope("unit_000"); - ++level; - } + jpp.pushScope("discretization"); + jpp.pushScope("weno"); - jpp.pushScope("discretization"); - jpp.pushScope("weno"); + jpp.set("WENO_ORDER", order); - jpp.set("WENO_ORDER", order); + jpp.popScope(); + jpp.popScope(); + for (int l = 0; l < level; ++l) jpp.popScope(); - jpp.popScope(); - - for (int l = 0; l < level; ++l) - jpp.popScope(); - } +} - // todo make copy optional for all variables - void setNumericalMethod(cadet::IParameterProvider& pp, nlohmann::json& setupJson, const std::string unitID, const bool copy = false) +// todo make copy optional for all variables +void setNumericalMethod(cadet::IParameterProvider& pp, nlohmann::json& setupJson, const std::string unitID, + const bool copy = false) +{ + // copy over numerical methods from reference file. Note that we leave out spatial and time step resolution + // parameters + pp.pushScope("model"); + pp.pushScope("solver"); + nlohmann::json solver = setupJson["model"]["solver"]; + solver["GS_TYPE"] = pp.getInt("GS_TYPE"); + solver["MAX_KRYLOV"] = pp.getInt("MAX_KRYLOV"); + solver["MAX_RESTARTS"] = pp.getInt("MAX_RESTARTS"); + solver["SCHUR_SAFETY"] = pp.getDouble("SCHUR_SAFETY"); + setupJson["model"]["solver"] = solver; + pp.popScope(); + + pp.pushScope("unit_" + unitID); + pp.pushScope("discretization"); + nlohmann::json discretization = setupJson["model"]["unit_" + unitID]["discretization"]; + if (pp.exists("NBOUND")) + discretization["NBOUND"] = pp.getIntArray("NBOUND"); // note: in the future this might be included somewhere + // else in the setup as its part of the model + if (pp.exists("RECONSTRUCTION")) + discretization["RECONSTRUCTION"] = pp.getString("RECONSTRUCTION"); + if (pp.exists("USE_ANALYTIC_JACOBIAN")) + discretization["USE_ANALYTIC_JACOBIAN"] = pp.getInt("USE_ANALYTIC_JACOBIAN"); + if (pp.exists("GS_TYPE")) + discretization["GS_TYPE"] = pp.getInt("GS_TYPE"); + if (pp.exists("MAX_KRYLOV")) + discretization["MAX_KRYLOV"] = pp.getInt("MAX_KRYLOV"); + if (pp.exists("MAX_RESTARTS")) + discretization["MAX_RESTARTS"] = pp.getInt("MAX_RESTARTS"); + if (pp.exists("SCHUR_SAFETY")) + discretization["SCHUR_SAFETY"] = pp.getDouble("SCHUR_SAFETY"); + if (pp.exists("PAR_DISC_TYPE")) + discretization["PAR_DISC_TYPE"] = pp.getStringArray("PAR_DISC_TYPE"); + if (pp.exists("PAR_GEOM")) // note: in the future this might be included somewhere else in the setup as its part of + // the model + discretization["PAR_GEOM"] = pp.getStringArray("PAR_GEOM"); + if (pp.exists("weno")) { - // copy over numerical methods from reference file. Note that we leave out spatial and time step resolution parameters - pp.pushScope("model"); - pp.pushScope("solver"); - nlohmann::json solver = setupJson["model"]["solver"]; - solver["GS_TYPE"] = pp.getInt("GS_TYPE"); - solver["MAX_KRYLOV"] = pp.getInt("MAX_KRYLOV"); - solver["MAX_RESTARTS"] = pp.getInt("MAX_RESTARTS"); - solver["SCHUR_SAFETY"] = pp.getDouble("SCHUR_SAFETY"); - setupJson["model"]["solver"] = solver; - pp.popScope(); - - pp.pushScope("unit_" + unitID); - pp.pushScope("discretization"); - nlohmann::json discretization = setupJson["model"]["unit_" + unitID]["discretization"]; - if (pp.exists("NBOUND")) - discretization["NBOUND"] = pp.getIntArray("NBOUND"); // note: in the future this might be included somewhere else in the setup as its part of the model - if (pp.exists("RECONSTRUCTION")) - discretization["RECONSTRUCTION"] = pp.getString("RECONSTRUCTION"); - if (pp.exists("USE_ANALYTIC_JACOBIAN")) - discretization["USE_ANALYTIC_JACOBIAN"] = pp.getInt("USE_ANALYTIC_JACOBIAN"); - if (pp.exists("GS_TYPE")) - discretization["GS_TYPE"] = pp.getInt("GS_TYPE"); - if (pp.exists("MAX_KRYLOV")) - discretization["MAX_KRYLOV"] = pp.getInt("MAX_KRYLOV"); - if (pp.exists("MAX_RESTARTS")) - discretization["MAX_RESTARTS"] = pp.getInt("MAX_RESTARTS"); - if (pp.exists("SCHUR_SAFETY")) - discretization["SCHUR_SAFETY"] = pp.getDouble("SCHUR_SAFETY"); - if (pp.exists("PAR_DISC_TYPE")) - discretization["PAR_DISC_TYPE"] = pp.getStringArray("PAR_DISC_TYPE"); - if (pp.exists("PAR_GEOM")) // note: in the future this might be included somewhere else in the setup as its part of the model - discretization["PAR_GEOM"] = pp.getStringArray("PAR_GEOM"); - if (pp.exists("weno")) - { - pp.pushScope("weno"); - nlohmann::json weno; - weno["WENO_ORDER"] = pp.getInt("WENO_ORDER"); - weno["WENO_EPS"] = pp.getDouble("WENO_EPS"); - weno["BOUNDARY_MODEL"] = pp.getInt("BOUNDARY_MODEL"); - discretization["weno"] = weno; - pp.popScope(); - } - setupJson["model"]["unit_" + unitID]["discretization"] = discretization; - pp.popScope(); - pp.popScope(); - pp.popScope(); - - pp.pushScope("solver"); - if (pp.exists("CONSISTENT_INIT_MODE")) - setupJson["solver"]["CONSISTENT_INIT_MODE"] = pp.getInt("CONSISTENT_INIT_MODE"); - if (pp.exists("CONSISTENT_INIT_MODE_SENS")) - setupJson["solver"]["CONSISTENT_INIT_MODE_SENS"] = pp.getInt("CONSISTENT_INIT_MODE_SENS"); - setupJson["solver"]["NTHREADS"] = pp.getInt("NTHREADS"); - nlohmann::json timeIntegrator; - pp.pushScope("time_integrator"); - timeIntegrator["ABSTOL"] = copy ? pp.getDouble("ABSTOL") : 1e-8; - timeIntegrator["ALGTOL"] = copy ? pp.getDouble("ALGTOL") : 1e-8; - timeIntegrator["RELTOL"] = copy ? pp.getDouble("RELTOL") : 1e-6; - timeIntegrator["INIT_STEP_SIZE"] = copy ? pp.getDouble("INIT_STEP_SIZE") : 1e-10; - timeIntegrator["MAX_STEPS"] = copy ? pp.getInt("MAX_STEPS") : 1000000; - pp.popScope(); - setupJson["solver"]["time_integrator"] = timeIntegrator; + pp.pushScope("weno"); + nlohmann::json weno; + weno["WENO_ORDER"] = pp.getInt("WENO_ORDER"); + weno["WENO_EPS"] = pp.getDouble("WENO_EPS"); + weno["BOUNDARY_MODEL"] = pp.getInt("BOUNDARY_MODEL"); + discretization["weno"] = weno; pp.popScope(); } + setupJson["model"]["unit_" + unitID]["discretization"] = discretization; + pp.popScope(); + pp.popScope(); + pp.popScope(); + + pp.pushScope("solver"); + if (pp.exists("CONSISTENT_INIT_MODE")) + setupJson["solver"]["CONSISTENT_INIT_MODE"] = pp.getInt("CONSISTENT_INIT_MODE"); + if (pp.exists("CONSISTENT_INIT_MODE_SENS")) + setupJson["solver"]["CONSISTENT_INIT_MODE_SENS"] = pp.getInt("CONSISTENT_INIT_MODE_SENS"); + setupJson["solver"]["NTHREADS"] = pp.getInt("NTHREADS"); + nlohmann::json timeIntegrator; + pp.pushScope("time_integrator"); + timeIntegrator["ABSTOL"] = copy ? pp.getDouble("ABSTOL") : 1e-8; + timeIntegrator["ALGTOL"] = copy ? pp.getDouble("ALGTOL") : 1e-8; + timeIntegrator["RELTOL"] = copy ? pp.getDouble("RELTOL") : 1e-6; + timeIntegrator["INIT_STEP_SIZE"] = copy ? pp.getDouble("INIT_STEP_SIZE") : 1e-10; + timeIntegrator["MAX_STEPS"] = copy ? pp.getInt("MAX_STEPS") : 1000000; + pp.popScope(); + setupJson["solver"]["time_integrator"] = timeIntegrator; + pp.popScope(); +} - void copySensitivities(cadet::IParameterProvider& pp, nlohmann::json& setupJson, const std::string unitID) - { - // copy over sensitivity settings - if (!pp.exists("sensitivity")) - return; +void copySensitivities(cadet::IParameterProvider& pp, nlohmann::json& setupJson, const std::string unitID) +{ + // copy over sensitivity settings + if (!pp.exists("sensitivity")) + return; - pp.pushScope("sensitivity"); - nlohmann::json sens; - sens["NSENS"] = pp.getInt("NSENS"); - sens["SENS_METHOD"] = pp.getString("SENS_METHOD"); - - for (int sensID = 0; true; sensID++) - { - std::string sensParam = std::to_string(sensID); - sensParam = "param_" + std::string(3 - sensParam.length(), '0') + sensParam; - if (!pp.exists(sensParam)) - break; - pp.pushScope(sensParam); - nlohmann::json sens_param; - sens_param["SENS_NAME"] = pp.getString("SENS_NAME"); - sens_param["SENS_COMP"] = pp.getInt("SENS_COMP"); - sens_param["SENS_BOUNDPHASE"] = pp.getInt("SENS_BOUNDPHASE"); - sens_param["SENS_PARTYPE"] = pp.getInt("SENS_PARTYPE"); - sens_param["SENS_REACTION"] = pp.getInt("SENS_REACTION"); - sens_param["SENS_SECTION"] = pp.getInt("SENS_SECTION"); - sens_param["SENS_UNIT"] = pp.getInt("SENS_UNIT"); - sens[sensParam] = sens_param; - pp.popScope(); - } + pp.pushScope("sensitivity"); + nlohmann::json sens; + sens["NSENS"] = pp.getInt("NSENS"); + sens["SENS_METHOD"] = pp.getString("SENS_METHOD"); + for (int sensID = 0; true; sensID++) + { + std::string sensParam = std::to_string(sensID); + sensParam = "param_" + std::string(3 - sensParam.length(), '0') + sensParam; + if (!pp.exists(sensParam)) + break; + pp.pushScope(sensParam); + nlohmann::json sens_param; + sens_param["SENS_NAME"] = pp.getString("SENS_NAME"); + sens_param["SENS_COMP"] = pp.getInt("SENS_COMP"); + sens_param["SENS_BOUNDPHASE"] = pp.getInt("SENS_BOUNDPHASE"); + sens_param["SENS_PARTYPE"] = pp.getInt("SENS_PARTYPE"); + sens_param["SENS_REACTION"] = pp.getInt("SENS_REACTION"); + sens_param["SENS_SECTION"] = pp.getInt("SENS_SECTION"); + sens_param["SENS_UNIT"] = pp.getInt("SENS_UNIT"); + sens[sensParam] = sens_param; pp.popScope(); - setupJson["sensitivity"] = sens; } - void copyMultiplexData(cadet::IParameterProvider& pp, nlohmann::json& setupJson, const std::string unitID) - { - pp.pushScope("model"); - pp.pushScope("unit_"+unitID); + pp.popScope(); + setupJson["sensitivity"] = sens; +} - if (pp.exists("FILM_DIFFUSION_MULTIPLEX")) - setupJson["model"]["unit_" + unitID]["FILM_DIFFUSION_MULTIPLEX"] = pp.getInt("FILM_DIFFUSION_MULTIPLEX"); +void copyMultiplexData(cadet::IParameterProvider& pp, nlohmann::json& setupJson, const std::string unitID) +{ + pp.pushScope("model"); + pp.pushScope("unit_" + unitID); - if (pp.exists("ADSORPTION_MODEL_MULTIPLEX")) - setupJson["model"]["unit_" + unitID]["ADSORPTION_MODEL_MULTIPLEX"] = pp.getInt("ADSORPTION_MODEL_MULTIPLEX"); + if (pp.exists("FILM_DIFFUSION_MULTIPLEX")) + setupJson["model"]["unit_" + unitID]["FILM_DIFFUSION_MULTIPLEX"] = pp.getInt("FILM_DIFFUSION_MULTIPLEX"); - if (pp.exists("COL_DISPERSION_MULTIPLEX")) - setupJson["model"]["unit_" + unitID]["COL_DISPERSION_MULTIPLEX"] = pp.getInt("COL_DISPERSION_MULTIPLEX"); + if (pp.exists("ADSORPTION_MODEL_MULTIPLEX")) + setupJson["model"]["unit_" + unitID]["ADSORPTION_MODEL_MULTIPLEX"] = pp.getInt("ADSORPTION_MODEL_MULTIPLEX"); - if (pp.exists("PAR_DIFFUSION_MULTIPLEX")) - setupJson["model"]["unit_" + unitID]["PAR_DIFFUSION_MULTIPLEX"] = pp.getInt("PAR_DIFFUSION_MULTIPLEX"); + if (pp.exists("COL_DISPERSION_MULTIPLEX")) + setupJson["model"]["unit_" + unitID]["COL_DISPERSION_MULTIPLEX"] = pp.getInt("COL_DISPERSION_MULTIPLEX"); - if (pp.exists("PAR_SURFDIFFUSION_MULTIPLEX")) - setupJson["model"]["unit_" + unitID]["PAR_SURFDIFFUSION_MULTIPLEX"] = pp.getInt("PAR_SURFDIFFUSION_MULTIPLEX"); + if (pp.exists("PAR_DIFFUSION_MULTIPLEX")) + setupJson["model"]["unit_" + unitID]["PAR_DIFFUSION_MULTIPLEX"] = pp.getInt("PAR_DIFFUSION_MULTIPLEX"); - if (pp.exists("PORE_ACCESSIBILITY_MULTIPLEX")) - setupJson["model"]["unit_" + unitID]["PORE_ACCESSIBILITY_MULTIPLEX"] = pp.getInt("PORE_ACCESSIBILITY_MULTIPLEX"); + if (pp.exists("PAR_SURFDIFFUSION_MULTIPLEX")) + setupJson["model"]["unit_" + unitID]["PAR_SURFDIFFUSION_MULTIPLEX"] = pp.getInt("PAR_SURFDIFFUSION_MULTIPLEX"); - if (pp.exists("REACTION_MODEL_PARTICLES_MULTIPLEX")) - setupJson["model"]["unit_" + unitID]["REACTION_MODEL_PARTICLES_MULTIPLEX"] = pp.getInt("REACTION_MODEL_PARTICLES_MULTIPLEX"); + if (pp.exists("PORE_ACCESSIBILITY_MULTIPLEX")) + setupJson["model"]["unit_" + unitID]["PORE_ACCESSIBILITY_MULTIPLEX"] = + pp.getInt("PORE_ACCESSIBILITY_MULTIPLEX"); - pp.popScope(); - pp.popScope(); - } + if (pp.exists("REACTION_MODEL_PARTICLES_MULTIPLEX")) + setupJson["model"]["unit_" + unitID]["REACTION_MODEL_PARTICLES_MULTIPLEX"] = + pp.getInt("REACTION_MODEL_PARTICLES_MULTIPLEX"); - void copyReturnData(cadet::IParameterProvider& pp, nlohmann::json& setupJson, const std::string unitID) - { - // copy over return settings - pp.pushScope("return"); + pp.popScope(); + pp.popScope(); +} - nlohmann::json ret; - { - pp.pushScope("unit_" + unitID); - nlohmann::json ret_unit; +void copyReturnData(cadet::IParameterProvider& pp, nlohmann::json& setupJson, const std::string unitID) +{ + // copy over return settings + pp.pushScope("return"); - if (pp.exists("WRITE_COORDINATES")) - ret_unit["WRITE_COORDINATES"] = pp.getInt("WRITE_COORDINATES"); + nlohmann::json ret; + { + pp.pushScope("unit_" + unitID); + nlohmann::json ret_unit; - if (pp.exists("WRITE_SOLUTION_BULK")) - ret_unit["WRITE_SOLUTION_BULK"] = pp.getInt("WRITE_SOLUTION_BULK"); + if (pp.exists("WRITE_COORDINATES")) + ret_unit["WRITE_COORDINATES"] = pp.getInt("WRITE_COORDINATES"); - if (pp.exists("WRITE_SOLUTION_OUTLET")) - ret_unit["WRITE_SOLUTION_OUTLET"] = pp.getInt("WRITE_SOLUTION_OUTLET"); + if (pp.exists("WRITE_SOLUTION_BULK")) + ret_unit["WRITE_SOLUTION_BULK"] = pp.getInt("WRITE_SOLUTION_BULK"); - if (pp.exists("WRITE_SOLUTION_LAST")) - ret_unit["WRITE_SOLUTION_LAST"] = pp.getInt("WRITE_SOLUTION_LAST"); + if (pp.exists("WRITE_SOLUTION_OUTLET")) + ret_unit["WRITE_SOLUTION_OUTLET"] = pp.getInt("WRITE_SOLUTION_OUTLET"); - if (pp.exists("WRITE_SENS_BULK")) - ret_unit["WRITE_SENS_BULK"] = pp.getInt("WRITE_SENS_BULK"); + if (pp.exists("WRITE_SOLUTION_LAST")) + ret_unit["WRITE_SOLUTION_LAST"] = pp.getInt("WRITE_SOLUTION_LAST"); - if (pp.exists("WRITE_SENS_OUTLET")) - ret_unit["WRITE_SENS_OUTLET"] = pp.getInt("WRITE_SENS_OUTLET"); + if (pp.exists("WRITE_SENS_BULK")) + ret_unit["WRITE_SENS_BULK"] = pp.getInt("WRITE_SENS_BULK"); - if (pp.exists("WRITE_SENS_LAST")) - ret_unit["WRITE_SENS_LAST"] = pp.getInt("WRITE_SENS_LAST"); + if (pp.exists("WRITE_SENS_OUTLET")) + ret_unit["WRITE_SENS_OUTLET"] = pp.getInt("WRITE_SENS_OUTLET"); - if (pp.exists("WRITE_SOLUTION_FLUX")) - ret_unit["WRITE_SOLUTION_FLUX"] = pp.getInt("WRITE_SOLUTION_FLUX"); + if (pp.exists("WRITE_SENS_LAST")) + ret_unit["WRITE_SENS_LAST"] = pp.getInt("WRITE_SENS_LAST"); - if (pp.exists("WRITE_SOLUTION_INLET")) - ret_unit["WRITE_SOLUTION_INLET"] = pp.getInt("WRITE_SOLUTION_INLET"); + if (pp.exists("WRITE_SOLUTION_FLUX")) + ret_unit["WRITE_SOLUTION_FLUX"] = pp.getInt("WRITE_SOLUTION_FLUX"); - if (pp.exists("WRITE_SOLUTION_PARTICLE")) - ret_unit["WRITE_SOLUTION_PARTICLE"] = pp.getInt("WRITE_SOLUTION_PARTICLE"); + if (pp.exists("WRITE_SOLUTION_INLET")) + ret_unit["WRITE_SOLUTION_INLET"] = pp.getInt("WRITE_SOLUTION_INLET"); - if (pp.exists("WRITE_SOLUTION_SOLID")) - ret_unit["WRITE_SOLUTION_SOLID"] = pp.getInt("WRITE_SOLUTION_SOLID"); + if (pp.exists("WRITE_SOLUTION_PARTICLE")) + ret_unit["WRITE_SOLUTION_PARTICLE"] = pp.getInt("WRITE_SOLUTION_PARTICLE"); - if (pp.exists("WRITE_SOLUTION_VOLUME")) - ret_unit["WRITE_SOLUTION_VOLUME"] = pp.getInt("WRITE_SOLUTION_VOLUME"); + if (pp.exists("WRITE_SOLUTION_SOLID")) + ret_unit["WRITE_SOLUTION_SOLID"] = pp.getInt("WRITE_SOLUTION_SOLID"); - ret["unit_" + unitID] = ret_unit; - pp.popScope(); - } + if (pp.exists("WRITE_SOLUTION_VOLUME")) + ret_unit["WRITE_SOLUTION_VOLUME"] = pp.getInt("WRITE_SOLUTION_VOLUME"); - if (pp.exists("SPLIT_COMPONENTS_DATA")) - ret["SPLIT_COMPONENTS_DATA"] = pp.getInt("SPLIT_COMPONENTS_DATA"); - if (pp.exists("SPLIT_PORTS_DATA")) - ret["SPLIT_PORTS_DATA"] = pp.getInt("SPLIT_PORTS_DATA"); - if (pp.exists("WRITE_SOLUTION_TIMES")) - ret["WRITE_SOLUTION_TIMES"] = pp.getInt("WRITE_SOLUTION_TIMES"); + ret["unit_" + unitID] = ret_unit; pp.popScope(); - setupJson["return"] = ret; } - void reverseFlow(cadet::JsonParameterProvider& jpp) + if (pp.exists("SPLIT_COMPONENTS_DATA")) + ret["SPLIT_COMPONENTS_DATA"] = pp.getInt("SPLIT_COMPONENTS_DATA"); + if (pp.exists("SPLIT_PORTS_DATA")) + ret["SPLIT_PORTS_DATA"] = pp.getInt("SPLIT_PORTS_DATA"); + if (pp.exists("WRITE_SOLUTION_TIMES")) + ret["WRITE_SOLUTION_TIMES"] = pp.getInt("WRITE_SOLUTION_TIMES"); + pp.popScope(); + setupJson["return"] = ret; +} + +void reverseFlow(cadet::JsonParameterProvider& jpp) +{ + int level = 0; + + if (jpp.exists("model")) + { + jpp.pushScope("model"); + ++level; + } + if (jpp.exists("unit_000")) { - int level = 0; + jpp.pushScope("unit_000"); + ++level; + } - if (jpp.exists("model")) - { - jpp.pushScope("model"); - ++level; - } - if (jpp.exists("unit_000")) - { - jpp.pushScope("unit_000"); - ++level; - } + jpp.set("VELOCITY", -jpp.getDouble("VELOCITY")); - jpp.set("VELOCITY", -jpp.getDouble("VELOCITY")); + for (int l = 0; l < level; ++l) + jpp.popScope(); +} - for (int l = 0; l < level; ++l) - jpp.popScope(); - } +void setCrossSectionArea(cadet::JsonParameterProvider& jpp, bool useTotalPorosity, int dir) +{ + int level = 0; - void setCrossSectionArea(cadet::JsonParameterProvider& jpp, bool useTotalPorosity, int dir) + if (jpp.exists("model")) { - int level = 0; + jpp.pushScope("model"); + ++level; + } + if (jpp.exists("unit_000")) + { + jpp.pushScope("unit_000"); + ++level; + } - if (jpp.exists("model")) - { - jpp.pushScope("model"); - ++level; - } - if (jpp.exists("unit_000")) - { - jpp.pushScope("unit_000"); - ++level; - } + const double vel = jpp.getDouble("VELOCITY"); + double por = 0.0; + if (useTotalPorosity && (jpp.exists("TOTAL_POROSITY"))) + por = jpp.getDouble("TOTAL_POROSITY"); + else + por = jpp.getDouble("COL_POROSITY"); + + // Assume a volumetric flow rate of 1.0 m^3/s + jpp.set("CROSS_SECTION_AREA", 1.0 / (vel * por)); - const double vel = jpp.getDouble("VELOCITY"); - double por = 0.0; - if (useTotalPorosity && (jpp.exists("TOTAL_POROSITY"))) - por = jpp.getDouble("TOTAL_POROSITY"); + if (dir == 0) + jpp.remove("VELOCITY"); + else + { + if (dir > 0) + jpp.set("VELOCITY", 1.0); else - por = jpp.getDouble("COL_POROSITY"); + jpp.set("VELOCITY", -1.0); + } - // Assume a volumetric flow rate of 1.0 m^3/s - jpp.set("CROSS_SECTION_AREA", 1.0 / (vel * por)); + for (int l = 0; l < level; ++l) + jpp.popScope(); +} - if (dir == 0) - jpp.remove("VELOCITY"); - else - { - if (dir > 0) - jpp.set("VELOCITY", 1.0); - else - jpp.set("VELOCITY", -1.0); - } +unsigned int fluxOffsetOfColumnUnitOp(cadet::IUnitOperation* unit) +{ + // Obtain offset to fluxes + FluxOffsetExtractionRecorder foer; + unit->reportSolutionStructure(foer); + return foer.fluxOffset(); +} - for (int l = 0; l < level; ++l) - jpp.popScope(); +void testForwardBackward(cadet::JsonParameterProvider jpp, double absTol, double relTol) +{ + // Forward flow + cadet::Driver drvFwd; + drvFwd.configure(jpp); + drvFwd.run(); + + // Backward flow + reverseFlow(jpp); + cadet::Driver drvBwd; + drvBwd.configure(jpp); + drvBwd.run(); + + cadet::InternalStorageUnitOpRecorder const* const fwdData = drvFwd.solution()->unitOperation(0); + cadet::InternalStorageUnitOpRecorder const* const bwdData = drvBwd.solution()->unitOperation(0); + + double const* fwdInlet = fwdData->inlet(); + double const* fwdOutlet = fwdData->outlet(); + double const* bwdInlet = bwdData->inlet(); + double const* bwdOutlet = bwdData->outlet(); + + const unsigned int nComp = fwdData->numComponents(); + for (unsigned int i = 0; i < fwdData->numDataPoints() * fwdData->numInletPorts() * nComp; + ++i, ++fwdInlet, ++fwdOutlet, ++bwdInlet, ++bwdOutlet) + { + // Forward flow inlet = backward flow outlet + CAPTURE(i); + CHECK((*fwdInlet) == makeApprox(*bwdInlet, relTol, absTol)); + + // Forward flow outlet = backward flow inlet + CAPTURE(i); + CHECK((*fwdOutlet) == makeApprox(*bwdOutlet, relTol, absTol)); } +} - unsigned int fluxOffsetOfColumnUnitOp(cadet::IUnitOperation* unit) +void testForwardBackward(const char* uoType, FVparams disc, double absTol, double relTol) +{ + SECTION("Forward vs backward flow (WENO=" + std::to_string(disc.getWenoOrder()) + ")") { - // Obtain offset to fluxes - FluxOffsetExtractionRecorder foer; - unit->reportSolutionStructure(foer); - return foer.fluxOffset(); + // Use Load-Wash-Elution test case + cadet::JsonParameterProvider jpp = createLWE(uoType, "FV"); + disc.setDisc(jpp); + + testForwardBackward(jpp, absTol, relTol); } +} - void testForwardBackward(cadet::JsonParameterProvider jpp, double absTol, double relTol) +void testForwardBackward(const char* uoType, DGparams disc, double absTol, double relTol) +{ + SECTION("Forward vs backward flow (DG integration mode " + std::to_string(disc.getIntegrationMode()) + ")") { - // Forward flow - cadet::Driver drvFwd; - drvFwd.configure(jpp); - drvFwd.run(); + // Use Load-Wash-Elution test case + cadet::JsonParameterProvider jpp = createLWE(uoType, "DG"); + disc.setDisc(jpp); + + testForwardBackward(jpp, absTol, relTol); + } +} - // Backward flow +void testAnalyticBenchmark(cadet::JsonParameterProvider jpp, const char* refFileRelPath, bool forwardFlow, + bool dynamicBinding, double absTol, double relTol) +{ + if (!forwardFlow) reverseFlow(jpp); - cadet::Driver drvBwd; - drvBwd.configure(jpp); - drvBwd.run(); - cadet::InternalStorageUnitOpRecorder const* const fwdData = drvFwd.solution()->unitOperation(0); - cadet::InternalStorageUnitOpRecorder const* const bwdData = drvBwd.solution()->unitOperation(0); + // Run simulation + cadet::Driver drv; + drv.configure(jpp); + drv.run(); - double const* fwdInlet = fwdData->inlet(); - double const* fwdOutlet = fwdData->outlet(); - double const* bwdInlet = bwdData->inlet(); - double const* bwdOutlet = bwdData->outlet(); + // Read reference data from test file + const std::string refFile = std::string(getTestDirectory()) + std::string(refFileRelPath); + ReferenceDataReader rd(refFile.c_str()); + const std::vector time = rd.time(); + const std::vector ref = (dynamicBinding ? rd.analyticDynamic() : rd.analyticQuasiStationary()); - const unsigned int nComp = fwdData->numComponents(); - for (unsigned int i = 0; i < fwdData->numDataPoints() * fwdData->numInletPorts() * nComp; ++i, ++fwdInlet, ++fwdOutlet, ++bwdInlet, ++bwdOutlet) - { - // Forward flow inlet = backward flow outlet - CAPTURE(i); - CHECK((*fwdInlet) == makeApprox(*bwdInlet, relTol, absTol)); + // Get data from simulation + cadet::InternalStorageUnitOpRecorder const* const simData = drv.solution()->unitOperation(0); + double const* outlet = simData->outlet(); - // Forward flow outlet = backward flow inlet - CAPTURE(i); - CHECK((*fwdOutlet) == makeApprox(*bwdOutlet, relTol, absTol)); - } + // Compare + for (unsigned int i = 0; i < simData->numDataPoints() * simData->numComponents() * simData->numInletPorts(); + ++i, ++outlet) + { + // Note that the simulation only saves the chromatogram at multiples of 2 (i.e., 0s, 2s, 4s, ...) + // whereas the reference solution is given at every second (0s, 1s, 2s, 3s, ...) + // Thus, we only take the even indices of the reference array + CAPTURE(time[2 * i]); + CHECK((*outlet) == makeApprox(ref[2 * i], relTol, absTol)); } +} - void testForwardBackward(const char* uoType, FVparams disc, double absTol, double relTol) +void testAnalyticBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, bool dynamicBinding, + DiscParams& disc, const std::string method, double absTol, double relTol) +{ + const std::string fwdStr = (forwardFlow ? "forward" : "backward"); + SECTION(method + ": Analytic " + fwdStr + " flow with " + (dynamicBinding ? "dynamic" : "quasi-stationary") + + " binding") { - SECTION("Forward vs backward flow (WENO=" + std::to_string(disc.getWenoOrder()) + ")") - { - // Use Load-Wash-Elution test case - cadet::JsonParameterProvider jpp = createLWE(uoType, "FV"); - disc.setDisc(jpp); + // Setup simulation + cadet::JsonParameterProvider jpp = createLinearBenchmark(dynamicBinding, false, uoType, method); + disc.setDisc(jpp); - testForwardBackward(jpp, absTol, relTol); - } + testAnalyticBenchmark(jpp, refFileRelPath, forwardFlow, dynamicBinding, absTol, relTol); } +} - void testForwardBackward(const char* uoType, DGparams disc, double absTol, double relTol) - { - SECTION("Forward vs backward flow (DG integration mode " + std::to_string(disc.getIntegrationMode()) + ")") - { - // Use Load-Wash-Elution test case - cadet::JsonParameterProvider jpp = createLWE(uoType, "DG"); - disc.setDisc(jpp); +void testAnalyticBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, bool dynamicBinding, + FVparams& disc, double absTol, double relTol) +{ + testAnalyticBenchmark(uoType, refFileRelPath, forwardFlow, dynamicBinding, disc, "FV", absTol, relTol); +} - testForwardBackward(jpp, absTol, relTol); - } - } +void testAnalyticBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, bool dynamicBinding, + DGparams& disc, double absTol, double relTol) +{ + testAnalyticBenchmark(uoType, refFileRelPath, forwardFlow, dynamicBinding, disc, "DG", absTol, relTol); +} - void testAnalyticBenchmark(cadet::JsonParameterProvider jpp, const char* refFileRelPath, bool forwardFlow, bool dynamicBinding, double absTol, double relTol) +void testAnalyticNonBindingBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, DiscParams& disc, + const std::string method, double absTol, double relTol) +{ + const std::string fwdStr = (forwardFlow ? "forward" : "backward"); + SECTION(method + ": Analytic " + fwdStr + " flow") { + // Setup simulation + cadet::JsonParameterProvider jpp = createLinearBenchmark(true, true, uoType, method); + disc.setDisc(jpp); + if (!forwardFlow) reverseFlow(jpp); @@ -743,129 +861,137 @@ namespace column const std::string refFile = std::string(getTestDirectory()) + std::string(refFileRelPath); ReferenceDataReader rd(refFile.c_str()); const std::vector time = rd.time(); - const std::vector ref = (dynamicBinding ? rd.analyticDynamic() : rd.analyticQuasiStationary()); + const std::vector ref = rd.analyticDynamic(); // Get data from simulation cadet::InternalStorageUnitOpRecorder const* const simData = drv.solution()->unitOperation(0); double const* outlet = simData->outlet(); // Compare - for (unsigned int i = 0; i < simData->numDataPoints() * simData->numComponents() * simData->numInletPorts(); ++i, ++outlet) + for (unsigned int i = 0; i < simData->numDataPoints() * simData->numComponents() * simData->numInletPorts(); + ++i, ++outlet) { - // Note that the simulation only saves the chromatogram at multiples of 2 (i.e., 0s, 2s, 4s, ...) - // whereas the reference solution is given at every second (0s, 1s, 2s, 3s, ...) - // Thus, we only take the even indices of the reference array - CAPTURE(time[2 * i]); - CHECK((*outlet) == makeApprox(ref[2 * i], relTol, absTol)); + CAPTURE(time[i]); + CHECK((*outlet) == makeApprox(ref[i], relTol, absTol)); } } +} - void testAnalyticBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, bool dynamicBinding, DiscParams& disc, const std::string method, double absTol, double relTol) - { - const std::string fwdStr = (forwardFlow ? "forward" : "backward"); - SECTION(method + ": Analytic " + fwdStr + " flow with " + (dynamicBinding ? "dynamic" : "quasi-stationary") + " binding") - { - // Setup simulation - cadet::JsonParameterProvider jpp = createLinearBenchmark(dynamicBinding, false, uoType, method); - disc.setDisc(jpp); +void testAnalyticNonBindingBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, FVparams& disc, + double absTol, double relTol) +{ + testAnalyticNonBindingBenchmark(uoType, refFileRelPath, forwardFlow, disc, "FV", absTol, relTol); +} - testAnalyticBenchmark(jpp, refFileRelPath, forwardFlow, dynamicBinding, absTol, relTol); - } - } +void testAnalyticNonBindingBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, DGparams& disc, + double absTol, double relTol) +{ + testAnalyticNonBindingBenchmark(uoType, refFileRelPath, forwardFlow, disc, "DG", absTol, relTol); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void testAnalyticBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, bool dynamicBinding, FVparams& disc, double absTol, double relTol) - { - testAnalyticBenchmark(uoType, refFileRelPath, forwardFlow, dynamicBinding, disc, "FV", absTol, relTol); - } +void testJacobianAD(cadet::JsonParameterProvider& jpp, const double absTolFDpattern) +{ + cadet::IModelBuilder* const mb = cadet::createModelBuilder(); + REQUIRE(nullptr != mb); + + cadet::IUnitOperation* const unitAna = unitoperation::createAndConfigureUnit(jpp, *mb); + cadet::IUnitOperation* const unitAD = unitoperation::createAndConfigureUnit(jpp, *mb); + + // Enable AD + cadet::ad::setDirections(cadet::ad::getMaxDirections()); + unitAD->useAnalyticJacobian(false); + + cadet::active* adRes = new cadet::active[unitAD->numDofs()]; + cadet::active* adY = new cadet::active[unitAD->numDofs()]; + + // Obtain memory for state, Jacobian multiply direction, Jacobian column + std::vector y(unitAD->numDofs(), 0.0); + std::vector jacDir(unitAD->numDofs(), 0.0); + std::vector jacCol1(unitAD->numDofs(), 0.0); + std::vector jacCol2(unitAD->numDofs(), 0.0); + + // Fill state vector with some values + util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, unitAna->numDofs()); + // util::populate(y.data(), [](unsigned int idx) { return 1.0; }, unitAna->numDofs()); + + // Setup matrices + const AdJacobianParams noAdParams{nullptr, nullptr, 0u}; + const AdJacobianParams adParams{adRes, adY, 0u}; + unitAD->prepareADvectors(adParams); + + const ConstSimulationState simState{y.data(), nullptr}; + unitAna->notifyDiscontinuousSectionTransition(0.0, 0u, simState, noAdParams); + unitAD->notifyDiscontinuousSectionTransition(0.0, 0u, simState, adParams); + + // Compute state Jacobian + const SimulationTime simTime{0.0, 0u}; + cadet::util::ThreadLocalStorage tls; + tls.resize(unitAna->threadLocalMemorySize()); + + unitAna->residualWithJacobian(simTime, simState, jacDir.data(), noAdParams, tls); + unitAD->residualWithJacobian(simTime, simState, jacDir.data(), adParams, tls); + std::fill(jacDir.begin(), jacDir.end(), 0.0); + + // Compare Jacobians + cadet::test::checkJacobianPatternFD(unitAna, unitAD, y.data(), nullptr, jacDir.data(), jacCol1.data(), + jacCol2.data(), tls, absTolFDpattern); + cadet::test::checkJacobianPatternFD(unitAna, unitAna, y.data(), nullptr, jacDir.data(), jacCol1.data(), + jacCol2.data(), tls, absTolFDpattern); + cadet::test::compareJacobian(unitAna, unitAD, nullptr, nullptr, jacDir.data(), jacCol1.data(), jacCol2.data()); + // cadet::test::compareJacobianFD(unitAna, unitAD, y.data(), nullptr, jacDir.data(), jacCol1.data(), + // jacCol2.data()); + + delete[] adRes; + delete[] adY; + mb->destroyUnitOperation(unitAna); + mb->destroyUnitOperation(unitAD); + destroyModelBuilder(mb); +} - void testAnalyticBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, bool dynamicBinding, DGparams& disc, double absTol, double relTol) - { - testAnalyticBenchmark(uoType, refFileRelPath, forwardFlow, dynamicBinding, disc, "DG", absTol, relTol); - } +void testJacobianForwardBackward(cadet::JsonParameterProvider& jpp, const double absTolFDpattern) +{ + cadet::IModelBuilder* const mb = cadet::createModelBuilder(); + REQUIRE(nullptr != mb); - void testAnalyticNonBindingBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, DiscParams& disc, const std::string method, double absTol, double relTol) - { - const std::string fwdStr = (forwardFlow ? "forward" : "backward"); - SECTION(method + ": Analytic " + fwdStr + " flow") - { - // Setup simulation - cadet::JsonParameterProvider jpp = createLinearBenchmark(true, true, uoType, method); - disc.setDisc(jpp); + // Use some test case parameters + const unsigned int nComp = jpp.getInt("NCOMP"); - if (!forwardFlow) - reverseFlow(jpp); + cadet::IUnitOperation* const unitAna = unitoperation::createAndConfigureUnit(jpp, *mb); + cadet::IUnitOperation* const unitAD = unitoperation::createAndConfigureUnit(jpp, *mb); - // Run simulation - cadet::Driver drv; - drv.configure(jpp); - drv.run(); + // Enable AD + cadet::ad::setDirections(cadet::ad::getMaxDirections()); + unitAD->useAnalyticJacobian(false); - // Read reference data from test file - const std::string refFile = std::string(getTestDirectory()) + std::string(refFileRelPath); - ReferenceDataReader rd(refFile.c_str()); - const std::vector time = rd.time(); - const std::vector ref = rd.analyticDynamic(); + cadet::active* adRes = new cadet::active[unitAD->numDofs()]; + cadet::active* adY = new cadet::active[unitAD->numDofs()]; - // Get data from simulation - cadet::InternalStorageUnitOpRecorder const* const simData = drv.solution()->unitOperation(0); - double const* outlet = simData->outlet(); + // Obtain memory for state, Jacobian multiply direction, Jacobian column + std::vector y(unitAD->numDofs(), 0.0); + std::vector jacDir(unitAD->numDofs(), 0.0); + std::vector jacCol1(unitAD->numDofs(), 0.0); + std::vector jacCol2(unitAD->numDofs(), 0.0); - // Compare - for (unsigned int i = 0; i < simData->numDataPoints() * simData->numComponents() * simData->numInletPorts(); ++i, ++outlet) - { - CAPTURE(time[i]); - CHECK((*outlet) == makeApprox(ref[i], relTol, absTol)); - } - } - } + // Fill state vector with some values + util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, unitAna->numDofs()); + // util::populate(y.data(), [](unsigned int idx) { return 1.0; }, unitAna->numDofs()); - void testAnalyticNonBindingBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, FVparams& disc, double absTol, double relTol) - { - testAnalyticNonBindingBenchmark(uoType, refFileRelPath, forwardFlow, disc, "FV", absTol, relTol); - } + // Setup matrices + const AdJacobianParams noAdParams{nullptr, nullptr, 0u}; + const AdJacobianParams adParams{adRes, adY, 0u}; + unitAD->prepareADvectors(adParams); - void testAnalyticNonBindingBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, DGparams& disc, double absTol, double relTol) - { - testAnalyticNonBindingBenchmark(uoType, refFileRelPath, forwardFlow, disc, "DG", absTol, relTol); - } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + unitAna->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, noAdParams); + unitAD->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, adParams); - void testJacobianAD(cadet::JsonParameterProvider& jpp, const double absTolFDpattern) + SECTION("Forward then backward flow (nonzero state)") { - cadet::IModelBuilder* const mb = cadet::createModelBuilder(); - REQUIRE(nullptr != mb); - - cadet::IUnitOperation* const unitAna = unitoperation::createAndConfigureUnit(jpp, *mb); - cadet::IUnitOperation* const unitAD = unitoperation::createAndConfigureUnit(jpp, *mb); - - // Enable AD - cadet::ad::setDirections(cadet::ad::getMaxDirections()); - unitAD->useAnalyticJacobian(false); - - cadet::active* adRes = new cadet::active[unitAD->numDofs()]; - cadet::active* adY = new cadet::active[unitAD->numDofs()]; - - // Obtain memory for state, Jacobian multiply direction, Jacobian column - std::vector y(unitAD->numDofs(), 0.0); - std::vector jacDir(unitAD->numDofs(), 0.0); - std::vector jacCol1(unitAD->numDofs(), 0.0); - std::vector jacCol2(unitAD->numDofs(), 0.0); - - // Fill state vector with some values - util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, unitAna->numDofs()); -// util::populate(y.data(), [](unsigned int idx) { return 1.0; }, unitAna->numDofs()); - - // Setup matrices - const AdJacobianParams noAdParams{nullptr, nullptr, 0u}; - const AdJacobianParams adParams{adRes, adY, 0u}; - unitAD->prepareADvectors(adParams); - - const ConstSimulationState simState{y.data(), nullptr}; - unitAna->notifyDiscontinuousSectionTransition(0.0, 0u, simState, noAdParams); - unitAD->notifyDiscontinuousSectionTransition(0.0, 0u, simState, adParams); - // Compute state Jacobian const SimulationTime simTime{0.0, 0u}; + const ConstSimulationState simState{y.data(), nullptr}; cadet::util::ThreadLocalStorage tls; tls.resize(unitAna->threadLocalMemorySize()); @@ -874,1060 +1000,1105 @@ namespace column std::fill(jacDir.begin(), jacDir.end(), 0.0); // Compare Jacobians - cadet::test::checkJacobianPatternFD(unitAna, unitAD, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data(), tls, absTolFDpattern); - cadet::test::checkJacobianPatternFD(unitAna, unitAna, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data(), tls, absTolFDpattern); + cadet::test::checkJacobianPatternFD(unitAna, unitAD, y.data(), nullptr, jacDir.data(), jacCol1.data(), + jacCol2.data(), tls, absTolFDpattern); + cadet::test::checkJacobianPatternFD(unitAna, unitAna, y.data(), nullptr, jacDir.data(), jacCol1.data(), + jacCol2.data(), tls, absTolFDpattern); cadet::test::compareJacobian(unitAna, unitAD, nullptr, nullptr, jacDir.data(), jacCol1.data(), jacCol2.data()); -// cadet::test::compareJacobianFD(unitAna, unitAD, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data()); - - delete[] adRes; - delete[] adY; - mb->destroyUnitOperation(unitAna); - mb->destroyUnitOperation(unitAD); - destroyModelBuilder(mb); - } + // cadet::test::compareJacobianFD(unitAna, unitAD, y.data(), nullptr, jacDir.data(), + // jacCol1.data(), jacCol2.data()); - void testJacobianForwardBackward(cadet::JsonParameterProvider& jpp, const double absTolFDpattern) - { - cadet::IModelBuilder* const mb = cadet::createModelBuilder(); - REQUIRE(nullptr != mb); - - // Use some test case parameters - const unsigned int nComp = jpp.getInt("NCOMP"); - - cadet::IUnitOperation* const unitAna = unitoperation::createAndConfigureUnit(jpp, *mb); - cadet::IUnitOperation* const unitAD = unitoperation::createAndConfigureUnit(jpp, *mb); - - // Enable AD - cadet::ad::setDirections(cadet::ad::getMaxDirections()); - unitAD->useAnalyticJacobian(false); - - cadet::active* adRes = new cadet::active[unitAD->numDofs()]; - cadet::active* adY = new cadet::active[unitAD->numDofs()]; - - // Obtain memory for state, Jacobian multiply direction, Jacobian column - std::vector y(unitAD->numDofs(), 0.0); - std::vector jacDir(unitAD->numDofs(), 0.0); - std::vector jacCol1(unitAD->numDofs(), 0.0); - std::vector jacCol2(unitAD->numDofs(), 0.0); - - // Fill state vector with some values - util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, unitAna->numDofs()); -// util::populate(y.data(), [](unsigned int idx) { return 1.0; }, unitAna->numDofs()); - - // Setup matrices - const AdJacobianParams noAdParams{nullptr, nullptr, 0u}; - const AdJacobianParams adParams{adRes, adY, 0u}; - unitAD->prepareADvectors(adParams); + if (jpp.getString("UNIT_TYPE").substr(0, 6) == "RADIAL") + { + // Reverse flow + const bool paramSet = unitAna->setParameter( + cadet::makeParamId(cadet::hashString("VELOCITY_COEFF"), 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), + -jpp.getDouble("VELOCITY_COEFF")); + REQUIRE(paramSet); + // Reverse flow + const bool paramSet2 = unitAD->setParameter( + cadet::makeParamId(cadet::hashString("VELOCITY_COEFF"), 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), + -jpp.getDouble("VELOCITY_COEFF")); + REQUIRE(paramSet2); + } + else + { + // Reverse flow + const bool paramSet = unitAna->setParameter( + cadet::makeParamId(cadet::hashString("VELOCITY"), 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), + -jpp.getDouble("VELOCITY")); + REQUIRE(paramSet); + // Reverse flow + const bool paramSet2 = unitAD->setParameter( + cadet::makeParamId(cadet::hashString("VELOCITY"), 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), + -jpp.getDouble("VELOCITY")); + REQUIRE(paramSet2); + } + // Setup unitAna->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, noAdParams); unitAD->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, adParams); - SECTION("Forward then backward flow (nonzero state)") - { - // Compute state Jacobian - const SimulationTime simTime{0.0, 0u}; - const ConstSimulationState simState{y.data(), nullptr}; - cadet::util::ThreadLocalStorage tls; - tls.resize(unitAna->threadLocalMemorySize()); - - unitAna->residualWithJacobian(simTime, simState, jacDir.data(), noAdParams, tls); - unitAD->residualWithJacobian(simTime, simState, jacDir.data(), adParams, tls); - std::fill(jacDir.begin(), jacDir.end(), 0.0); - - // Compare Jacobians - cadet::test::checkJacobianPatternFD(unitAna, unitAD, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data(), tls, absTolFDpattern); - cadet::test::checkJacobianPatternFD(unitAna, unitAna, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data(), tls, absTolFDpattern); - cadet::test::compareJacobian(unitAna, unitAD, nullptr, nullptr, jacDir.data(), jacCol1.data(), jacCol2.data()); -// cadet::test::compareJacobianFD(unitAna, unitAD, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data()); - - if (jpp.getString("UNIT_TYPE").substr(0, 6) == "RADIAL") - { - // Reverse flow - const bool paramSet = unitAna->setParameter(cadet::makeParamId(cadet::hashString("VELOCITY_COEFF"), 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), -jpp.getDouble("VELOCITY_COEFF")); - REQUIRE(paramSet); - // Reverse flow - const bool paramSet2 = unitAD->setParameter(cadet::makeParamId(cadet::hashString("VELOCITY_COEFF"), 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), -jpp.getDouble("VELOCITY_COEFF")); - REQUIRE(paramSet2); - } - else - { - // Reverse flow - const bool paramSet = unitAna->setParameter(cadet::makeParamId(cadet::hashString("VELOCITY"), 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), -jpp.getDouble("VELOCITY")); - REQUIRE(paramSet); - // Reverse flow - const bool paramSet2 = unitAD->setParameter(cadet::makeParamId(cadet::hashString("VELOCITY"), 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), -jpp.getDouble("VELOCITY")); - REQUIRE(paramSet2); - } - - // Setup - unitAna->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, noAdParams); - unitAD->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, adParams); - - // Compute state Jacobian - unitAna->residualWithJacobian(simTime, simState, jacDir.data(), noAdParams, tls); - unitAD->residualWithJacobian(simTime, simState, jacDir.data(), adParams, tls); - std::fill(jacDir.begin(), jacDir.end(), 0.0); - - // Compare Jacobians - cadet::test::checkJacobianPatternFD(unitAna, unitAD, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data(), tls, absTolFDpattern); - cadet::test::checkJacobianPatternFD(unitAna, unitAna, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data(), tls, absTolFDpattern); -// cadet::test::compareJacobianFD(unitAD, unitAna, y.data(), jacDir.data(), nullptr, jacCol1.data(), jacCol2.data()); -// cadet::test::compareJacobianFD(unitAna, unitAD, y.data(), jacDir.data(), nullptr, jacCol1.data(), jacCol2.data()); - cadet::test::compareJacobian(unitAna, unitAD, nullptr, nullptr, jacDir.data(), jacCol1.data(), jacCol2.data()); - } + // Compute state Jacobian + unitAna->residualWithJacobian(simTime, simState, jacDir.data(), noAdParams, tls); + unitAD->residualWithJacobian(simTime, simState, jacDir.data(), adParams, tls); + std::fill(jacDir.begin(), jacDir.end(), 0.0); - delete[] adRes; - delete[] adY; - mb->destroyUnitOperation(unitAna); - mb->destroyUnitOperation(unitAD); - - destroyModelBuilder(mb); + // Compare Jacobians + cadet::test::checkJacobianPatternFD(unitAna, unitAD, y.data(), nullptr, jacDir.data(), jacCol1.data(), + jacCol2.data(), tls, absTolFDpattern); + cadet::test::checkJacobianPatternFD(unitAna, unitAna, y.data(), nullptr, jacDir.data(), jacCol1.data(), + jacCol2.data(), tls, absTolFDpattern); + // cadet::test::compareJacobianFD(unitAD, unitAna, y.data(), jacDir.data(), nullptr, + // jacCol1.data(), jacCol2.data()); cadet::test::compareJacobianFD(unitAna, unitAD, y.data(), + // jacDir.data(), nullptr, jacCol1.data(), jacCol2.data()); + cadet::test::compareJacobian(unitAna, unitAD, nullptr, nullptr, jacDir.data(), jacCol1.data(), jacCol2.data()); } - void testJacobianForwardBackward(const char* uoType, FVparams disc, const double absTolFDpattern) - { - SECTION("Forward vs backward flow Jacobian (WENO=" + std::to_string(disc.getWenoOrder()) + ")") - { - // Use Load-Wash-Elution test case - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, "FV"); - disc.setDisc(jpp); + delete[] adRes; + delete[] adY; + mb->destroyUnitOperation(unitAna); + mb->destroyUnitOperation(unitAD); - testJacobianForwardBackward(jpp, absTolFDpattern); - } - } + destroyModelBuilder(mb); +} - void testJacobianForwardBackward(const char* uoType, DGparams disc, const double absTolFDpattern) +void testJacobianForwardBackward(const char* uoType, FVparams disc, const double absTolFDpattern) +{ + SECTION("Forward vs backward flow Jacobian (WENO=" + std::to_string(disc.getWenoOrder()) + ")") { - SECTION("Forward vs backward flow Jacobian (DG integration mode " + std::to_string(disc.getIntegrationMode()) + ")") - { - // Use Load-Wash-Elution test case - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, "DG"); - disc.setDisc(jpp); + // Use Load-Wash-Elution test case + cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, "FV"); + disc.setDisc(jpp); - testJacobianForwardBackward(jpp, absTolFDpattern); - } + testJacobianForwardBackward(jpp, absTolFDpattern); } +} - void testJacobianWenoForwardBackwardFD(const std::string& uoType, const std::string& spatialMethod, int wenoOrder, double h, double absTol, double relTol) +void testJacobianForwardBackward(const char* uoType, DGparams disc, const double absTolFDpattern) +{ + SECTION("Forward vs backward flow Jacobian (DG integration mode " + std::to_string(disc.getIntegrationMode()) + ")") { - cadet::IModelBuilder* const mb = cadet::createModelBuilder(); - REQUIRE(nullptr != mb); - - SECTION("Forward vs backward flow Jacobian (WENO=" + std::to_string(wenoOrder) + ")") - { - // Use some test case parameters - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); - const unsigned int nComp = jpp.getInt("NCOMP"); + // Use Load-Wash-Elution test case + cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, "DG"); + disc.setDisc(jpp); - FVparams disc; - disc.setWenoOrder(wenoOrder); - cadet::IUnitOperation* const unit = createAndConfigureUnit(*mb, jpp, disc); + testJacobianForwardBackward(jpp, absTolFDpattern); + } +} - // Obtain memory for state, Jacobian multiply direction, Jacobian column - std::vector y(unit->numDofs(), 0.0); - std::vector jacDir(unit->numDofs(), 0.0); - std::vector jacCol1(unit->numDofs(), 0.0); - std::vector jacCol2(unit->numDofs(), 0.0); +void testJacobianWenoForwardBackwardFD(const std::string& uoType, const std::string& spatialMethod, int wenoOrder, + double h, double absTol, double relTol) +{ + cadet::IModelBuilder* const mb = cadet::createModelBuilder(); + REQUIRE(nullptr != mb); - // Fill state vector with some values - util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, unit->numDofs()); -// util::populate(y.data(), [](unsigned int idx) { return 1.0; }, unit->numDofs()); + SECTION("Forward vs backward flow Jacobian (WENO=" + std::to_string(wenoOrder) + ")") + { + // Use some test case parameters + cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); + const unsigned int nComp = jpp.getInt("NCOMP"); - // Setup matrices - const AdJacobianParams noAdParams{nullptr, nullptr, 0u}; - unit->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, noAdParams); + FVparams disc; + disc.setWenoOrder(wenoOrder); + cadet::IUnitOperation* const unit = createAndConfigureUnit(*mb, jpp, disc); - SECTION("Forward then backward flow (nonzero state)") - { - // Compute state Jacobian - const SimulationTime simTime{0.0, 0u}; - const ConstSimulationState simState{y.data(), nullptr}; - cadet::util::ThreadLocalStorage tls; - tls.resize(unit->threadLocalMemorySize()); + // Obtain memory for state, Jacobian multiply direction, Jacobian column + std::vector y(unit->numDofs(), 0.0); + std::vector jacDir(unit->numDofs(), 0.0); + std::vector jacCol1(unit->numDofs(), 0.0); + std::vector jacCol2(unit->numDofs(), 0.0); - unit->residualWithJacobian(simTime, simState, jacDir.data(), noAdParams, tls); - std::fill(jacDir.begin(), jacDir.end(), 0.0); + // Fill state vector with some values + util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, unit->numDofs()); + // util::populate(y.data(), [](unsigned int idx) { return 1.0; }, unit->numDofs()); - // Compare Jacobians - cadet::test::checkJacobianPatternFD(unit, unit, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data(), tls); - cadet::test::compareJacobianFD(unit, unit, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data(), tls, h, absTol, relTol); + // Setup matrices + const AdJacobianParams noAdParams{nullptr, nullptr, 0u}; + unit->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, noAdParams); - // Reverse flow - const bool paramSet = unit->setParameter(cadet::makeParamId(cadet::hashString("VELOCITY"), 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), -jpp.getDouble("VELOCITY")); - REQUIRE(paramSet); + SECTION("Forward then backward flow (nonzero state)") + { + // Compute state Jacobian + const SimulationTime simTime{0.0, 0u}; + const ConstSimulationState simState{y.data(), nullptr}; + cadet::util::ThreadLocalStorage tls; + tls.resize(unit->threadLocalMemorySize()); - // Setup - unit->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, noAdParams); + unit->residualWithJacobian(simTime, simState, jacDir.data(), noAdParams, tls); + std::fill(jacDir.begin(), jacDir.end(), 0.0); - // Compute state Jacobian - unit->residualWithJacobian(simTime, simState, jacDir.data(), noAdParams, tls); - std::fill(jacDir.begin(), jacDir.end(), 0.0); + // Compare Jacobians + cadet::test::checkJacobianPatternFD(unit, unit, y.data(), nullptr, jacDir.data(), jacCol1.data(), + jacCol2.data(), tls); + cadet::test::compareJacobianFD(unit, unit, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data(), + tls, h, absTol, relTol); + + // Reverse flow + const bool paramSet = unit->setParameter( + cadet::makeParamId(cadet::hashString("VELOCITY"), 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), + -jpp.getDouble("VELOCITY")); + REQUIRE(paramSet); - // Compare Jacobians - cadet::test::checkJacobianPatternFD(unit, unit, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data(), tls); - cadet::test::compareJacobianFD(unit, unit, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data(), tls, h, absTol, relTol); - } + // Setup + unit->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, noAdParams); - mb->destroyUnitOperation(unit); + // Compute state Jacobian + unit->residualWithJacobian(simTime, simState, jacDir.data(), noAdParams, tls); + std::fill(jacDir.begin(), jacDir.end(), 0.0); + + // Compare Jacobians + cadet::test::checkJacobianPatternFD(unit, unit, y.data(), nullptr, jacDir.data(), jacCol1.data(), + jacCol2.data(), tls); + cadet::test::compareJacobianFD(unit, unit, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data(), + tls, h, absTol, relTol); } - destroyModelBuilder(mb); + + mb->destroyUnitOperation(unit); } + destroyModelBuilder(mb); +} - void testTimeDerivativeJacobianFD(const std::string& uoType, const std::string& spatialMethod, double h, double absTol, double relTol) - { - cadet::IModelBuilder* const mb = cadet::createModelBuilder(); - REQUIRE(nullptr != mb); +void testTimeDerivativeJacobianFD(const std::string& uoType, const std::string& spatialMethod, double h, double absTol, + double relTol) +{ + cadet::IModelBuilder* const mb = cadet::createModelBuilder(); + REQUIRE(nullptr != mb); - // Use some test case parameters - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); + // Use some test case parameters + cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); - for (int bindMode = 0; bindMode < 2; ++bindMode) + for (int bindMode = 0; bindMode < 2; ++bindMode) + { + const bool isKinetic = bindMode; + SECTION(isKinetic ? "Kinetic binding" : "Quasi-stationary binding") { - const bool isKinetic = bindMode; - SECTION(isKinetic ? "Kinetic binding" : "Quasi-stationary binding") - { - cadet::test::setBindingMode(jpp, isKinetic); - - cadet::IUnitOperation* const unit = createAndConfigureUnit(*mb, jpp); + cadet::test::setBindingMode(jpp, isKinetic); - cadet::util::ThreadLocalStorage tls; - tls.resize(unit->threadLocalMemorySize()); + cadet::IUnitOperation* const unit = createAndConfigureUnit(*mb, jpp); - // Obtain memory for state, Jacobian multiply direction, Jacobian column - const unsigned int nDof = unit->numDofs(); - std::vector y(nDof, 0.0); - std::vector yDot(nDof, 0.0); - std::vector jacDir(nDof, 0.0); - std::vector jacCol1(nDof, 0.0); - std::vector jacCol2(nDof, 0.0); + cadet::util::ThreadLocalStorage tls; + tls.resize(unit->threadLocalMemorySize()); - // Fill state vectors with some values - util::populate(y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); - util::populate(yDot.data(), [=](unsigned int idx) { - return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; - }, nDof); + // Obtain memory for state, Jacobian multiply direction, Jacobian column + const unsigned int nDof = unit->numDofs(); + std::vector y(nDof, 0.0); + std::vector yDot(nDof, 0.0); + std::vector jacDir(nDof, 0.0); + std::vector jacCol1(nDof, 0.0); + std::vector jacCol2(nDof, 0.0); + + // Fill state vectors with some values + util::populate(y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); + util::populate( + yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); - // Setup matrices - unit->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, AdJacobianParams{nullptr, nullptr, 0u}); + // Setup matrices + unit->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, + AdJacobianParams{nullptr, nullptr, 0u}); - // Compare Jacobians - cadet::test::compareTimeDerivativeJacobianFD(unit, unit, y.data(), yDot.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), tls, h, absTol, relTol); + // Compare Jacobians + cadet::test::compareTimeDerivativeJacobianFD(unit, unit, y.data(), yDot.data(), jacDir.data(), + jacCol1.data(), jacCol2.data(), tls, h, absTol, relTol); - mb->destroyUnitOperation(unit); - } + mb->destroyUnitOperation(unit); } - - destroyModelBuilder(mb); } - void testArrowHeadJacobianFD(const std::string& uoType, double h, double absTol, double relTol) - { - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, "FV"); - testArrowHeadJacobianFD(jpp, h, absTol, relTol); - } + destroyModelBuilder(mb); +} - void testArrowHeadJacobianFD(const std::string& uoType, bool dynamicBinding, double h, double absTol, double relTol) - { - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, "FV"); - setBindingMode(jpp, dynamicBinding); - testArrowHeadJacobianFD(jpp, h, absTol, relTol); - } +void testArrowHeadJacobianFD(const std::string& uoType, double h, double absTol, double relTol) +{ + cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, "FV"); + testArrowHeadJacobianFD(jpp, h, absTol, relTol); +} - void testArrowHeadJacobianFDVariableParSurfDiff(const std::string& uoType, double h, double absTol, double relTol) - { - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, "FV"); - setBindingMode(jpp, false); - { - auto ms = util::makeOptionalGroupScope(jpp, "model"); - auto us = util::makeOptionalGroupScope(jpp, "unit_000"); +void testArrowHeadJacobianFD(const std::string& uoType, bool dynamicBinding, double h, double absTol, double relTol) +{ + cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, "FV"); + setBindingMode(jpp, dynamicBinding); + testArrowHeadJacobianFD(jpp, h, absTol, relTol); +} - jpp.set("PAR_SURFDIFFUSION_DEP", "LIQUID_SALT_EXPONENTIAL"); - jpp.set("PAR_SURFDIFFUSION_EXPFACTOR", std::vector{ 0.8, 1.6 }); - jpp.set("PAR_SURFDIFFUSION_EXPARGMULT", std::vector{ 1.3, 2.1 }); - } +void testArrowHeadJacobianFDVariableParSurfDiff(const std::string& uoType, double h, double absTol, double relTol) +{ + cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, "FV"); + setBindingMode(jpp, false); + { + auto ms = util::makeOptionalGroupScope(jpp, "model"); + auto us = util::makeOptionalGroupScope(jpp, "unit_000"); - testArrowHeadJacobianFD(jpp, h, absTol, relTol); + jpp.set("PAR_SURFDIFFUSION_DEP", "LIQUID_SALT_EXPONENTIAL"); + jpp.set("PAR_SURFDIFFUSION_EXPFACTOR", std::vector{0.8, 1.6}); + jpp.set("PAR_SURFDIFFUSION_EXPARGMULT", std::vector{1.3, 2.1}); } - void testJacobianADVariableParSurfDiff(const std::string& uoType, const std::string& spatialMethod, bool dynamicBinding) - { - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); - setBindingMode(jpp, dynamicBinding); - { - auto ms = util::makeOptionalGroupScope(jpp, "model"); - auto us = util::makeOptionalGroupScope(jpp, "unit_000"); + testArrowHeadJacobianFD(jpp, h, absTol, relTol); +} - jpp.set("PAR_SURFDIFFUSION_DEP", "LIQUID_SALT_EXPONENTIAL"); - jpp.set("PAR_SURFDIFFUSION_EXPFACTOR", std::vector{ 0.8, 1.6 }); - jpp.set("PAR_SURFDIFFUSION_EXPARGMULT", std::vector{ 1.3, 2.1 }); - } +void testJacobianADVariableParSurfDiff(const std::string& uoType, const std::string& spatialMethod, bool dynamicBinding) +{ + cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); + setBindingMode(jpp, dynamicBinding); + { + auto ms = util::makeOptionalGroupScope(jpp, "model"); + auto us = util::makeOptionalGroupScope(jpp, "unit_000"); - testJacobianAD(jpp); + jpp.set("PAR_SURFDIFFUSION_DEP", "LIQUID_SALT_EXPONENTIAL"); + jpp.set("PAR_SURFDIFFUSION_EXPFACTOR", std::vector{0.8, 1.6}); + jpp.set("PAR_SURFDIFFUSION_EXPARGMULT", std::vector{1.3, 2.1}); } - void testArrowHeadJacobianFD(cadet::JsonParameterProvider& jpp, double h, double absTol, double relTol) - { - cadet::IModelBuilder* const mb = cadet::createModelBuilder(); - REQUIRE(nullptr != mb); + testJacobianAD(jpp); +} - cadet::IUnitOperation* const unitAna = unitoperation::createAndConfigureUnit(jpp, *mb); - cadet::IUnitOperation* const unitFD = unitoperation::createAndConfigureUnit(jpp, *mb); +void testArrowHeadJacobianFD(cadet::JsonParameterProvider& jpp, double h, double absTol, double relTol) +{ + cadet::IModelBuilder* const mb = cadet::createModelBuilder(); + REQUIRE(nullptr != mb); + + cadet::IUnitOperation* const unitAna = unitoperation::createAndConfigureUnit(jpp, *mb); + cadet::IUnitOperation* const unitFD = unitoperation::createAndConfigureUnit(jpp, *mb); + + cadet::util::ThreadLocalStorage tls; + tls.resize(unitAna->threadLocalMemorySize()); + + // Obtain offset to fluxes + const unsigned int fluxOffset = fluxOffsetOfColumnUnitOp(unitFD); + + // Obtain memory for state, Jacobian multiply direction, Jacobian column + std::vector y(unitFD->numDofs(), 0.0); + std::vector jacDir(unitFD->numDofs(), 0.0); + std::vector jacCol1(unitFD->numDofs(), 0.0); + std::vector jacCol2(unitFD->numDofs(), 0.0); + + // Fill state vector with some values + util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, unitAna->numDofs()); + // util::populate(y.data(), [](unsigned int idx) { return 1.0; }, unitAna->numDofs()); + + // Setup matrices + const AdJacobianParams noParams{nullptr, nullptr, 0u}; + unitAna->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, noParams); + unitFD->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, noParams); + + // Compute state Jacobian + unitAna->residualWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), nullptr}, jacDir.data(), + noParams, tls); + unitFD->residualWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), nullptr}, jacDir.data(), + noParams, tls); + std::fill(jacDir.begin(), jacDir.end(), 0.0); + + // Compare Jacobians + cadet::test::compareJacobianArrowHeadFD( + [=, &tls](double const* lDir, double* res) -> void { + unitFD->residual(SimulationTime{0.0, 0u}, ConstSimulationState{lDir, nullptr}, res, tls); + }, + [&](double const* lDir, double* res) -> void { + unitAna->multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), nullptr}, lDir, 1.0, + 0.0, res); + }, + y.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), unitFD->numDofs(), fluxOffset, h, absTol, relTol); + + mb->destroyUnitOperation(unitAna); + mb->destroyUnitOperation(unitFD); + destroyModelBuilder(mb); +} - cadet::util::ThreadLocalStorage tls; - tls.resize(unitAna->threadLocalMemorySize()); +void testFwdSensJacobians(cadet::JsonParameterProvider jpp, double h, double absTol, double relTol, + const bool hasBinding) +{ + cadet::IModelBuilder* const mb = cadet::createModelBuilder(); + REQUIRE(nullptr != mb); - // Obtain offset to fluxes - const unsigned int fluxOffset = fluxOffsetOfColumnUnitOp(unitFD); + for (int bindMode = 0; bindMode < static_cast(hasBinding) + 1; ++bindMode) + { + const bool isKinetic = bindMode; + SECTION(hasBinding ? "No binding" : (isKinetic ? "Kinetic binding" : "Quasi-stationary binding")) + { + if (hasBinding) + cadet::test::setBindingMode(jpp, isKinetic); + cadet::IUnitOperation* const unit = createAndConfigureUnit(*mb, jpp); - // Obtain memory for state, Jacobian multiply direction, Jacobian column - std::vector y(unitFD->numDofs(), 0.0); - std::vector jacDir(unitFD->numDofs(), 0.0); - std::vector jacCol1(unitFD->numDofs(), 0.0); - std::vector jacCol2(unitFD->numDofs(), 0.0); + // Enable AD + cadet::ad::setDirections(cadet::ad::getMaxDirections()); + cadet::active* adRes = new cadet::active[unit->numDofs()]; + const AdJacobianParams adParams{adRes, nullptr, 0}; + unit->prepareADvectors(adParams); - // Fill state vector with some values - util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, unitAna->numDofs()); -// util::populate(y.data(), [](unsigned int idx) { return 1.0; }, unitAna->numDofs()); + // Add dispersion parameter sensitivity + REQUIRE(unit->setSensitiveParameter(makeParamId(hashString("COL_DISPERSION"), 0, CompIndep, ParTypeIndep, + BoundStateIndep, ReactionIndep, SectionIndep), + 0, 1.0)); - // Setup matrices - const AdJacobianParams noParams{nullptr, nullptr, 0u}; - unitAna->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, noParams); - unitFD->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, noParams); + // Obtain memory for state, Jacobian multiply direction, Jacobian column + const unsigned int nDof = unit->numDofs(); + const std::vector zeros(nDof, 0.0); + const std::vector ones(nDof, 1.0); + std::vector y(nDof, 0.0); + std::vector yDot(nDof, 0.0); + std::vector jacDir(nDof, 0.0); + std::vector jacCol1(nDof, 0.0); + std::vector jacCol2(nDof, 0.0); + std::vector temp1(nDof, 0.0); + std::vector temp2(nDof, 0.0); + std::vector temp3(nDof, 0.0); + + std::vector yS(1, zeros.data()); + std::vector ySdot(1, zeros.data()); + std::vector resS(1, nullptr); - // Compute state Jacobian - unitAna->residualWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), nullptr}, jacDir.data(), noParams, tls); - unitFD->residualWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), nullptr}, jacDir.data(), noParams, tls); - std::fill(jacDir.begin(), jacDir.end(), 0.0); + cadet::util::ThreadLocalStorage tls; + tls.resize(unit->threadLocalMemorySize()); - // Compare Jacobians - cadet::test::compareJacobianArrowHeadFD( - [=, &tls](double const* lDir, double* res) -> void { unitFD->residual(SimulationTime{0.0, 0u}, ConstSimulationState{lDir, nullptr}, res, tls); }, - [&](double const* lDir, double* res) -> void { unitAna->multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), nullptr}, lDir, 1.0, 0.0, res); }, - y.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), unitFD->numDofs(), fluxOffset, h, absTol, relTol); + // Fill state vector with some values + util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); + util::populate( + yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); - mb->destroyUnitOperation(unitAna); - mb->destroyUnitOperation(unitFD); - destroyModelBuilder(mb); + // Setup matrices + unit->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, adParams); + + // Calculate Jacobian + unit->residualWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), yDot.data()}, + jacDir.data(), adParams, tls); + + // Calculate parameter derivative + unit->residualSensFwdAdOnly(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), yDot.data()}, adRes, + tls); + + // Check state Jacobian + cadet::test::compareJacobianFD( + [&](double const* lDir, double* res) -> void { + yS[0] = lDir; + resS[0] = res; + unit->residualSensFwdCombine(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), yDot.data()}, + yS, ySdot, resS, adRes, temp1.data(), temp2.data(), temp3.data()); + }, + [&](double const* lDir, double* res) -> void { + unit->multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), yDot.data()}, + lDir, 1.0, 0.0, res); + }, + zeros.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), nDof, h, absTol, relTol); + + // Reset evaluation point + yS[0] = zeros.data(); + ySdot[0] = zeros.data(); + + // Check time derivative Jacobian + cadet::test::compareJacobianFD( + [&](double const* lDir, double* res) -> void { + ySdot[0] = lDir; + resS[0] = res; + unit->residualSensFwdCombine(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), yDot.data()}, + yS, ySdot, resS, adRes, temp1.data(), temp2.data(), temp3.data()); + }, + [&](double const* lDir, double* res) -> void { + unit->multiplyWithDerivativeJacobian(SimulationTime{0.0, 0u}, + ConstSimulationState{y.data(), yDot.data()}, lDir, res); + }, + zeros.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), nDof, h, absTol, relTol); + + delete[] adRes; + mb->destroyUnitOperation(unit); + } } - void testFwdSensJacobians(cadet::JsonParameterProvider jpp, double h, double absTol, double relTol, const bool hasBinding) - { - cadet::IModelBuilder* const mb = cadet::createModelBuilder(); - REQUIRE(nullptr != mb); - - for (int bindMode = 0; bindMode < static_cast(hasBinding) + 1; ++bindMode) - { - const bool isKinetic = bindMode; - SECTION(hasBinding ? "No binding" : (isKinetic ? "Kinetic binding" : "Quasi-stationary binding")) - { - if (hasBinding) - cadet::test::setBindingMode(jpp, isKinetic); - cadet::IUnitOperation* const unit = createAndConfigureUnit(*mb, jpp); - - // Enable AD - cadet::ad::setDirections(cadet::ad::getMaxDirections()); - cadet::active* adRes = new cadet::active[unit->numDofs()]; - const AdJacobianParams adParams{adRes, nullptr, 0}; - unit->prepareADvectors(adParams); - - // Add dispersion parameter sensitivity - REQUIRE(unit->setSensitiveParameter(makeParamId(hashString("COL_DISPERSION"), 0, CompIndep, ParTypeIndep, BoundStateIndep, ReactionIndep, SectionIndep), 0, 1.0)); - - // Obtain memory for state, Jacobian multiply direction, Jacobian column - const unsigned int nDof = unit->numDofs(); - const std::vector zeros(nDof, 0.0); - const std::vector ones(nDof, 1.0); - std::vector y(nDof, 0.0); - std::vector yDot(nDof, 0.0); - std::vector jacDir(nDof, 0.0); - std::vector jacCol1(nDof, 0.0); - std::vector jacCol2(nDof, 0.0); - std::vector temp1(nDof, 0.0); - std::vector temp2(nDof, 0.0); - std::vector temp3(nDof, 0.0); - - std::vector yS(1, zeros.data()); - std::vector ySdot(1, zeros.data()); - std::vector resS(1, nullptr); - - cadet::util::ThreadLocalStorage tls; - tls.resize(unit->threadLocalMemorySize()); - - // Fill state vector with some values - util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); - util::populate(yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); - - // Setup matrices - unit->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, adParams); - - // Calculate Jacobian - unit->residualWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), yDot.data()}, jacDir.data(), adParams, tls); - - // Calculate parameter derivative - unit->residualSensFwdAdOnly(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), yDot.data()}, adRes, tls); - - // Check state Jacobian - cadet::test::compareJacobianFD( - [&](double const* lDir, double* res) -> void { - yS[0] = lDir; - resS[0] = res; - unit->residualSensFwdCombine(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), yDot.data()}, yS, ySdot, resS, adRes, temp1.data(), temp2.data(), temp3.data()); - }, - [&](double const* lDir, double* res) -> void { unit->multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), yDot.data()}, lDir, 1.0, 0.0, res); }, - zeros.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), nDof, h, absTol, relTol); - - // Reset evaluation point - yS[0] = zeros.data(); - ySdot[0] = zeros.data(); - - // Check time derivative Jacobian - cadet::test::compareJacobianFD( - [&](double const* lDir, double* res) -> void { - ySdot[0] = lDir; - resS[0] = res; - unit->residualSensFwdCombine(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), yDot.data()}, yS, ySdot, resS, adRes, temp1.data(), temp2.data(), temp3.data()); - }, - [&](double const* lDir, double* res) -> void { unit->multiplyWithDerivativeJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), yDot.data()}, lDir, res); }, - zeros.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), nDof, h, absTol, relTol); - - delete[] adRes; - mb->destroyUnitOperation(unit); - } - } + destroyModelBuilder(mb); +} - destroyModelBuilder(mb); - } +void testFwdSensSolutionFD(const std::string& uoType, const std::string& spatialMethod, bool disableSensErrorTest, + double const* fdStepSize, double const* absTols, double const* relTols, + double const* passRates) +{ + const std::vector params = { + cadet::makeParamId("COL_DISPERSION", 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, + cadet::ReactionIndep, cadet::SectionIndep), + cadet::makeParamId("CONST_COEFF", 1, 0, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, 0), + cadet::makeParamId("SMA_KA", 0, 1, cadet::ParTypeIndep, 0, cadet::ReactionIndep, cadet::SectionIndep), + cadet::makeParamId("CONNECTION", cadet::UnitOpIndep, cadet::CompIndep, cadet::ParTypeIndep, 1, 0, 0), + }; + const std::vector paramNames = {"COL_DISPERSION", "CONST_COEFF", "SMA_KA", "CONNECTION"}; + const double absTolSens[] = {1e-12, 1e-6, 1e-6, 1e-6}; - void testFwdSensSolutionFD(const std::string& uoType, const std::string& spatialMethod, bool disableSensErrorTest, double const* fdStepSize, double const* absTols, double const* relTols, double const* passRates) + for (int bindMode = 0; bindMode < 2; ++bindMode) { - const std::vector params = { - cadet::makeParamId("COL_DISPERSION", 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), - cadet::makeParamId("CONST_COEFF", 1, 0, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, 0), - cadet::makeParamId("SMA_KA", 0, 1, cadet::ParTypeIndep, 0, cadet::ReactionIndep, cadet::SectionIndep), - cadet::makeParamId("CONNECTION", cadet::UnitOpIndep, cadet::CompIndep, cadet::ParTypeIndep, 1, 0, 0), - }; - const std::vector paramNames = {"COL_DISPERSION", "CONST_COEFF", "SMA_KA", "CONNECTION"}; - const double absTolSens[] = {1e-12, 1e-6, 1e-6, 1e-6}; - - for (int bindMode = 0; bindMode < 2; ++bindMode) + const bool isKinetic = bindMode; + for (std::size_t n = 0; n < params.size(); ++n) { - const bool isKinetic = bindMode; - for (std::size_t n = 0; n < params.size(); ++n) + SECTION("Parameter " + std::string(paramNames[n]) + + (isKinetic ? " Kinetic binding" : " Quasi-stationary binding")) { - SECTION("Parameter " + std::string(paramNames[n]) + (isKinetic ? " Kinetic binding" : " Quasi-stationary binding")) + const double absTol = absTols[n]; + const double relTol = relTols[n]; + const double passRate = passRates[n]; + const cadet::ParameterId& curParam = params[n]; + const double h = fdStepSize[n]; + + // Setup simulation including forward sensitivities + cadet::JsonParameterProvider jppAna = createLWE(uoType, spatialMethod); + cadet::test::setBindingMode(jppAna, isKinetic); + cadet::test::column::setCrossSectionArea(jppAna, uoType == "LUMPED_RATE_MODEL_WITHOUT_PORES", 0); + cadet::test::addSensitivity(jppAna, paramNames[n], curParam, absTolSens[n]); + cadet::test::returnSensitivities(jppAna, 0); + if (disableSensErrorTest) + cadet::test::disableSensitivityErrorTest(jppAna); + + // Run simulation + cadet::Driver drv; + drv.configure(jppAna); + REQUIRE(drv.simulator()->numSensParams() == 1); + drv.run(); + + // Setup FD simulation + cadet::JsonParameterProvider jppFD = createLWE(uoType, spatialMethod); + cadet::test::setBindingMode(jppFD, isKinetic); + cadet::test::column::setCrossSectionArea(jppFD, uoType == "LUMPED_RATE_MODEL_WITHOUT_PORES", 0); + + // Configure FD simulation + cadet::Driver drvLeft; + drvLeft.configure(jppFD); + + // Extract parameter values + const double baseVal = drvLeft.simulator()->model()->getParameterDouble(curParam); + REQUIRE(!std::isnan(baseVal)); + + // Run left FD point + drvLeft.simulator()->setParameterValue(curParam, baseVal * (1.0 - h)); + drvLeft.run(); + + // Configure and run right FD simulation + cadet::Driver drvRight; + drvRight.configure(jppFD); + drvRight.simulator()->setParameterValue(curParam, baseVal * (1.0 + h)); + drvRight.run(); + + // Get data from simulation + cadet::InternalStorageUnitOpRecorder const* const simData = drv.solution()->unitOperation(0); + const unsigned int nComp = simData->numComponents(); + const unsigned int nPorts = simData->numInletPorts(); + const unsigned int nDataPoints = simData->numDataPoints() * nComp * nPorts; + double const* const time = drv.solution()->time(); + double const* outlet = simData->sensOutlet(0); + double const* outletL = drvLeft.solution()->unitOperation(0)->outlet(); + double const* outletR = drvRight.solution()->unitOperation(0)->outlet(); + + // Compare + const double actStepSize = 2.0 * h * baseVal; + unsigned int numPassed = 0; + for (unsigned int i = 0; i < nDataPoints; ++i, ++outlet, ++outletL, ++outletR) { - const double absTol = absTols[n]; - const double relTol = relTols[n]; - const double passRate = passRates[n]; - const cadet::ParameterId& curParam = params[n]; - const double h = fdStepSize[n]; - - // Setup simulation including forward sensitivities - cadet::JsonParameterProvider jppAna = createLWE(uoType, spatialMethod); - cadet::test::setBindingMode(jppAna, isKinetic); - cadet::test::column::setCrossSectionArea(jppAna, uoType == "LUMPED_RATE_MODEL_WITHOUT_PORES", 0); - cadet::test::addSensitivity(jppAna, paramNames[n], curParam, absTolSens[n]); - cadet::test::returnSensitivities(jppAna, 0); - if (disableSensErrorTest) - cadet::test::disableSensitivityErrorTest(jppAna); - - // Run simulation - cadet::Driver drv; - drv.configure(jppAna); - REQUIRE(drv.simulator()->numSensParams() == 1); - drv.run(); - - // Setup FD simulation - cadet::JsonParameterProvider jppFD = createLWE(uoType, spatialMethod); - cadet::test::setBindingMode(jppFD, isKinetic); - cadet::test::column::setCrossSectionArea(jppFD, uoType == "LUMPED_RATE_MODEL_WITHOUT_PORES", 0); - - // Configure FD simulation - cadet::Driver drvLeft; - drvLeft.configure(jppFD); - - // Extract parameter values - const double baseVal = drvLeft.simulator()->model()->getParameterDouble(curParam); - REQUIRE(!std::isnan(baseVal)); - - // Run left FD point - drvLeft.simulator()->setParameterValue(curParam, baseVal * (1.0 - h)); - drvLeft.run(); - - // Configure and run right FD simulation - cadet::Driver drvRight; - drvRight.configure(jppFD); - drvRight.simulator()->setParameterValue(curParam, baseVal * (1.0 + h)); - drvRight.run(); - - // Get data from simulation - cadet::InternalStorageUnitOpRecorder const* const simData = drv.solution()->unitOperation(0); - const unsigned int nComp = simData->numComponents(); - const unsigned int nPorts = simData->numInletPorts(); - const unsigned int nDataPoints = simData->numDataPoints() * nComp * nPorts; - double const* const time = drv.solution()->time(); - double const* outlet = simData->sensOutlet(0); - double const* outletL = drvLeft.solution()->unitOperation(0)->outlet(); - double const* outletR = drvRight.solution()->unitOperation(0)->outlet(); - - // Compare - const double actStepSize = 2.0 * h * baseVal; - unsigned int numPassed = 0; - for (unsigned int i = 0; i < nDataPoints; ++i, ++outlet, ++outletL, ++outletR) - { - const double cmpVal = *outlet; - const double fdVal = ((*outletR) - (*outletL)) / actStepSize; - const unsigned int comp = (i % (nComp * nPorts)) % nComp; - const unsigned int port = (i % (nComp * nPorts)) / nComp; - const unsigned int timeIdx = i / (nComp * nPorts); - - INFO("Time " << time[timeIdx] << " Port " << port << " Component " << comp << " time point idx " << timeIdx); - CHECK(fdVal == makeApprox(*outlet, relTol, absTol)); - - const bool relativeOK = std::abs(*outlet - fdVal) <= relTol * std::abs(*outlet); - if (relativeOK) - ++numPassed; - } - - const double ratio = static_cast(numPassed) / static_cast(nDataPoints); - CAPTURE(ratio); - CHECK(ratio >= passRate); + const double cmpVal = *outlet; + const double fdVal = ((*outletR) - (*outletL)) / actStepSize; + const unsigned int comp = (i % (nComp * nPorts)) % nComp; + const unsigned int port = (i % (nComp * nPorts)) / nComp; + const unsigned int timeIdx = i / (nComp * nPorts); + + INFO("Time " << time[timeIdx] << " Port " << port << " Component " << comp << " time point idx " + << timeIdx); + CHECK(fdVal == makeApprox(*outlet, relTol, absTol)); + + const bool relativeOK = std::abs(*outlet - fdVal) <= relTol * std::abs(*outlet); + if (relativeOK) + ++numPassed; } + + const double ratio = static_cast(numPassed) / static_cast(nDataPoints); + CAPTURE(ratio); + CHECK(ratio >= passRate); } } } +} - void testFwdSensSolutionForwardBackward(const std::string& uoType, const std::string& spatialMethod, double const* absTols, double const* relTols, double const* passRates) - { - const std::vector params = { - cadet::makeParamId("COL_DISPERSION", 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), - cadet::makeParamId("CONST_COEFF", 1, 0, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, 0), - cadet::makeParamId("SMA_KA", 0, 1, cadet::ParTypeIndep, 0, cadet::ReactionIndep, cadet::SectionIndep), - cadet::makeParamId("CONNECTION", cadet::UnitOpIndep, cadet::CompIndep, cadet::ParTypeIndep, 1, 0, 0), - }; - const std::vector paramNames = {"COL_DISPERSION", "CONST_COEFF", "SMA_KA", "CONNECTION"}; - const double absTolSens[] = {1e-12, 1e-6, 1e-6, 1e-6}; +void testFwdSensSolutionForwardBackward(const std::string& uoType, const std::string& spatialMethod, + double const* absTols, double const* relTols, double const* passRates) +{ + const std::vector params = { + cadet::makeParamId("COL_DISPERSION", 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, + cadet::ReactionIndep, cadet::SectionIndep), + cadet::makeParamId("CONST_COEFF", 1, 0, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, 0), + cadet::makeParamId("SMA_KA", 0, 1, cadet::ParTypeIndep, 0, cadet::ReactionIndep, cadet::SectionIndep), + cadet::makeParamId("CONNECTION", cadet::UnitOpIndep, cadet::CompIndep, cadet::ParTypeIndep, 1, 0, 0), + }; + const std::vector paramNames = {"COL_DISPERSION", "CONST_COEFF", "SMA_KA", "CONNECTION"}; + const double absTolSens[] = {1e-12, 1e-6, 1e-6, 1e-6}; - for (int bindMode = 0; bindMode < 2; ++bindMode) + for (int bindMode = 0; bindMode < 2; ++bindMode) + { + const bool isKinetic = bindMode; + for (std::size_t n = 0; n < params.size(); ++n) { - const bool isKinetic = bindMode; - for (std::size_t n = 0; n < params.size(); ++n) + SECTION("Parameter " + std::string(paramNames[n]) + + (isKinetic ? " Kinetic binding" : " Quasi-stationary binding")) { - SECTION("Parameter " + std::string(paramNames[n]) + (isKinetic ? " Kinetic binding" : " Quasi-stationary binding")) - { - const double absTol = absTols[n]; - const double relTol = relTols[n]; - const double passRate = passRates[n]; - const cadet::ParameterId& curParam = params[n]; - - // Setup simulation including forward sensitivities - cadet::JsonParameterProvider jpp = createLWE(uoType, spatialMethod); - cadet::test::setBindingMode(jpp, isKinetic); - cadet::test::column::setCrossSectionArea(jpp, uoType == "LUMPED_RATE_MODEL_WITHOUT_PORES", 1); - cadet::test::addSensitivity(jpp, paramNames[n], curParam, absTolSens[n]); - cadet::test::returnSensitivities(jpp, 0, true); - cadet::test::disableSensitivityErrorTest(jpp); - - // Run simulation - cadet::Driver drvFwd; - drvFwd.configure(jpp); - drvFwd.run(); - - // Run simulation with reversed flow - reverseFlow(jpp); - cadet::Driver drvBwd; - drvBwd.configure(jpp); - drvBwd.run(); - - // Get data from simulation - cadet::InternalStorageUnitOpRecorder const* const fwdData = drvFwd.solution()->unitOperation(0); - cadet::InternalStorageUnitOpRecorder const* const bwdData = drvBwd.solution()->unitOperation(0); - - const unsigned int nComp = fwdData->numComponents(); - const unsigned int nPorts = fwdData->numInletPorts(); - const unsigned int nDataPoints = fwdData->numDataPoints() * nComp * nPorts; - double const* const time = drvFwd.solution()->time(); - double const* fwdOutlet = fwdData->sensOutlet(0); - double const* bwdOutlet = bwdData->sensOutlet(0); - - // Compare - unsigned int numPassed = 0; - for (unsigned int i = 0; i < nDataPoints; ++i, ++fwdOutlet, ++bwdOutlet) - { - const unsigned int comp = (i % (nComp * nPorts)) % nComp; - const unsigned int port = (i % (nComp * nPorts)) / nComp; - const unsigned int timeIdx = i / (nComp * nPorts); - - INFO("Time " << time[timeIdx] << " Port " << port << " Component " << comp << " time point idx " << timeIdx); - CHECK(*bwdOutlet == makeApprox(*fwdOutlet, relTol, absTol)); - - const bool relativeOK = std::abs(*bwdOutlet - *fwdOutlet) <= relTol * std::abs(*fwdOutlet); - if (relativeOK) - ++numPassed; - } - - const double ratio = static_cast(numPassed) / static_cast(nDataPoints); - CAPTURE(ratio); - CHECK(ratio >= passRate); - } - } - } - } + const double absTol = absTols[n]; + const double relTol = relTols[n]; + const double passRate = passRates[n]; + const cadet::ParameterId& curParam = params[n]; - void testConsistentInitializationLinearBinding(const std::string& uoType, const std::string& spatialMethod, double consTol, double absTol, const int reqBnd, const int useAD) - { - cadet::IModelBuilder* const mb = cadet::createModelBuilder(); - REQUIRE(nullptr != mb); + // Setup simulation including forward sensitivities + cadet::JsonParameterProvider jpp = createLWE(uoType, spatialMethod); + cadet::test::setBindingMode(jpp, isKinetic); + cadet::test::column::setCrossSectionArea(jpp, uoType == "LUMPED_RATE_MODEL_WITHOUT_PORES", 1); + cadet::test::addSensitivity(jpp, paramNames[n], curParam, absTolSens[n]); + cadet::test::returnSensitivities(jpp, 0, true); + cadet::test::disableSensitivityErrorTest(jpp); - for (int bindingMode = 0; bindingMode < 2; ++bindingMode) - { - if ((bindingMode == 0 && reqBnd == 1) || (bindingMode == 1 && reqBnd == 0)) - continue; - const bool isKinetic = (bindingMode == 0); - for (int adMode = 0; adMode < 2; ++adMode) - { - if ((adMode == 0 && useAD == 1) || (adMode == 1 && useAD == 0)) - continue; - const bool adEnabled = (adMode > 0); - SECTION(std::string(isKinetic ? " Kinetic binding" : " Quasi-stationary binding") + " with AD " + (adEnabled ? "enabled" : "disabled")) - { - // Use some test case parameters - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); - cadet::test::setBindingMode(jpp, isKinetic); - cadet::IUnitOperation* const unit = createAndConfigureUnit(*mb, jpp); + // Run simulation + cadet::Driver drvFwd; + drvFwd.configure(jpp); + drvFwd.run(); - // Fill state vector with given initial values - std::vector y(unit->numDofs(), 0.0); - util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, unit->numDofs()); + // Run simulation with reversed flow + reverseFlow(jpp); + cadet::Driver drvBwd; + drvBwd.configure(jpp); + drvBwd.run(); + + // Get data from simulation + cadet::InternalStorageUnitOpRecorder const* const fwdData = drvFwd.solution()->unitOperation(0); + cadet::InternalStorageUnitOpRecorder const* const bwdData = drvBwd.solution()->unitOperation(0); + + const unsigned int nComp = fwdData->numComponents(); + const unsigned int nPorts = fwdData->numInletPorts(); + const unsigned int nDataPoints = fwdData->numDataPoints() * nComp * nPorts; + double const* const time = drvFwd.solution()->time(); + double const* fwdOutlet = fwdData->sensOutlet(0); + double const* bwdOutlet = bwdData->sensOutlet(0); + + // Compare + unsigned int numPassed = 0; + for (unsigned int i = 0; i < nDataPoints; ++i, ++fwdOutlet, ++bwdOutlet) + { + const unsigned int comp = (i % (nComp * nPorts)) % nComp; + const unsigned int port = (i % (nComp * nPorts)) / nComp; + const unsigned int timeIdx = i / (nComp * nPorts); - unitoperation::testConsistentInitialization(unit, adEnabled, y.data(), consTol, absTol); + INFO("Time " << time[timeIdx] << " Port " << port << " Component " << comp << " time point idx " + << timeIdx); + CHECK(*bwdOutlet == makeApprox(*fwdOutlet, relTol, absTol)); - mb->destroyUnitOperation(unit); + const bool relativeOK = std::abs(*bwdOutlet - *fwdOutlet) <= relTol * std::abs(*fwdOutlet); + if (relativeOK) + ++numPassed; } + + const double ratio = static_cast(numPassed) / static_cast(nDataPoints); + CAPTURE(ratio); + CHECK(ratio >= passRate); } } - destroyModelBuilder(mb); } +} - void testConsistentInitializationSMABinding(const std::string& uoType, const std::string& spatialMethod, double const* const initState, double consTol, double absTol, const int reqBnd, const int useAD) - { - cadet::IModelBuilder* const mb = cadet::createModelBuilder(); - REQUIRE(nullptr != mb); +void testConsistentInitializationLinearBinding(const std::string& uoType, const std::string& spatialMethod, + double consTol, double absTol, const int reqBnd, const int useAD) +{ + cadet::IModelBuilder* const mb = cadet::createModelBuilder(); + REQUIRE(nullptr != mb); - for (int bindingMode = 0; bindingMode < 2; ++bindingMode) + for (int bindingMode = 0; bindingMode < 2; ++bindingMode) + { + if ((bindingMode == 0 && reqBnd == 1) || (bindingMode == 1 && reqBnd == 0)) + continue; + const bool isKinetic = (bindingMode == 0); + for (int adMode = 0; adMode < 2; ++adMode) { - if ((bindingMode == 0 && reqBnd == 1) || (bindingMode == 1 && reqBnd == 0)) + if ((adMode == 0 && useAD == 1) || (adMode == 1 && useAD == 0)) continue; - const bool isKinetic = (bindingMode == 0); - for (int adMode = 0; adMode < 2; ++adMode) + const bool adEnabled = (adMode > 0); + SECTION(std::string(isKinetic ? " Kinetic binding" : " Quasi-stationary binding") + " with AD " + + (adEnabled ? "enabled" : "disabled")) { - if ((adMode == 0 && useAD == 1) || (adMode == 1 && useAD == 0)) - continue; - const bool adEnabled = (adMode > 0); - SECTION(std::string(isKinetic ? " Kinetic binding" : " Quasi-stationary binding") + " with AD " + (adEnabled ? "enabled" : "disabled")) - { - // Use some test case parameters - cadet::JsonParameterProvider jpp = createColumnWithSMA(uoType, spatialMethod); - cadet::test::setBindingMode(jpp, isKinetic); - cadet::IUnitOperation* const unit = createAndConfigureUnit(*mb, jpp); + // Use some test case parameters + cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); + cadet::test::setBindingMode(jpp, isKinetic); + cadet::IUnitOperation* const unit = createAndConfigureUnit(*mb, jpp); - // Fill state vector with given initial values - std::vector y(initState, initState + unit->numDofs()); + // Fill state vector with given initial values + std::vector y(unit->numDofs(), 0.0); + util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, unit->numDofs()); - unitoperation::testConsistentInitialization(unit, adEnabled, y.data(), consTol, absTol); + unitoperation::testConsistentInitialization(unit, adEnabled, y.data(), consTol, absTol); - mb->destroyUnitOperation(unit); - } + mb->destroyUnitOperation(unit); } } - destroyModelBuilder(mb); } + destroyModelBuilder(mb); +} - void testConsistentInitializationSensitivity(const std::string& uoType, const std::string& spatialMethod, double const* const y, double const* const yDot, bool linearBinding, double absTol, const int reqBnd, const int useAD) - { - cadet::IModelBuilder* const mb = cadet::createModelBuilder(); - REQUIRE(nullptr != mb); +void testConsistentInitializationSMABinding(const std::string& uoType, const std::string& spatialMethod, + double const* const initState, double consTol, double absTol, + const int reqBnd, const int useAD) +{ + cadet::IModelBuilder* const mb = cadet::createModelBuilder(); + REQUIRE(nullptr != mb); - for (int bindingMode = 0; bindingMode < 1; ++bindingMode) + for (int bindingMode = 0; bindingMode < 2; ++bindingMode) + { + if ((bindingMode == 0 && reqBnd == 1) || (bindingMode == 1 && reqBnd == 0)) + continue; + const bool isKinetic = (bindingMode == 0); + for (int adMode = 0; adMode < 2; ++adMode) { - if ((bindingMode == 0 && reqBnd == 1) || (bindingMode == 1 && reqBnd == 0)) + if ((adMode == 0 && useAD == 1) || (adMode == 1 && useAD == 0)) continue; - const bool isKinetic = (bindingMode == 0); - for (int adMode = 0; adMode < 2; ++adMode) + const bool adEnabled = (adMode > 0); + SECTION(std::string(isKinetic ? " Kinetic binding" : " Quasi-stationary binding") + " with AD " + + (adEnabled ? "enabled" : "disabled")) { - if ((adMode == 0 && useAD == 1) || (adMode == 1 && useAD == 0)) - continue; - const bool adEnabled = (adMode > 0); - SECTION(std::string(isKinetic ? " Kinetic binding" : " Quasi-stationary binding") + " with AD " + (adEnabled ? "enabled" : "disabled")) - { - // Use some test case parameters - cadet::JsonParameterProvider jpp = linearBinding ? createColumnWithTwoCompLinearBinding(uoType, spatialMethod) : createColumnWithSMA(uoType, spatialMethod); - cadet::test::setBindingMode(jpp, isKinetic); - cadet::IUnitOperation* const unit = createAndConfigureUnit(*mb, jpp); - - unit->setSensitiveParameter(cadet::makeParamId("INIT_C", 0, 0, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), 0, 1.0); - if (linearBinding) - unit->setSensitiveParameter(cadet::makeParamId("LIN_KA", 0, 0, cadet::ParTypeIndep, 0, cadet::ReactionIndep, cadet::SectionIndep), 1, 1.0); - else - unit->setSensitiveParameter(cadet::makeParamId("SMA_NU", 0, 1, cadet::ParTypeIndep, 0, cadet::ReactionIndep, cadet::SectionIndep), 1, 1.0); + // Use some test case parameters + cadet::JsonParameterProvider jpp = createColumnWithSMA(uoType, spatialMethod); + cadet::test::setBindingMode(jpp, isKinetic); + cadet::IUnitOperation* const unit = createAndConfigureUnit(*mb, jpp); - unit->setSensitiveParameter(cadet::makeParamId("COL_LENGTH", 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), 2, 1.0); + // Fill state vector with given initial values + std::vector y(initState, initState + unit->numDofs()); - REQUIRE(unit->numSensParams() == 3); - unitoperation::testConsistentInitializationSensitivity(unit, adEnabled, y, yDot, absTol); + unitoperation::testConsistentInitialization(unit, adEnabled, y.data(), consTol, absTol); - mb->destroyUnitOperation(unit); - } + mb->destroyUnitOperation(unit); } } - destroyModelBuilder(mb); } + destroyModelBuilder(mb); +} - void testInletDofJacobian(const std::string& uoType, const std::string& spatialMethod) - { - cadet::IModelBuilder* const mb = cadet::createModelBuilder(); - REQUIRE(nullptr != mb); +void testConsistentInitializationSensitivity(const std::string& uoType, const std::string& spatialMethod, + double const* const y, double const* const yDot, bool linearBinding, + double absTol, const int reqBnd, const int useAD) +{ + cadet::IModelBuilder* const mb = cadet::createModelBuilder(); + REQUIRE(nullptr != mb); + for (int bindingMode = 0; bindingMode < 1; ++bindingMode) + { + if ((bindingMode == 0 && reqBnd == 1) || (bindingMode == 1 && reqBnd == 0)) + continue; + const bool isKinetic = (bindingMode == 0); for (int adMode = 0; adMode < 2; ++adMode) { + if ((adMode == 0 && useAD == 1) || (adMode == 1 && useAD == 0)) + continue; const bool adEnabled = (adMode > 0); - SECTION(std::string("AD ") + (adEnabled ? "enabled" : "disabled")) + SECTION(std::string(isKinetic ? " Kinetic binding" : " Quasi-stationary binding") + " with AD " + + (adEnabled ? "enabled" : "disabled")) { // Use some test case parameters - cadet::JsonParameterProvider jpp = createColumnWithSMA(uoType, spatialMethod); + cadet::JsonParameterProvider jpp = linearBinding + ? createColumnWithTwoCompLinearBinding(uoType, spatialMethod) + : createColumnWithSMA(uoType, spatialMethod); + cadet::test::setBindingMode(jpp, isKinetic); cadet::IUnitOperation* const unit = createAndConfigureUnit(*mb, jpp); - unitoperation::testInletDofJacobian(unit, adEnabled); + unit->setSensitiveParameter(cadet::makeParamId("INIT_C", 0, 0, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, + cadet::SectionIndep), + 0, 1.0); + if (linearBinding) + unit->setSensitiveParameter(cadet::makeParamId("LIN_KA", 0, 0, cadet::ParTypeIndep, 0, + cadet::ReactionIndep, cadet::SectionIndep), + 1, 1.0); + else + unit->setSensitiveParameter(cadet::makeParamId("SMA_NU", 0, 1, cadet::ParTypeIndep, 0, + cadet::ReactionIndep, cadet::SectionIndep), + 1, 1.0); + + unit->setSensitiveParameter(cadet::makeParamId("COL_LENGTH", 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, + cadet::SectionIndep), + 2, 1.0); + + REQUIRE(unit->numSensParams() == 3); + unitoperation::testConsistentInitializationSensitivity(unit, adEnabled, y, yDot, absTol); mb->destroyUnitOperation(unit); } } - destroyModelBuilder(mb); } + destroyModelBuilder(mb); +} + +void testInletDofJacobian(const std::string& uoType, const std::string& spatialMethod) +{ + cadet::IModelBuilder* const mb = cadet::createModelBuilder(); + REQUIRE(nullptr != mb); - void testReferenceBenchmark(const std::string& modelFileRelPath, const std::string& refFileRelPath, const std::string& unitID, const std::vector absTol, const std::vector relTol, const DiscParams& disc, const bool compare_sens, const int simDataStride) + for (int adMode = 0; adMode < 2; ++adMode) { - const int unitOpID = std::stoi(unitID); + const bool adEnabled = (adMode > 0); + SECTION(std::string("AD ") + (adEnabled ? "enabled" : "disabled")) + { + // Use some test case parameters + cadet::JsonParameterProvider jpp = createColumnWithSMA(uoType, spatialMethod); + cadet::IUnitOperation* const unit = createAndConfigureUnit(*mb, jpp); + + unitoperation::testInletDofJacobian(unit, adEnabled); + + mb->destroyUnitOperation(unit); + } + } + destroyModelBuilder(mb); +} - // read json model setup file - const std::string setupFile = std::string(getTestDirectory()) + modelFileRelPath; - JsonParameterProvider pp_setup(JsonParameterProvider::fromFile(setupFile)); +void testReferenceBenchmark(const std::string& modelFileRelPath, const std::string& refFileRelPath, + const std::string& unitID, const std::vector absTol, + const std::vector relTol, const DiscParams& disc, const bool compare_sens, + const int simDataStride) +{ + const int unitOpID = std::stoi(unitID); + + // read json model setup file + const std::string setupFile = std::string(getTestDirectory()) + modelFileRelPath; + JsonParameterProvider pp_setup(JsonParameterProvider::fromFile(setupFile)); + + // adjust numerical parameters + nlohmann::json* setupJson = pp_setup.data(); + + // copy over some numerical parameters from reference file, e.g. consistent initialization + cadet::io::HDF5Reader rd; + const std::string refFile = std::string(getTestDirectory()) + refFileRelPath; + rd.openFile(refFile, "r"); + ParameterProviderImpl pp_ref(rd); + setNumericalMethod(pp_ref, *setupJson, unitID, true); + pp_ref.popScope(); + + // copy solution times + pp_ref.pushScope("input"); + pp_ref.pushScope("solver"); + setupJson[0]["solver"]["USER_SOLUTION_TIMES"] = pp_ref.getDoubleArray("USER_SOLUTION_TIMES"); + pp_ref.popScope(); + + // copy multiplex data + copyMultiplexData(pp_ref, *setupJson, unitID); + + // copy return data + copyReturnData(pp_ref, *setupJson, unitID); + + // copy sensitivity setup + if (pp_ref.exists("sensitivity") && compare_sens) + copySensitivities(pp_ref, *setupJson, unitID); + + pp_ref.popScope(); + rd.closeFile(); + + // set remaining spatial numerical parameters + disc.setDisc(pp_setup, unitID); + + // run simulation + Driver drv; + drv.configure(pp_setup); + drv.run(); + + // get simulation result + InternalStorageUnitOpRecorder const* const simData = drv.solution()->unitOperation(unitOpID); + double const* sim_outlet = simData->outlet(); + + // read h5 reference data + rd.openFile(refFile, "r"); + + // get outlet and sensitivity reference + pp_ref.pushScope("output"); + pp_ref.pushScope("solution"); + pp_ref.pushScope("unit_" + unitID); + std::vector ref_outlet; + if (pp_ref.exists("SOLUTION_OUTLET")) + ref_outlet = pp_ref.getDoubleArray("SOLUTION_OUTLET"); + else + ref_outlet = pp_ref.getDoubleArray("SOLUTION_OUTLET_PORT_000"); + pp_ref.popScope(); + pp_ref.popScope(); + + // compare the simulation results with the reference data + for (unsigned int i = 0; i < ref_outlet.size(); ++i) + CHECK((sim_outlet[i * simDataStride]) == cadet::test::makeApprox(ref_outlet[i], relTol[0], absTol[0])); + if (pp_ref.exists("sensitivity") && compare_sens) + { + pp_ref.pushScope("sensitivity"); - // adjust numerical parameters - nlohmann::json* setupJson = pp_setup.data(); + unsigned int sensID = 0; + std::string sensParam = std::to_string(sensID); + sensParam = "param_" + std::string(3 - sensParam.length(), '0') + sensParam; + CAPTURE(sensParam); - // copy over some numerical parameters from reference file, e.g. consistent initialization - cadet::io::HDF5Reader rd; - const std::string refFile = std::string(getTestDirectory()) + refFileRelPath; - rd.openFile(refFile, "r"); - ParameterProviderImpl pp_ref(rd); - setNumericalMethod(pp_ref, *setupJson, unitID, true); - pp_ref.popScope(); + while (pp_ref.exists(sensParam)) + { + pp_ref.pushScope(sensParam); + pp_ref.pushScope("unit_" + unitID); + const std::vector ref_sens = pp_ref.getDoubleArray("SENS_OUTLET"); + pp_ref.popScope(); + pp_ref.popScope(); - // copy solution times - pp_ref.pushScope("input"); - pp_ref.pushScope("solver"); - setupJson[0]["solver"]["USER_SOLUTION_TIMES"] = pp_ref.getDoubleArray("USER_SOLUTION_TIMES"); - pp_ref.popScope(); + double const* sim_sens = simData->sensOutlet(sensID); - // copy multiplex data - copyMultiplexData(pp_ref, *setupJson, unitID); + for (unsigned int i = 0; i < ref_sens.size(); ++i) + CHECK((sim_sens[i]) == cadet::test::makeApprox(ref_sens[i], relTol[sensID + 1], absTol[sensID + 1])); - // copy return data - copyReturnData(pp_ref, *setupJson, unitID); + sensID++; + sensParam = std::to_string(sensID); + sensParam = "param_" + std::string(3 - sensParam.length(), '0') + sensParam; + } + } + rd.closeFile(); +} - // copy sensitivity setup - if (pp_ref.exists("sensitivity") && compare_sens) - copySensitivities(pp_ref, *setupJson, unitID); - +// todo ? include L1 errors or parameterize error choice ? +void testEOCReferenceBenchmark(const std::string& modelFileRelPath, const std::string& refFileRelPath, + const std::string& convFileRelPath, const std::string& unitID, + const std::vector absTol, const std::vector relTol, + const unsigned int nDisc, const DiscParams& startDisc, const bool compare_sens) +{ + const int unitOpID = std::stoi(unitID); + + // read model setup file + const std::string setupFile = std::string(getTestDirectory()) + modelFileRelPath; + JsonParameterProvider pp_setup(JsonParameterProvider::fromFile(setupFile)); + + // adjust numerical parameters + nlohmann::json* setupJson = pp_setup.data(); + + // copy over some numerical parameters from reference file, e.g. consistent initialization + cadet::io::HDF5Reader rd; + const std::string refFile = std::string(getTestDirectory()) + refFileRelPath; + rd.openFile(refFile, "r"); + ParameterProviderImpl pp_ref(rd); + setNumericalMethod(pp_ref, *setupJson, unitID); + pp_ref.popScope(); + + // copy solution times + pp_ref.pushScope("input"); + pp_ref.pushScope("solver"); + setupJson[0]["solver"]["USER_SOLUTION_TIMES"] = pp_ref.getDoubleArray("USER_SOLUTION_TIMES"); + pp_ref.popScope(); + + // copy multiplex data + copyMultiplexData(pp_ref, *setupJson, unitID); + + // copy sensitivity setup + int nSens = 0; + if (pp_ref.exists("sensitivity")) + { + pp_ref.pushScope("sensitivity"); + nSens = pp_ref.getInt("NSENS"); pp_ref.popScope(); - rd.closeFile(); + } - // set remaining spatial numerical parameters - disc.setDisc(pp_setup, unitID); + // copy return data + copyReturnData(pp_ref, *setupJson, unitID); + + // configure sensitivities + if (nSens && compare_sens) + copySensitivities(pp_ref, *setupJson, unitID); + + pp_ref.popScope(); + + // read h5 reference data + pp_ref.pushScope("output"); + pp_ref.pushScope("solution"); + pp_ref.pushScope("unit_" + unitID); + const std::vector ref_outlet = pp_ref.getDoubleArray("SOLUTION_OUTLET"); + pp_ref.popScope(); + pp_ref.popScope(); + pp_ref.popScope(); + + // read convergence file + const std::string convFile = std::string(getTestDirectory()) + convFileRelPath; + JsonParameterProvider pp_conv(JsonParameterProvider::fromFile(convFile)); + + pp_conv.pushScope("convergence"); + pp_conv.pushScope("outlet"); + std::vector discZ = pp_conv.getDoubleArray("$N_e^z$"); + std::vector discP; + const int startNCol = startDisc.getNAxCells(); + const int startNPar = startDisc.getNParCells(); + if (startNPar > 0) + discP = pp_conv.getDoubleArray("$N_e^p$"); + pp_conv.popScope(); + + int discIdx = 0; + auto it = std::find(discZ.begin(), discZ.end(), static_cast(startNCol)); + if (it != discZ.end()) + discIdx = std::distance(discZ.begin(), it); + else + throw std::out_of_range("discretization N_e^z = " + std::to_string(startNCol) + + " not found in convergence reference data"); + if (startNPar > 0 && discP[discIdx] != static_cast(startNPar)) + throw std::out_of_range("discretization N_e^p = " + std::to_string(startNPar) + + " not found in convergence reference data at the same index as N_e^z"); + + // run the simulations and compute EOC + std::vector absErrors(ref_outlet.size()); + std::vector> L1Errors(1 + nSens, std::vector(nDisc, 0.0)); + std::vector> LinfErrors(1 + nSens, std::vector(nDisc, 0.0)); + std::vector> L1EOC(1 + nSens, std::vector(nDisc, 0.0)); + std::vector> LinfEOC(1 + nSens, std::vector(nDisc, 0.0)); + + for (int disc = 1; disc <= nDisc; disc++) + { + const int nAxCells = startNCol * std::pow(2, disc - 1); + const int nParCells = startNPar * std::pow(2, disc - 1); + setNumAxialCells(pp_setup, nAxCells, unitID); + if (startNPar > 0) + setNumParCells(pp_setup, nParCells, unitID); // run simulation Driver drv; drv.configure(pp_setup); drv.run(); - // get simulation result + // compute errors and EOC InternalStorageUnitOpRecorder const* const simData = drv.solution()->unitOperation(unitOpID); - double const* sim_outlet = simData->outlet(); - - // read h5 reference data - rd.openFile(refFile, "r"); - - // get outlet and sensitivity reference - pp_ref.pushScope("output"); - pp_ref.pushScope("solution"); - pp_ref.pushScope("unit_" + unitID); - std::vector ref_outlet; - if (pp_ref.exists("SOLUTION_OUTLET")) - ref_outlet = pp_ref.getDoubleArray("SOLUTION_OUTLET"); - else - ref_outlet = pp_ref.getDoubleArray("SOLUTION_OUTLET_PORT_000"); - pp_ref.popScope(); - pp_ref.popScope(); - - // compare the simulation results with the reference data - for (unsigned int i = 0; i < ref_outlet.size(); ++i) - CHECK((sim_outlet[i * simDataStride]) == cadet::test::makeApprox(ref_outlet[i], relTol[0], absTol[0])); - if (pp_ref.exists("sensitivity") && compare_sens) - { - pp_ref.pushScope("sensitivity"); - - unsigned int sensID = 0; - std::string sensParam = std::to_string(sensID); - sensParam = "param_" + std::string(3 - sensParam.length(), '0') + sensParam; - CAPTURE(sensParam); - - while (pp_ref.exists(sensParam)) - { - pp_ref.pushScope(sensParam); - pp_ref.pushScope("unit_" + unitID); - const std::vector ref_sens = pp_ref.getDoubleArray("SENS_OUTLET"); - pp_ref.popScope(); - pp_ref.popScope(); - - double const* sim_sens = simData->sensOutlet(sensID); - - for (unsigned int i = 0; i < ref_sens.size(); ++i) - CHECK((sim_sens[i]) == cadet::test::makeApprox(ref_sens[i], relTol[sensID + 1], absTol[sensID + 1])); - - sensID++; - sensParam = std::to_string(sensID); - sensParam = "param_" + std::string(3 - sensParam.length(), '0') + sensParam; - } - } - rd.closeFile(); - } - - // todo ? include L1 errors or parameterize error choice ? - void testEOCReferenceBenchmark(const std::string& modelFileRelPath, const std::string& refFileRelPath, const std::string& convFileRelPath, const std::string& unitID, const std::vector absTol, const std::vector relTol, const unsigned int nDisc, const DiscParams& startDisc, const bool compare_sens) - { - const int unitOpID = std::stoi(unitID); - - // read model setup file - const std::string setupFile = std::string(getTestDirectory()) + modelFileRelPath; - JsonParameterProvider pp_setup(JsonParameterProvider::fromFile(setupFile)); + double const* approximation = simData->outlet(); - // adjust numerical parameters - nlohmann::json* setupJson = pp_setup.data(); + std::transform(approximation, approximation + ref_outlet.size(), ref_outlet.begin(), absErrors.begin(), + [](double a, double b) { return std::abs(a - b); }); - // copy over some numerical parameters from reference file, e.g. consistent initialization - cadet::io::HDF5Reader rd; - const std::string refFile = std::string(getTestDirectory()) + refFileRelPath; - rd.openFile(refFile, "r"); - ParameterProviderImpl pp_ref(rd); - setNumericalMethod(pp_ref, *setupJson, unitID); - pp_ref.popScope(); + L1Errors[0][disc - 1] = std::accumulate(absErrors.begin(), absErrors.end(), 0.0) / ref_outlet.size(); + LinfErrors[0][disc - 1] = *std::max_element(absErrors.begin(), absErrors.end()); - // copy solution times - pp_ref.pushScope("input"); - pp_ref.pushScope("solver"); - setupJson[0]["solver"]["USER_SOLUTION_TIMES"] = pp_ref.getDoubleArray("USER_SOLUTION_TIMES"); - pp_ref.popScope(); + pp_conv.pushScope("outlet"); + std::vector LinfErrors_ref = pp_conv.getDoubleArray("Max. error"); + std::vector LinfEOC_ref = pp_conv.getDoubleArray("Max. EOC"); + pp_conv.popScope(); - // copy multiplex data - copyMultiplexData(pp_ref, *setupJson, unitID); + CAPTURE(nAxCells); + CAPTURE(LinfErrors[0][disc - 1]); + CAPTURE(LinfErrors_ref[discIdx + disc - 1]); + CHECK((LinfErrors[0][disc - 1]) == + cadet::test::makeApprox(LinfErrors_ref[discIdx + disc - 1], relTol[0], absTol[0])); - // copy sensitivity setup - int nSens = 0; - if (pp_ref.exists("sensitivity")) + if (disc > 1) { - pp_ref.pushScope("sensitivity"); - nSens = pp_ref.getInt("NSENS"); - pp_ref.popScope(); + L1EOC[0][disc - 1] = std::log(L1Errors[0][disc - 1] / L1Errors[0][disc - 2]) / + std::log(std::pow(2, disc - 1) / std::pow(2, disc)); + LinfEOC[0][disc - 1] = std::log(LinfErrors[0][disc - 1] / LinfErrors[0][disc - 2]) / + std::log(std::pow(2, disc - 1) / std::pow(2, disc)); + CAPTURE(LinfEOC[0][disc - 1]); + CAPTURE(LinfEOC_ref[discIdx + disc - 1]); + CHECK((LinfEOC[0][disc - 1]) == + cadet::test::makeApprox(LinfEOC_ref[discIdx + disc - 1], relTol[0], absTol[0])); } - // copy return data - copyReturnData(pp_ref, *setupJson, unitID); - - // configure sensitivities + // compare sensitivity EOC if (nSens && compare_sens) - copySensitivities(pp_ref, *setupJson, unitID); - - pp_ref.popScope(); + { + for (int sensID = 0; sensID < nSens; sensID++) + { + // get reference errors and EOC (to this end get sensitivity name to push convergence scope to reference + // solution) + pp_ref.pushScope("input"); + pp_ref.pushScope("sensitivity"); + std::string sensParam = std::to_string(sensID); + sensParam = "param_" + std::string(3 - sensParam.length(), '0') + sensParam; + pp_ref.pushScope(sensParam); + std::string sens_name = pp_ref.getString("SENS_NAME"); + pp_conv.pushScope("sens_" + sens_name); + LinfErrors_ref = pp_conv.getDoubleArray("Max. error"); + LinfEOC_ref = pp_conv.getDoubleArray("Max. EOC"); + pp_conv.popScope(); + pp_ref.popScope(); + pp_ref.popScope(); + pp_ref.popScope(); - // read h5 reference data - pp_ref.pushScope("output"); - pp_ref.pushScope("solution"); - pp_ref.pushScope("unit_" + unitID); - const std::vector ref_outlet = pp_ref.getDoubleArray("SOLUTION_OUTLET"); - pp_ref.popScope(); - pp_ref.popScope(); - pp_ref.popScope(); + // compute error and EOC + pp_ref.pushScope("output"); + pp_ref.pushScope("sensitivity"); + pp_ref.pushScope(sensParam); + pp_ref.pushScope("unit_" + unitID); + const std::vector ref_sens = pp_ref.getDoubleArray("SENS_OUTLET"); - // read convergence file - const std::string convFile = std::string(getTestDirectory()) + convFileRelPath; - JsonParameterProvider pp_conv(JsonParameterProvider::fromFile(convFile)); + approximation = simData->sensOutlet(sensID); - pp_conv.pushScope("convergence"); - pp_conv.pushScope("outlet"); - std::vector discZ = pp_conv.getDoubleArray("$N_e^z$"); - std::vector discP; - const int startNCol = startDisc.getNAxCells(); - const int startNPar = startDisc.getNParCells(); - if (startNPar > 0) - discP = pp_conv.getDoubleArray("$N_e^p$"); - pp_conv.popScope(); + std::transform(approximation, approximation + ref_sens.size(), ref_sens.begin(), absErrors.begin(), + [](double a, double b) { return std::abs(a - b); }); + L1Errors[sensID + 1][disc - 1] = + std::accumulate(absErrors.begin(), absErrors.end(), 0.0) / ref_sens.size(); + LinfErrors[sensID + 1][disc - 1] = *std::max_element(absErrors.begin(), absErrors.end()); - int discIdx = 0; - auto it = std::find(discZ.begin(), discZ.end(), static_cast(startNCol)); - if (it != discZ.end()) - discIdx = std::distance(discZ.begin(), it); - else - throw std::out_of_range("discretization N_e^z = " + std::to_string(startNCol) + " not found in convergence reference data"); - if (startNPar > 0 && discP[discIdx] != static_cast(startNPar)) - throw std::out_of_range("discretization N_e^p = " + std::to_string(startNPar) + " not found in convergence reference data at the same index as N_e^z"); - - // run the simulations and compute EOC - std::vector absErrors(ref_outlet.size()); - std::vector> L1Errors(1 + nSens, std::vector(nDisc, 0.0)); - std::vector> LinfErrors(1 + nSens, std::vector(nDisc, 0.0)); - std::vector> L1EOC(1 + nSens, std::vector(nDisc, 0.0)); - std::vector> LinfEOC(1 + nSens, std::vector(nDisc, 0.0)); - - for (int disc = 1; disc <= nDisc; disc++) - { - const int nAxCells = startNCol * std::pow(2, disc - 1); - const int nParCells = startNPar * std::pow(2, disc - 1); - setNumAxialCells(pp_setup, nAxCells, unitID); - if (startNPar > 0) - setNumParCells(pp_setup, nParCells, unitID); - - // run simulation - Driver drv; - drv.configure(pp_setup); - drv.run(); - - // compute errors and EOC - InternalStorageUnitOpRecorder const* const simData = drv.solution()->unitOperation(unitOpID); - double const* approximation = simData->outlet(); - - std::transform(approximation, approximation + ref_outlet.size(), ref_outlet.begin(), absErrors.begin(), - [](double a, double b) { return std::abs(a - b); }); - - L1Errors[0][disc - 1] = std::accumulate(absErrors.begin(), absErrors.end(), 0.0) / ref_outlet.size(); - LinfErrors[0][disc - 1] = *std::max_element(absErrors.begin(), absErrors.end()); - - pp_conv.pushScope("outlet"); - std::vector LinfErrors_ref = pp_conv.getDoubleArray("Max. error"); - std::vector LinfEOC_ref = pp_conv.getDoubleArray("Max. EOC"); - pp_conv.popScope(); - - CAPTURE(nAxCells); - CAPTURE(LinfErrors[0][disc - 1]); - CAPTURE(LinfErrors_ref[discIdx + disc - 1]); - CHECK((LinfErrors[0][disc - 1]) == cadet::test::makeApprox(LinfErrors_ref[discIdx + disc - 1], relTol[0], absTol[0])); - - if (disc > 1) - { - L1EOC[0][disc - 1] = std::log(L1Errors[0][disc - 1] / L1Errors[0][disc - 2]) / std::log(std::pow(2, disc - 1) / std::pow(2, disc)); - LinfEOC[0][disc - 1] = std::log(LinfErrors[0][disc - 1] / LinfErrors[0][disc - 2]) / std::log(std::pow(2, disc - 1) / std::pow(2, disc)); - CAPTURE(LinfEOC[0][disc - 1]); - CAPTURE(LinfEOC_ref[discIdx + disc - 1]); - CHECK((LinfEOC[0][disc - 1]) == cadet::test::makeApprox(LinfEOC_ref[discIdx + disc - 1], relTol[0], absTol[0])); - } + CAPTURE(sensID, sens_name); + CAPTURE(LinfErrors[sensID + 1][disc - 1]); + CAPTURE(LinfErrors_ref[discIdx + disc - 1]); + CHECK((LinfErrors[sensID + 1][disc - 1]) == cadet::test::makeApprox(LinfErrors_ref[discIdx + disc - 1], + relTol[1 + sensID], + absTol[1 + sensID])); - // compare sensitivity EOC - if (nSens && compare_sens) - { - for (int sensID = 0; sensID < nSens; sensID++) + if (disc > 1) { - // get reference errors and EOC (to this end get sensitivity name to push convergence scope to reference solution) - pp_ref.pushScope("input"); - pp_ref.pushScope("sensitivity"); - std::string sensParam = std::to_string(sensID); - sensParam = "param_" + std::string(3 - sensParam.length(), '0') + sensParam; - pp_ref.pushScope(sensParam); - std::string sens_name = pp_ref.getString("SENS_NAME"); - pp_conv.pushScope("sens_" + sens_name); - LinfErrors_ref = pp_conv.getDoubleArray("Max. error"); - LinfEOC_ref = pp_conv.getDoubleArray("Max. EOC"); - pp_conv.popScope(); - pp_ref.popScope(); - pp_ref.popScope(); - pp_ref.popScope(); - - // compute error and EOC - pp_ref.pushScope("output"); - pp_ref.pushScope("sensitivity"); - pp_ref.pushScope(sensParam); - pp_ref.pushScope("unit_" + unitID); - const std::vector ref_sens = pp_ref.getDoubleArray("SENS_OUTLET"); - - approximation = simData->sensOutlet(sensID); - - std::transform(approximation, approximation + ref_sens.size(), ref_sens.begin(), absErrors.begin(), - [](double a, double b) { return std::abs(a - b); }); - L1Errors[sensID+1][disc - 1] = std::accumulate(absErrors.begin(), absErrors.end(), 0.0) / ref_sens.size(); - LinfErrors[sensID + 1][disc - 1] = *std::max_element(absErrors.begin(), absErrors.end()); - - CAPTURE(sensID, sens_name); - CAPTURE(LinfErrors[sensID + 1][disc - 1]); - CAPTURE(LinfErrors_ref[discIdx + disc - 1]); - CHECK((LinfErrors[sensID + 1][disc - 1]) == cadet::test::makeApprox(LinfErrors_ref[discIdx + disc - 1], relTol[1 + sensID], absTol[1 + sensID])); - - if (disc > 1) - { - L1EOC[sensID + 1][disc - 1] = std::log(L1Errors[sensID + 1][disc - 1] / L1Errors[sensID + 1][disc - 2]) / std::log(std::pow(2, disc - 1) / std::pow(2, disc)); - LinfEOC[sensID + 1][disc - 1] = std::log(LinfErrors[sensID + 1][disc - 1] / LinfErrors[sensID + 1][disc - 2]) / std::log(std::pow(2, disc - 1) / std::pow(2, disc)); - CAPTURE(LinfEOC[sensID + 1][disc - 1]); - CAPTURE(LinfEOC_ref[discIdx + disc - 1]); - CHECK((LinfEOC[sensID + 1][disc - 1]) == cadet::test::makeApprox(LinfEOC_ref[discIdx + disc - 1], relTol[1 + sensID], absTol[1 + sensID])); - } - pp_ref.popScope(); - pp_ref.popScope(); - pp_ref.popScope(); - pp_ref.popScope(); + L1EOC[sensID + 1][disc - 1] = + std::log(L1Errors[sensID + 1][disc - 1] / L1Errors[sensID + 1][disc - 2]) / + std::log(std::pow(2, disc - 1) / std::pow(2, disc)); + LinfEOC[sensID + 1][disc - 1] = + std::log(LinfErrors[sensID + 1][disc - 1] / LinfErrors[sensID + 1][disc - 2]) / + std::log(std::pow(2, disc - 1) / std::pow(2, disc)); + CAPTURE(LinfEOC[sensID + 1][disc - 1]); + CAPTURE(LinfEOC_ref[discIdx + disc - 1]); + CHECK((LinfEOC[sensID + 1][disc - 1]) == cadet::test::makeApprox(LinfEOC_ref[discIdx + disc - 1], + relTol[1 + sensID], + absTol[1 + sensID])); } + pp_ref.popScope(); + pp_ref.popScope(); + pp_ref.popScope(); + pp_ref.popScope(); } } - rd.closeFile(); } + rd.closeFile(); +} - void testForeignReferenceBenchmark(const std::string& configFileRelPath, const std::string& refFileRelPath, const std::string& unitID, const double absTol, const double relTol, const int compIdx) - { - const int unitOpID = std::stoi(unitID); - - // read json model setup file - const std::string setupFile = std::string(getTestDirectory()) + configFileRelPath; - JsonParameterProvider pp_setup(JsonParameterProvider::fromFile(setupFile)); - - // adjust numerical parameters - nlohmann::json* setupJson = pp_setup.data(); - - // run simulation - Driver drv; - drv.configure(pp_setup); - drv.run(); - - // get simulation result - InternalStorageUnitOpRecorder const* const simData = drv.solution()->unitOperation(unitOpID); - double const* sim_outlet = simData->outlet(); - - // read h5 reference data - cadet::io::HDF5Reader rd; - const std::string refFile = std::string(getTestDirectory()) + refFileRelPath; - rd.openFile(refFile, "r"); - ParameterProviderImpl pp_ref(rd); - pp_ref.popScope(); - pp_ref.pushScope("output"); - pp_ref.pushScope("solution"); - const std::vector ref_outlet = pp_ref.getDoubleArray("SOLUTION_OUTLET"); - - // check if only one specific component is considered in the reference solution and set stride and offset accordingly - const unsigned int compStride = (compIdx == -1) ? 1 : simData->numComponents(); - sim_outlet += (compIdx == -1) ? 0 : compIdx; - - // compare the simulation results with the reference data - for (unsigned int i = 0, j = 0; j < ref_outlet.size(); i += compStride, j += 1) - CHECK((sim_outlet[i]) == cadet::test::makeApprox(ref_outlet[j], relTol, absTol)); - - rd.closeFile(); - } +void testForeignReferenceBenchmark(const std::string& configFileRelPath, const std::string& refFileRelPath, + const std::string& unitID, const double absTol, const double relTol, + const int compIdx) +{ + const int unitOpID = std::stoi(unitID); + + // read json model setup file + const std::string setupFile = std::string(getTestDirectory()) + configFileRelPath; + JsonParameterProvider pp_setup(JsonParameterProvider::fromFile(setupFile)); + + // adjust numerical parameters + nlohmann::json* setupJson = pp_setup.data(); + + // run simulation + Driver drv; + drv.configure(pp_setup); + drv.run(); + + // get simulation result + InternalStorageUnitOpRecorder const* const simData = drv.solution()->unitOperation(unitOpID); + double const* sim_outlet = simData->outlet(); + + // read h5 reference data + cadet::io::HDF5Reader rd; + const std::string refFile = std::string(getTestDirectory()) + refFileRelPath; + rd.openFile(refFile, "r"); + ParameterProviderImpl pp_ref(rd); + pp_ref.popScope(); + pp_ref.pushScope("output"); + pp_ref.pushScope("solution"); + const std::vector ref_outlet = pp_ref.getDoubleArray("SOLUTION_OUTLET"); + + // check if only one specific component is considered in the reference solution and set stride and offset + // accordingly + const unsigned int compStride = (compIdx == -1) ? 1 : simData->numComponents(); + sim_outlet += (compIdx == -1) ? 0 : compIdx; + + // compare the simulation results with the reference data + for (unsigned int i = 0, j = 0; j < ref_outlet.size(); i += compStride, j += 1) + CHECK((sim_outlet[i]) == cadet::test::makeApprox(ref_outlet[j], relTol, absTol)); + + rd.closeFile(); +} } // namespace column } // namespace test diff --git a/test/ColumnTests.hpp b/test/ColumnTests.hpp index e247f5300..f13885b95 100644 --- a/test/ColumnTests.hpp +++ b/test/ColumnTests.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines simulation tests for column unit operations. */ @@ -32,369 +32,447 @@ namespace test namespace column { - struct DiscParams { - int nAxCells; - int nParCells; - - virtual ~DiscParams() = default; - - virtual int getNAxCells() const = 0; - virtual int getNParCells() const = 0; - - virtual void setDisc(JsonParameterProvider& jpp, const std::string unitID = "000") const = 0; - }; - - struct FVparams : public DiscParams { - int nAxCells; - int nParCells; - int wenoOrder; - int nRadCells; - - FVparams() : nAxCells(0), nParCells(0), wenoOrder(0), nRadCells(0) {} - FVparams(int nCol) - : nAxCells(nCol), nParCells(0), wenoOrder(0), nRadCells(0) {} - FVparams(int nCol, int nPar) - : nAxCells(nCol), nParCells(nPar), wenoOrder(0), nRadCells(0) {} - FVparams(int nCol, int nPar, int wenoOrder) - : nAxCells(nCol), nParCells(nPar), wenoOrder(wenoOrder), nRadCells(0) {} - FVparams(int nCol, int nPar, int wenoOrder, int nRad) - : nAxCells(nCol), nParCells(nPar), wenoOrder(wenoOrder), nRadCells(nRad) {} - - int getNAxCells() const override { return nAxCells; } - int getNParCells() const override { return nParCells; } - void setWenoOrder(int order) { wenoOrder = order; } - int getWenoOrder() { return wenoOrder; } - void setNRad(int nRad) { nRadCells = nRad; } - void setDisc(JsonParameterProvider& jpp, const std::string unitID = "000") const override; - }; - - struct DGparams : public DiscParams { - int exactIntegration; - int polyDeg; - int nElem; - int parPolyDeg; - int parNelem; - - DGparams() : exactIntegration(-1), polyDeg(0), nElem(0), parPolyDeg(0), parNelem(0) {} - DGparams(int exact, int poly, int elem) - : exactIntegration(exact), polyDeg(poly), nElem(elem), parPolyDeg(0), parNelem(0) {} - DGparams(int exact, int poly, int elem, int parPolyDeg, int parNelem) - : exactIntegration(exact), polyDeg(poly), nElem(elem), parPolyDeg(parPolyDeg), parNelem(parNelem) {} - - int getNAxCells() const override { return nElem; } - int getNParCells() const override { return parNelem; } - void setIntegrationMode(int integrationMode) { exactIntegration = integrationMode; } - int getIntegrationMode() { return exactIntegration; } - void setDisc(JsonParameterProvider& jpp, const std::string unitID = "000") const override; - }; - - /** - * @brief Sets the number of axial cells in a configuration of a column-like unit operation - * @details Overwrites the NCOL field in the discretization group of the given ParameterProvider. - * @param [in,out] jpp ParameterProvider to change the number of axial cells in - * @param [in] nCol Number of axial cells - * @param [in] unitID unit operation ID - */ - void setNumAxialCells(cadet::JsonParameterProvider& jpp, unsigned int nCol, std::string unitID="000"); - - /** - * @brief Sets the WENO order in a configuration of a column-like unit operation - * @details Overwrites the WENO_ORDER field in the weno group of the given ParameterProvider. - * @param [in,out] jpp ParameterProvider to change the WENO order in - * @param [in] order Target order - */ - void setWenoOrder(cadet::JsonParameterProvider& jpp, int order); - - /** - * @brief Reverses the flow of a column-like unit operation - * @param [in,out] jpp ParameterProvider to change the flow direction in - */ - void reverseFlow(cadet::JsonParameterProvider& jpp); - - /** - * @brief Infers cross section area of column model from interstitial velocity - * @details Uses interstitial velocity and porosity to calculate cross section area. - * To this end, a volumetric flow rate of 1.0 m^3/s is assumed. Depending on - * @p dir, the velocity field is removed or set to @c +1.0 or @c -1.0 indicating - * direction of the flow inside the unit operation. - * - * @param [in,out] jpp ParameterProvider to add cross section area to - * @param [in] useTotalPorosity Determines whether TOTAL_POROSITY is used (@c true) or COL_POROSITY (@c false) - * @param [in] dir Flow direction in unit operation (@c 0 removes field, @c 1 standard direction, @c -1 flow reversal) - */ - void setCrossSectionArea(cadet::JsonParameterProvider& jpp, bool useTotalPorosity, int dir); - - /** - * @brief Returns the offset to the flux part in the local state vector - * @param [in] unit Unit operation - * @return Offset to the flux part - */ - unsigned int fluxOffsetOfColumnUnitOp(cadet::IUnitOperation* unit); - - /** - * @brief Runs a simulation test comparing against (semi-)analytic single component pulse injection reference data - * @details Linear binding model is used in the column-like unit operation. - * @param [in] uoType Unit operation type - * @param [in] refFileRelPath Path to the reference data file from the directory of this file - * @param [in] forwardFlow Determines whether the unit operates in forward flow (@c true) or backwards flow (@c false) - * @param [in] dynamicBinding Determines whether dynamic binding (@c true) or rapid equilibrium (@c false) is used - * @param [in] disc spatial discretization parameters - * @param [in] method spatial discretization method - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testAnalyticBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, bool dynamicBinding, DiscParams& disc, const std::string method, double absTol, double relTol); - void testAnalyticBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, bool dynamicBinding, FVparams& disc, double absTol, double relTol); - void testAnalyticBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, bool dynamicBinding, DGparams& disc, double absTol, double relTol); - - /** - * @brief Runs a simulation test comparing against (semi-)analytic single component pulse injection reference data - * @details The component is assumed to be non-binding. - * @param [in] uoType Unit operation type - * @param [in] refFileRelPath Path to the reference data file from the directory of this file - * @param [in] forwardFlow Determines whether the unit operates in forward flow (@c true) or backwards flow (@c false) - * @param [in] disc spatial discretization parameters - * @param [in] method spatial discretization method - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testAnalyticNonBindingBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, DiscParams& disc, const std::string method, double absTol, double relTol); - void testAnalyticNonBindingBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, FVparams& disc, double absTol, double relTol); - void testAnalyticNonBindingBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, DGparams& disc, double absTol, double relTol); - - - /** - * @brief Runs a simulation test comparing forward and backwards flow in the load-wash-elution example - * @param [in] uoType Unit operation type - * @param [in] disc spatial discretization parameters - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testForwardBackward(const char* uoType, FVparams disc, double absTol, double relTol); - void testForwardBackward(const char* uoType, DGparams disc, double absTol, double relTol); - void testForwardBackward(cadet::JsonParameterProvider jpp, double absTol, double relTol); - - /** - * @brief Checks the full Jacobian against AD and FD pattern switching - * @details Checks the analytic Jacobian against the AD Jacobian and checks both against the FD pattern. - * @param [in] jpp Configured column model - * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern - */ - void testJacobianAD(cadet::JsonParameterProvider& jpp, const double absTolFDpattern = 0.0); - - /** - * @brief Checks the full Jacobian against AD and FD pattern switching in case of variable surface diffusion coefficient - * @details Checks the analytic Jacobian against the AD Jacobian and checks both against the FD pattern. - * @param [in] uoType Unit operation type - * @param [in] dynamicBinding Determines whether dynamic binding is used - */ - void testJacobianADVariableParSurfDiff(const std::string& uoType, const std::string& spatialMethod, bool dynamicBinding); - - /** - * @brief Checks the full Jacobian against AD and FD pattern switching from forward to backward flow and back - * @details Checks the analytic Jacobian against the AD Jacobian and checks both against the FD pattern. - * Checks both forward and backward flow mode as well as switching between them. - * - * @param [in] uoType Unit operation type - * @param [in] disc spatial discretization parameters - * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern - */ - void testJacobianForwardBackward(const char* uoType, FVparams disc, const double absTolFDpattern = 0.0); - void testJacobianForwardBackward(const char* uoType, DGparams disc, const double absTolFDpattern = 0.0); - void testJacobianForwardBackward(cadet::JsonParameterProvider& jpp, const double absTolFDpattern = 0.0); - - /** - * @brief Checks the full Jacobian against FD switching from forward to backward flow and back - * @details Checks the analytic Jacobian against the finite difference Jacobian. - * Checks both forward and backward flow mode as well as switching between them. - * Uses centered finite differences. - * - * @param [in] uoType Unit operation type - * @param [in] wenoOrder Order of the WENO method - * @param [in] h Step size of centered finite differences - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testJacobianWenoForwardBackwardFD(const std::string& uoType, const std::string& spatialMethod, int wenoOrder, double h = 1e-6, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0); - - /** - * @brief Checks the (analytic) time derivative Jacobian against FD - * @details Uses centered finite differences. - * @param [in] uoType Unit operation type - * @param [in] h Step size of centered finite differences - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testTimeDerivativeJacobianFD(const std::string& uoType, const std::string& spatialMethod, double h = 1e-6, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0); - - /** - * @brief Checks the bottom macro row and right macro column of the Jacobian against FD - * @details Uses centered finite differences to check the flux part of the Jacobian. - * @param [in] uoType Unit operation type - * @param [in] h Step size of centered finite differences - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testArrowHeadJacobianFD(const std::string& uoType, double h = 1e-6, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0); - - /** - * @brief Checks the bottom macro row and right macro column of the Jacobian against FD - * @details Uses centered finite differences to check the flux part of the Jacobian. - * @param [in] uoType Unit operation type - * @param [in] dynamicBinding Determines whether dynamic binding is used - * @param [in] h Step size of centered finite differences - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testArrowHeadJacobianFD(const std::string& uoType, bool dynamicBinding, double h = 1e-6, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0); - - /** - * @brief Checks the bottom macro row and right macro column of the Jacobian against FD - * @details Uses centered finite differences to check the flux part of the Jacobian. - * @param [in] jpp Configured column model - * @param [in] h Step size of centered finite differences - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testArrowHeadJacobianFD(cadet::JsonParameterProvider& jpp, double h = 1e-6, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0); - - /** - * @brief Checks the bottom macro row and right macro column of the Jacobian against FD in case of variable surface diffusion coefficient - * @details Uses centered finite differences to check the flux part of the Jacobian. - * @param [in] uoType Unit operation type - * @param [in] h Step size of centered finite differences - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testArrowHeadJacobianFDVariableParSurfDiff(const std::string& uoType, double h = 1e-6, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0); - - /** - * @brief Checks the forward sensitivity residual using analytic Jacobians - * @details Uses centered finite differences. - * @param [in] jpp Configured unit model - * @param [in] h Step size of centered finite differences - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testFwdSensJacobians(cadet::JsonParameterProvider jpp, double h = 1e-6, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0, const bool hasBinding=true); - - /** - * @brief Checks the forward sensitivity solution against finite differences - * @details Assumes column-like unit models and uses centered finite differences. Checks 4 parameters in - * the standard load-wash-elution test case: - * COL_DISPERSION, CONST_COEFF (salt, loading), SMA_KA (first protein), CONNECTION (volumetric flow rate). - * Each sensitivity is checked for quasi-stationary and dynamic binding, which means that - * in total 8 checks are performed. - * @param [in] uoType Unit operation type - * @param [in] disableSensErrorTest Determines whether sensitivities take part in local error test - * @param [in] fdStepSize Array with step sizes of centered finite differences - * @param [in] absTols Array with absolute error tolerances - * @param [in] relTols Array with relative error tolerances - * @param [in] passRates Array with rates of relative error test passes - */ - void testFwdSensSolutionFD(const std::string& uoType, const std::string& spatialMethod, bool disableSensErrorTest, double const* fdStepSize, double const* absTols, double const* relTols, double const* passRates); - - /** - * @brief Checks the forward sensitivity solution with forward flow against the one using backward flow - * @details Assumes column-like unit models and checks 4 parameters in the standard load-wash-elution test case: - * COL_DISPERSION, CONST_COEFF (salt, loading), SMA_KA (first protein), CONNECTION (volumetric flow rate). - * Each sensitivity is checked for quasi-stationary and dynamic binding, which means that - * in total 8 checks are performed. - * @param [in] uoType Unit operation type - * @param [in] absTols Array with absolute error tolerances - * @param [in] relTols Array with relative error tolerances - * @param [in] passRates Array with rates of relative error test passes - */ - void testFwdSensSolutionForwardBackward(const std::string& uoType, const std::string& spatialMethod, double const* absTols, double const* relTols, double const* passRates); - - /** - * @brief Checks consistent initialization using a model with linear binding - * @details Assumes column-like unit models and checks the residual of the model equations after - * consistent initialization. A linear binding model is applied. Both binding modes and - * both AD and analytic Jacobians are checked. - * @param [in] uoType Unit operation type - * @param [in] consTol Error tolerance for consistent initialization solver - * @param [in] absTol Error tolerance for checking whether algebraic residual is 0 - * @param [in] reqBnd specifies binding mode, defaults to using both kinetic and equilibrium - * @param [in] useAD specifies Jacobian mode, defaults to using both analytical and AD - */ - void testConsistentInitializationLinearBinding(const std::string& uoType, const std::string& spatialMethod, double consTol, double absTol, const int reqBnd = -1, const int useAD = -1); - - /** - * @brief Checks consistent initialization using a model with SMA binding - * @details Assumes column-like unit models and checks the residual of the model equations after - * consistent initialization. Both binding modes and both AD and analytic Jacobians are checked. - * @param [in] uoType Unit operation type - * @param [in] initState Initial state vector to start process from - * @param [in] consTol Error tolerance for consistent initialization solver - * @param [in] absTol Error tolerance for checking whether algebraic residual is 0 - * @param [in] reqBnd specifies binding mode, defaults to using both kinetic and equilibrium - * @param [in] useAD specifies Jacobian mode, defaults to using both analytical and AD - */ - void testConsistentInitializationSMABinding(const std::string& uoType, const std::string& spatialMethod, double const* const initState, double consTol, double absTol, const int reqBnd = -1, const int useAD = -1); - - /** - * @brief Checks consistent initialization of sensitivities in a column-like model - * @details Assumes column-like unit models and checks the residual of the sensitivity equations after - * consistent initialization. Both binding modes and both AD and analytic Jacobians are checked. - * @param [in] uoType Unit operation type - * @param [in] y State vector of original system - * @param [in] yDot Time derivative of state vector of original system - * @param [in] linearBinding Determines whether linear binding or SMA binding model is used - * @param [in] absTol Error tolerance for checking whether sensitivity residual is 0 - * @param [in] reqBnd specifies binding mode, defaults to using both kinetic and equilibrium - * @param [in] useAD specifies Jacobian mode, defaults to using both analytical and AD - */ - void testConsistentInitializationSensitivity(const std::string& uoType, const std::string& spatialMethod, double const* const y, double const* const yDot, bool linearBinding, double absTol, const int reqBnd = -1, const int useAD = -1); - - /** - * @brief Checks whether the inlet DOFs produce the identity matrix in the Jacobian of the unit operation - * @details Assumes column-like unit models. Both AD and analytic Jacobians are checked. - * @param [in] uoType Unit operation type - */ - void testInletDofJacobian(const std::string& uoType, const std::string& spatialMethod); - - /** - * @brief Runs a simulation test comparing against numerical reference data (outlet data) - * @param [in] setupFileRelPath Path to the setup data file from the directory of this file - * @param [in] refFileRelPath Path to the reference data file from the directory of this file - * @param [in] unitID ID of the unit of interest - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - * @param [in] disc Numerical discretization parameters - * @param [in] compare_sens Specifies whether sensitivities are included - */ - void testReferenceBenchmark(const std::string& modelFileRelPath, const std::string& refFileRelPath, const std::string& unitID, const std::vector absTol, const std::vector relTol, const cadet::test::column::DiscParams& disc, const bool compare_sens = false, const int simDataStride = 1); - - /** - * @brief Runs an EOC test comparing against numerical reference data (outlet data) - * @param [in] setupFileRelPath Path to the setup data file from the directory of this file. Model configuration is sufficient, rest will be copied from reference file. - * @param [in] refFileRelPath Path to the reference data file from the directory of this file - * @param [in] convFileRelPath Path to the convergence reference data file from the directory of this file - * @param [in] unitID ID of the unit of interest - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - * @param [in] nDisc number of discretizations to be computed for EOC - * @param [in] disc Numerical discretization parameters with starting resolution - * @param [in] compare_sens Specifies whether sensitivities are included - */ - void testEOCReferenceBenchmark(const std::string& modelFileRelPath, const std::string& refFileRelPath, const std::string& convFileRelPath, const std::string& unitID, const std::vector absTol, const std::vector relTol, const unsigned int nDisc, const cadet::test::column::DiscParams& disc, const bool compare_sens = false); - - /** - * @brief Runs a simulation test comparing against numerical reference data (outlet data), generated by a foreign source - * @detail Reference data from the foreign source needs to be stored in an h5 file with hierarchy output->solution->SOLUTION_OUTLET - * @param [in] setupFileRelPath Path to the setup data file from the directory of this file. Full CADET configuration is required - * @param [in] refFileRelPath Path to the reference data file from the directory of this file - * @param [in] unitID ID of the unit of interest - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - * @param [in] compIdx Index of component of interest. Defaults to -1 to compare all components - */ - void testForeignReferenceBenchmark(const std::string& configFileRelPath, const std::string& refFileRelPath, const std::string& unitID, const double absTol, const double relTol, const int compIdx); +struct DiscParams +{ + int nAxCells; + int nParCells; + + virtual ~DiscParams() = default; + + virtual int getNAxCells() const = 0; + virtual int getNParCells() const = 0; + + virtual void setDisc(JsonParameterProvider& jpp, const std::string unitID = "000") const = 0; +}; + +struct FVparams : public DiscParams +{ + int nAxCells; + int nParCells; + int wenoOrder; + int nRadCells; + + FVparams() : nAxCells(0), nParCells(0), wenoOrder(0), nRadCells(0) + { + } + FVparams(int nCol) : nAxCells(nCol), nParCells(0), wenoOrder(0), nRadCells(0) + { + } + FVparams(int nCol, int nPar) : nAxCells(nCol), nParCells(nPar), wenoOrder(0), nRadCells(0) + { + } + FVparams(int nCol, int nPar, int wenoOrder) : nAxCells(nCol), nParCells(nPar), wenoOrder(wenoOrder), nRadCells(0) + { + } + FVparams(int nCol, int nPar, int wenoOrder, int nRad) + : nAxCells(nCol), nParCells(nPar), wenoOrder(wenoOrder), nRadCells(nRad) + { + } + + int getNAxCells() const override + { + return nAxCells; + } + int getNParCells() const override + { + return nParCells; + } + void setWenoOrder(int order) + { + wenoOrder = order; + } + int getWenoOrder() + { + return wenoOrder; + } + void setNRad(int nRad) + { + nRadCells = nRad; + } + void setDisc(JsonParameterProvider& jpp, const std::string unitID = "000") const override; +}; + +struct DGparams : public DiscParams +{ + int exactIntegration; + int polyDeg; + int nElem; + int parPolyDeg; + int parNelem; + + DGparams() : exactIntegration(-1), polyDeg(0), nElem(0), parPolyDeg(0), parNelem(0) + { + } + DGparams(int exact, int poly, int elem) + : exactIntegration(exact), polyDeg(poly), nElem(elem), parPolyDeg(0), parNelem(0) + { + } + DGparams(int exact, int poly, int elem, int parPolyDeg, int parNelem) + : exactIntegration(exact), polyDeg(poly), nElem(elem), parPolyDeg(parPolyDeg), parNelem(parNelem) + { + } + + int getNAxCells() const override + { + return nElem; + } + int getNParCells() const override + { + return parNelem; + } + void setIntegrationMode(int integrationMode) + { + exactIntegration = integrationMode; + } + int getIntegrationMode() + { + return exactIntegration; + } + void setDisc(JsonParameterProvider& jpp, const std::string unitID = "000") const override; +}; + +/** + * @brief Sets the number of axial cells in a configuration of a column-like unit operation + * @details Overwrites the NCOL field in the discretization group of the given ParameterProvider. + * @param [in,out] jpp ParameterProvider to change the number of axial cells in + * @param [in] nCol Number of axial cells + * @param [in] unitID unit operation ID + */ +void setNumAxialCells(cadet::JsonParameterProvider& jpp, unsigned int nCol, std::string unitID = "000"); + +/** + * @brief Sets the WENO order in a configuration of a column-like unit operation + * @details Overwrites the WENO_ORDER field in the weno group of the given ParameterProvider. + * @param [in,out] jpp ParameterProvider to change the WENO order in + * @param [in] order Target order + */ +void setWenoOrder(cadet::JsonParameterProvider& jpp, int order); + +/** + * @brief Reverses the flow of a column-like unit operation + * @param [in,out] jpp ParameterProvider to change the flow direction in + */ +void reverseFlow(cadet::JsonParameterProvider& jpp); + +/** + * @brief Infers cross section area of column model from interstitial velocity + * @details Uses interstitial velocity and porosity to calculate cross section area. + * To this end, a volumetric flow rate of 1.0 m^3/s is assumed. Depending on + * @p dir, the velocity field is removed or set to @c +1.0 or @c -1.0 indicating + * direction of the flow inside the unit operation. + * + * @param [in,out] jpp ParameterProvider to add cross section area to + * @param [in] useTotalPorosity Determines whether TOTAL_POROSITY is used (@c true) or COL_POROSITY (@c false) + * @param [in] dir Flow direction in unit operation (@c 0 removes field, @c 1 standard direction, @c -1 flow reversal) + */ +void setCrossSectionArea(cadet::JsonParameterProvider& jpp, bool useTotalPorosity, int dir); + +/** + * @brief Returns the offset to the flux part in the local state vector + * @param [in] unit Unit operation + * @return Offset to the flux part + */ +unsigned int fluxOffsetOfColumnUnitOp(cadet::IUnitOperation* unit); +/** + * @brief Runs a simulation test comparing against (semi-)analytic single component pulse injection reference data + * @details Linear binding model is used in the column-like unit operation. + * @param [in] uoType Unit operation type + * @param [in] refFileRelPath Path to the reference data file from the directory of this file + * @param [in] forwardFlow Determines whether the unit operates in forward flow (@c true) or backwards flow (@c false) + * @param [in] dynamicBinding Determines whether dynamic binding (@c true) or rapid equilibrium (@c false) is used + * @param [in] disc spatial discretization parameters + * @param [in] method spatial discretization method + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testAnalyticBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, bool dynamicBinding, + DiscParams& disc, const std::string method, double absTol, double relTol); +void testAnalyticBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, bool dynamicBinding, + FVparams& disc, double absTol, double relTol); +void testAnalyticBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, bool dynamicBinding, + DGparams& disc, double absTol, double relTol); + +/** + * @brief Runs a simulation test comparing against (semi-)analytic single component pulse injection reference data + * @details The component is assumed to be non-binding. + * @param [in] uoType Unit operation type + * @param [in] refFileRelPath Path to the reference data file from the directory of this file + * @param [in] forwardFlow Determines whether the unit operates in forward flow (@c true) or backwards flow (@c false) + * @param [in] disc spatial discretization parameters + * @param [in] method spatial discretization method + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testAnalyticNonBindingBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, DiscParams& disc, + const std::string method, double absTol, double relTol); +void testAnalyticNonBindingBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, FVparams& disc, + double absTol, double relTol); +void testAnalyticNonBindingBenchmark(const char* uoType, const char* refFileRelPath, bool forwardFlow, DGparams& disc, + double absTol, double relTol); + +/** + * @brief Runs a simulation test comparing forward and backwards flow in the load-wash-elution example + * @param [in] uoType Unit operation type + * @param [in] disc spatial discretization parameters + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testForwardBackward(const char* uoType, FVparams disc, double absTol, double relTol); +void testForwardBackward(const char* uoType, DGparams disc, double absTol, double relTol); +void testForwardBackward(cadet::JsonParameterProvider jpp, double absTol, double relTol); + +/** + * @brief Checks the full Jacobian against AD and FD pattern switching + * @details Checks the analytic Jacobian against the AD Jacobian and checks both against the FD pattern. + * @param [in] jpp Configured column model + * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern + */ +void testJacobianAD(cadet::JsonParameterProvider& jpp, const double absTolFDpattern = 0.0); + +/** + * @brief Checks the full Jacobian against AD and FD pattern switching in case of variable surface diffusion coefficient + * @details Checks the analytic Jacobian against the AD Jacobian and checks both against the FD pattern. + * @param [in] uoType Unit operation type + * @param [in] dynamicBinding Determines whether dynamic binding is used + */ +void testJacobianADVariableParSurfDiff(const std::string& uoType, const std::string& spatialMethod, + bool dynamicBinding); + +/** + * @brief Checks the full Jacobian against AD and FD pattern switching from forward to backward flow and back + * @details Checks the analytic Jacobian against the AD Jacobian and checks both against the FD pattern. + * Checks both forward and backward flow mode as well as switching between them. + * + * @param [in] uoType Unit operation type + * @param [in] disc spatial discretization parameters + * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern + */ +void testJacobianForwardBackward(const char* uoType, FVparams disc, const double absTolFDpattern = 0.0); +void testJacobianForwardBackward(const char* uoType, DGparams disc, const double absTolFDpattern = 0.0); +void testJacobianForwardBackward(cadet::JsonParameterProvider& jpp, const double absTolFDpattern = 0.0); + +/** + * @brief Checks the full Jacobian against FD switching from forward to backward flow and back + * @details Checks the analytic Jacobian against the finite difference Jacobian. + * Checks both forward and backward flow mode as well as switching between them. + * Uses centered finite differences. + * + * @param [in] uoType Unit operation type + * @param [in] wenoOrder Order of the WENO method + * @param [in] h Step size of centered finite differences + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testJacobianWenoForwardBackwardFD(const std::string& uoType, const std::string& spatialMethod, int wenoOrder, + double h = 1e-6, double absTol = 0.0, + double relTol = std::numeric_limits::epsilon() * 100.0); + +/** + * @brief Checks the (analytic) time derivative Jacobian against FD + * @details Uses centered finite differences. + * @param [in] uoType Unit operation type + * @param [in] h Step size of centered finite differences + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testTimeDerivativeJacobianFD(const std::string& uoType, const std::string& spatialMethod, double h = 1e-6, + double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0); + +/** + * @brief Checks the bottom macro row and right macro column of the Jacobian against FD + * @details Uses centered finite differences to check the flux part of the Jacobian. + * @param [in] uoType Unit operation type + * @param [in] h Step size of centered finite differences + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testArrowHeadJacobianFD(const std::string& uoType, double h = 1e-6, double absTol = 0.0, + double relTol = std::numeric_limits::epsilon() * 100.0); + +/** + * @brief Checks the bottom macro row and right macro column of the Jacobian against FD + * @details Uses centered finite differences to check the flux part of the Jacobian. + * @param [in] uoType Unit operation type + * @param [in] dynamicBinding Determines whether dynamic binding is used + * @param [in] h Step size of centered finite differences + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testArrowHeadJacobianFD(const std::string& uoType, bool dynamicBinding, double h = 1e-6, double absTol = 0.0, + double relTol = std::numeric_limits::epsilon() * 100.0); + +/** + * @brief Checks the bottom macro row and right macro column of the Jacobian against FD + * @details Uses centered finite differences to check the flux part of the Jacobian. + * @param [in] jpp Configured column model + * @param [in] h Step size of centered finite differences + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testArrowHeadJacobianFD(cadet::JsonParameterProvider& jpp, double h = 1e-6, double absTol = 0.0, + double relTol = std::numeric_limits::epsilon() * 100.0); + +/** + * @brief Checks the bottom macro row and right macro column of the Jacobian against FD in case of variable surface + * diffusion coefficient + * @details Uses centered finite differences to check the flux part of the Jacobian. + * @param [in] uoType Unit operation type + * @param [in] h Step size of centered finite differences + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testArrowHeadJacobianFDVariableParSurfDiff(const std::string& uoType, double h = 1e-6, double absTol = 0.0, + double relTol = std::numeric_limits::epsilon() * 100.0); + +/** + * @brief Checks the forward sensitivity residual using analytic Jacobians + * @details Uses centered finite differences. + * @param [in] jpp Configured unit model + * @param [in] h Step size of centered finite differences + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testFwdSensJacobians(cadet::JsonParameterProvider jpp, double h = 1e-6, double absTol = 0.0, + double relTol = std::numeric_limits::epsilon() * 100.0, const bool hasBinding = true); + +/** + * @brief Checks the forward sensitivity solution against finite differences + * @details Assumes column-like unit models and uses centered finite differences. Checks 4 parameters in + * the standard load-wash-elution test case: + * COL_DISPERSION, CONST_COEFF (salt, loading), SMA_KA (first protein), CONNECTION (volumetric flow rate). + * Each sensitivity is checked for quasi-stationary and dynamic binding, which means that + * in total 8 checks are performed. + * @param [in] uoType Unit operation type + * @param [in] disableSensErrorTest Determines whether sensitivities take part in local error test + * @param [in] fdStepSize Array with step sizes of centered finite differences + * @param [in] absTols Array with absolute error tolerances + * @param [in] relTols Array with relative error tolerances + * @param [in] passRates Array with rates of relative error test passes + */ +void testFwdSensSolutionFD(const std::string& uoType, const std::string& spatialMethod, bool disableSensErrorTest, + double const* fdStepSize, double const* absTols, double const* relTols, + double const* passRates); + +/** + * @brief Checks the forward sensitivity solution with forward flow against the one using backward flow + * @details Assumes column-like unit models and checks 4 parameters in the standard load-wash-elution test case: + * COL_DISPERSION, CONST_COEFF (salt, loading), SMA_KA (first protein), CONNECTION (volumetric flow rate). + * Each sensitivity is checked for quasi-stationary and dynamic binding, which means that + * in total 8 checks are performed. + * @param [in] uoType Unit operation type + * @param [in] absTols Array with absolute error tolerances + * @param [in] relTols Array with relative error tolerances + * @param [in] passRates Array with rates of relative error test passes + */ +void testFwdSensSolutionForwardBackward(const std::string& uoType, const std::string& spatialMethod, + double const* absTols, double const* relTols, double const* passRates); + +/** + * @brief Checks consistent initialization using a model with linear binding + * @details Assumes column-like unit models and checks the residual of the model equations after + * consistent initialization. A linear binding model is applied. Both binding modes and + * both AD and analytic Jacobians are checked. + * @param [in] uoType Unit operation type + * @param [in] consTol Error tolerance for consistent initialization solver + * @param [in] absTol Error tolerance for checking whether algebraic residual is 0 + * @param [in] reqBnd specifies binding mode, defaults to using both kinetic and equilibrium + * @param [in] useAD specifies Jacobian mode, defaults to using both analytical and AD + */ +void testConsistentInitializationLinearBinding(const std::string& uoType, const std::string& spatialMethod, + double consTol, double absTol, const int reqBnd = -1, + const int useAD = -1); + +/** + * @brief Checks consistent initialization using a model with SMA binding + * @details Assumes column-like unit models and checks the residual of the model equations after + * consistent initialization. Both binding modes and both AD and analytic Jacobians are checked. + * @param [in] uoType Unit operation type + * @param [in] initState Initial state vector to start process from + * @param [in] consTol Error tolerance for consistent initialization solver + * @param [in] absTol Error tolerance for checking whether algebraic residual is 0 + * @param [in] reqBnd specifies binding mode, defaults to using both kinetic and equilibrium + * @param [in] useAD specifies Jacobian mode, defaults to using both analytical and AD + */ +void testConsistentInitializationSMABinding(const std::string& uoType, const std::string& spatialMethod, + double const* const initState, double consTol, double absTol, + const int reqBnd = -1, const int useAD = -1); + +/** + * @brief Checks consistent initialization of sensitivities in a column-like model + * @details Assumes column-like unit models and checks the residual of the sensitivity equations after + * consistent initialization. Both binding modes and both AD and analytic Jacobians are checked. + * @param [in] uoType Unit operation type + * @param [in] y State vector of original system + * @param [in] yDot Time derivative of state vector of original system + * @param [in] linearBinding Determines whether linear binding or SMA binding model is used + * @param [in] absTol Error tolerance for checking whether sensitivity residual is 0 + * @param [in] reqBnd specifies binding mode, defaults to using both kinetic and equilibrium + * @param [in] useAD specifies Jacobian mode, defaults to using both analytical and AD + */ +void testConsistentInitializationSensitivity(const std::string& uoType, const std::string& spatialMethod, + double const* const y, double const* const yDot, bool linearBinding, + double absTol, const int reqBnd = -1, const int useAD = -1); + +/** + * @brief Checks whether the inlet DOFs produce the identity matrix in the Jacobian of the unit operation + * @details Assumes column-like unit models. Both AD and analytic Jacobians are checked. + * @param [in] uoType Unit operation type + */ +void testInletDofJacobian(const std::string& uoType, const std::string& spatialMethod); + +/** + * @brief Runs a simulation test comparing against numerical reference data (outlet data) + * @param [in] setupFileRelPath Path to the setup data file from the directory of this file + * @param [in] refFileRelPath Path to the reference data file from the directory of this file + * @param [in] unitID ID of the unit of interest + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + * @param [in] disc Numerical discretization parameters + * @param [in] compare_sens Specifies whether sensitivities are included + */ +void testReferenceBenchmark(const std::string& modelFileRelPath, const std::string& refFileRelPath, + const std::string& unitID, const std::vector absTol, + const std::vector relTol, const cadet::test::column::DiscParams& disc, + const bool compare_sens = false, const int simDataStride = 1); + +/** + * @brief Runs an EOC test comparing against numerical reference data (outlet data) + * @param [in] setupFileRelPath Path to the setup data file from the directory of this file. Model configuration is + * sufficient, rest will be copied from reference file. + * @param [in] refFileRelPath Path to the reference data file from the directory of this file + * @param [in] convFileRelPath Path to the convergence reference data file from the directory of this file + * @param [in] unitID ID of the unit of interest + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + * @param [in] nDisc number of discretizations to be computed for EOC + * @param [in] disc Numerical discretization parameters with starting resolution + * @param [in] compare_sens Specifies whether sensitivities are included + */ +void testEOCReferenceBenchmark(const std::string& modelFileRelPath, const std::string& refFileRelPath, + const std::string& convFileRelPath, const std::string& unitID, + const std::vector absTol, const std::vector relTol, + const unsigned int nDisc, const cadet::test::column::DiscParams& disc, + const bool compare_sens = false); + +/** + * @brief Runs a simulation test comparing against numerical reference data (outlet data), generated by a foreign source + * @detail Reference data from the foreign source needs to be stored in an h5 file with hierarchy + * output->solution->SOLUTION_OUTLET + * @param [in] setupFileRelPath Path to the setup data file from the directory of this file. Full CADET configuration is + * required + * @param [in] refFileRelPath Path to the reference data file from the directory of this file + * @param [in] unitID ID of the unit of interest + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + * @param [in] compIdx Index of component of interest. Defaults to -1 to compare all components + */ +void testForeignReferenceBenchmark(const std::string& configFileRelPath, const std::string& refFileRelPath, + const std::string& unitID, const double absTol, const double relTol, + const int compIdx); } // namespace column } // namespace test } // namespace cadet -#endif // CADETTEST_COLUMNSIMTEST_HPP_ +#endif // CADETTEST_COLUMNSIMTEST_HPP_ diff --git a/test/ConvectionDispersionOperator.cpp b/test/ConvectionDispersionOperator.cpp index 84670a05f..7b3e08b71 100644 --- a/test/ConvectionDispersionOperator.cpp +++ b/test/ConvectionDispersionOperator.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -35,119 +35,122 @@ namespace { - /** - * @brief Fills the bulk part of the state vector with a given function - * @details The function @p f uses the component index, the column cell index, and the - * current (forward flow) index to assign a value. - * @param [out] y Filled state vector - * @param [in] f Function for computing the content of the bulk part of the state vector - * @param [in] nComp Number of components - * @param [in] nCol Number of bulk cells per component - */ - inline void fillStateBulkFwd(double* y, std::function f, unsigned int nComp, unsigned int nCol) +/** + * @brief Fills the bulk part of the state vector with a given function + * @details The function @p f uses the component index, the column cell index, and the + * current (forward flow) index to assign a value. + * @param [out] y Filled state vector + * @param [in] f Function for computing the content of the bulk part of the state vector + * @param [in] nComp Number of components + * @param [in] nCol Number of bulk cells per component + */ +inline void fillStateBulkFwd(double* y, std::function f, + unsigned int nComp, unsigned int nCol) +{ + for (unsigned int col = 0; col < nCol; ++col) { - for (unsigned int col = 0; col < nCol; ++col) + double* const r = y + nComp + nComp * col; + for (unsigned int comp = 0; comp < nComp; ++comp) { - double* const r = y + nComp + nComp * col; - for (unsigned int comp = 0; comp < nComp; ++comp) - { - r[comp] = f(comp, col, col * nComp + comp); - } + r[comp] = f(comp, col, col * nComp + comp); } } +} - /** - * @brief Fills the bulk part of the state vector with a given function assuming backwards flow - * @details The function @p f uses the component index, the column cell index, and the - * current (forward flow) index to assign a value. - * @param [out] y Filled state vector - * @param [in] f Function for computing the content of the bulk part of the state vector - * @param [in] nComp Number of components - * @param [in] nCol Number of bulk cells per component - */ - inline void fillStateBulkBwd(double* y, std::function f, unsigned int nComp, unsigned int nCol) +/** + * @brief Fills the bulk part of the state vector with a given function assuming backwards flow + * @details The function @p f uses the component index, the column cell index, and the + * current (forward flow) index to assign a value. + * @param [out] y Filled state vector + * @param [in] f Function for computing the content of the bulk part of the state vector + * @param [in] nComp Number of components + * @param [in] nCol Number of bulk cells per component + */ +inline void fillStateBulkBwd(double* y, std::function f, + unsigned int nComp, unsigned int nCol) +{ + for (unsigned int col = nCol - 1; col < nCol; --col) { - for (unsigned int col = nCol-1; col < nCol; --col) + double* const r = y + nComp + nComp * col; + for (unsigned int comp = 0; comp < nComp; ++comp) { - double* const r = y + nComp + nComp * col; - for (unsigned int comp = 0; comp < nComp; ++comp) - { - r[comp] = f(comp, nCol - col - 1, (nCol - col - 1) * nComp + comp); - } + r[comp] = f(comp, nCol - col - 1, (nCol - col - 1) * nComp + comp); } } +} - /** - * @brief Compares two residual bulk parts where one is created by forward and the other by backwards flow - * @param [in] r1 Residual of one mode (forward / backward) - * @param [in] r2 Residual of the other mode (backward / forward) - * @param [in] nComp Number of components - * @param [in] nCol Number of bulk cells per component - */ - inline void compareResidualBulkFwdBwd(double const* r1, double const* r2, int nComp, int nCol) +/** + * @brief Compares two residual bulk parts where one is created by forward and the other by backwards flow + * @param [in] r1 Residual of one mode (forward / backward) + * @param [in] r2 Residual of the other mode (backward / forward) + * @param [in] nComp Number of components + * @param [in] nCol Number of bulk cells per component + */ +inline void compareResidualBulkFwdBwd(double const* r1, double const* r2, int nComp, int nCol) +{ + for (int col = 0; col < nCol; ++col) { - for (int col = 0; col < nCol; ++col) + double const* const a = r1 + nComp + nComp * col; + double const* const b = r2 + nComp + nComp * (nCol - col - 1); + for (int comp = 0; comp < nComp; ++comp) { - double const* const a = r1 + nComp + nComp * col; - double const* const b = r2 + nComp + nComp * (nCol - col - 1); - for (int comp = 0; comp < nComp; ++comp) - { - CHECK(a[comp] == RelApprox(b[comp])); - } - } + CHECK(a[comp] == RelApprox(b[comp])); + } } +} - /** - * @brief Compares two residual bulk parts that have the same flow direction - * @param [in] r1 Residual - * @param [in] r2 Residual - * @param [in] nComp Number of components - * @param [in] nCol Number of bulk cells per component - */ - inline void compareResidualBulkFwdFwd(double const* r1, double const* r2, int nComp, int nCol) +/** + * @brief Compares two residual bulk parts that have the same flow direction + * @param [in] r1 Residual + * @param [in] r2 Residual + * @param [in] nComp Number of components + * @param [in] nCol Number of bulk cells per component + */ +inline void compareResidualBulkFwdFwd(double const* r1, double const* r2, int nComp, int nCol) +{ + for (int col = 0; col < nCol; ++col) { - for (int col = 0; col < nCol; ++col) + double const* const a = r1 + nComp + nComp * col; + double const* const b = r2 + nComp + nComp * col; + for (int comp = 0; comp < nComp; ++comp) { - double const* const a = r1 + nComp + nComp * col; - double const* const b = r2 + nComp + nComp * col; - for (int comp = 0; comp < nComp; ++comp) - { - CHECK(a[comp] == RelApprox(b[comp])); - } - } + CHECK(a[comp] == RelApprox(b[comp])); + } } +} - template - inline cadet::active* createAndConfigureOperator(OperatorType& convDispOp, int& nComp, int& nCol, int wenoOrder) - { - // Obtain parameters from some test case - cadet::JsonParameterProvider jpp = createColumnWithSMA("GENERAL_RATE_MODEL", "FV"); - cadet::test::column::setWenoOrder(jpp, wenoOrder); - - nComp = jpp.getInt("NCOMP"); - jpp.pushScope("discretization"); - nCol = jpp.getInt("NCOL"); - jpp.popScope(); - - // Configure the operator - typedef std::unordered_map ParameterMap; - ParameterMap parameters; - cadet::ModelBuilder builder; - REQUIRE(convDispOp.configureModelDiscretization(jpp, builder, nComp, nCol)); - REQUIRE(convDispOp.configure(0, jpp, parameters)); - - // Make sure that VELOCITY parameter is present - const cadet::ParameterId paramVelocity = cadet::makeParamId(cadet::hashString("VELOCITY"), 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep); - const typename ParameterMap::iterator itVelocity = parameters.find(paramVelocity); - REQUIRE(itVelocity != parameters.end()); - - return itVelocity->second; - } +template +inline cadet::active* createAndConfigureOperator(OperatorType& convDispOp, int& nComp, int& nCol, int wenoOrder) +{ + // Obtain parameters from some test case + cadet::JsonParameterProvider jpp = createColumnWithSMA("GENERAL_RATE_MODEL", "FV"); + cadet::test::column::setWenoOrder(jpp, wenoOrder); + + nComp = jpp.getInt("NCOMP"); + jpp.pushScope("discretization"); + nCol = jpp.getInt("NCOL"); + jpp.popScope(); + + // Configure the operator + typedef std::unordered_map ParameterMap; + ParameterMap parameters; + cadet::ModelBuilder builder; + REQUIRE(convDispOp.configureModelDiscretization(jpp, builder, nComp, nCol)); + REQUIRE(convDispOp.configure(0, jpp, parameters)); + + // Make sure that VELOCITY parameter is present + const cadet::ParameterId paramVelocity = + cadet::makeParamId(cadet::hashString("VELOCITY"), 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep); + const typename ParameterMap::iterator itVelocity = parameters.find(paramVelocity); + REQUIRE(itVelocity != parameters.end()); + + return itVelocity->second; +} } // namespace -template -void testResidualBulkWenoForwardBackward(int wenoOrder) +template void testResidualBulkWenoForwardBackward(int wenoOrder) { SECTION("Forward vs backward flow residual bulk (WENO=" + std::to_string(wenoOrder) + ")") { @@ -173,7 +176,8 @@ void testResidualBulkWenoForwardBackward(int wenoOrder) SECTION("Forward flow yields backwards flow residual (zero state)") { // Forward flow residual - convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, res.data(), false, cadet::WithoutParamSensitivity()); + convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, res.data(), false, + cadet::WithoutParamSensitivity()); // Reverse flow velocity->setValue(-origVelocity); @@ -181,7 +185,8 @@ void testResidualBulkWenoForwardBackward(int wenoOrder) std::vector resRev(nDof, 0.0); // Backward flow residual - convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, resRev.data(), false, cadet::WithoutParamSensitivity()); + convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, resRev.data(), false, + cadet::WithoutParamSensitivity()); // Compare compareResidualBulkFwdBwd(res.data(), resRev.data(), nComp, nCol); @@ -192,7 +197,8 @@ void testResidualBulkWenoForwardBackward(int wenoOrder) std::vector resFwd2(nDof, 0.0); // Forward flow residual - convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, resFwd2.data(), false, cadet::WithoutParamSensitivity()); + convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, resFwd2.data(), false, + cadet::WithoutParamSensitivity()); // Compare against first forward flow residual compareResidualBulkFwdFwd(res.data(), resFwd2.data(), nComp, nCol); @@ -202,12 +208,19 @@ void testResidualBulkWenoForwardBackward(int wenoOrder) SECTION("Forward flow yields backwards flow residual (nonzero state)") { // Fill state vector with some values - fillStateBulkFwd(y.data(), [](unsigned int comp, unsigned int col, unsigned int idx) { return std::abs(std::sin(idx * 0.13)); }, nComp, nCol); + fillStateBulkFwd( + y.data(), + [](unsigned int comp, unsigned int col, unsigned int idx) { return std::abs(std::sin(idx * 0.13)); }, + nComp, nCol); - convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, res.data(), false, cadet::WithoutParamSensitivity()); + convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, res.data(), false, + cadet::WithoutParamSensitivity()); // Reverse state for backwards flow - fillStateBulkBwd(y.data(), [](unsigned int comp, unsigned int col, unsigned int idx) { return std::abs(std::sin(idx * 0.13)); }, nComp, nCol); + fillStateBulkBwd( + y.data(), + [](unsigned int comp, unsigned int col, unsigned int idx) { return std::abs(std::sin(idx * 0.13)); }, + nComp, nCol); // Reverse flow velocity->setValue(-origVelocity); @@ -215,7 +228,8 @@ void testResidualBulkWenoForwardBackward(int wenoOrder) std::vector resRev(nDof, 0.0); // Backward flow residual - convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, resRev.data(), false, cadet::WithoutParamSensitivity()); + convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, resRev.data(), false, + cadet::WithoutParamSensitivity()); // Compare compareResidualBulkFwdBwd(res.data(), resRev.data(), nComp, nCol); @@ -226,10 +240,14 @@ void testResidualBulkWenoForwardBackward(int wenoOrder) std::vector resFwd2(nDof, 0.0); // Fill state vector with forward values again - fillStateBulkFwd(y.data(), [](unsigned int comp, unsigned int col, unsigned int idx) { return std::abs(std::sin(idx * 0.13)); }, nComp, nCol); + fillStateBulkFwd( + y.data(), + [](unsigned int comp, unsigned int col, unsigned int idx) { return std::abs(std::sin(idx * 0.13)); }, + nComp, nCol); // Forward flow residual - convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, resFwd2.data(), false, cadet::WithoutParamSensitivity()); + convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, resFwd2.data(), false, + cadet::WithoutParamSensitivity()); // Compare against first forward flow residual compareResidualBulkFwdFwd(res.data(), resFwd2.data(), nComp, nCol); @@ -237,8 +255,7 @@ void testResidualBulkWenoForwardBackward(int wenoOrder) } } -template -void testTimeDerivativeBulkJacobianFD(double h, double absTol, double relTol) +template void testTimeDerivativeBulkJacobianFD(double h, double absTol, double relTol) { int nComp = 0; int nCol = 0; @@ -257,18 +274,23 @@ void testTimeDerivativeBulkJacobianFD(double h, double absTol, double relTol) std::vector jacCol2(nDof, 0.0); // Fill state vectors with some values - cadet::test::util::populate(y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); - cadet::test::util::populate(yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); // Compare Jacobians cadet::test::compareJacobianFD( - [&](double const* dir, double* res) -> void { convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), dir, res, false, cadet::WithoutParamSensitivity()); }, - [&](double const* dir, double* res) -> void { convDispOp.multiplyWithDerivativeJacobian(cadet::SimulationTime{0.0, 0u}, dir, res); }, + [&](double const* dir, double* res) -> void { + convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), dir, res, false, cadet::WithoutParamSensitivity()); + }, + [&](double const* dir, double* res) -> void { + convDispOp.multiplyWithDerivativeJacobian(cadet::SimulationTime{0.0, 0u}, dir, res); + }, yDot.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), nDof, h, absTol, relTol); } -template -void testBulkJacobianWenoForwardBackward(int wenoOrder) +template void testBulkJacobianWenoForwardBackward(int wenoOrder) { SECTION("Forward vs backward flow Jacobian (WENO=" + std::to_string(wenoOrder) + ")") { @@ -298,12 +320,14 @@ void testBulkJacobianWenoForwardBackward(int wenoOrder) std::vector jacCol2(nDof, 0.0); // Fill state vector with some values - cadet::test::util::populate(y.data() + nComp, [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof - nComp); + cadet::test::util::populate( + y.data() + nComp, [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof - nComp); SECTION("Forward then backward flow (nonzero state)") { // Compute state Jacobian - opAna.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, jacDir.data(), true, cadet::WithoutParamSensitivity()); + opAna.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, jacDir.data(), true, + cadet::WithoutParamSensitivity()); std::fill(jacDir.begin(), jacDir.end(), 0.0); cadet::ad::copyToAd(y.data(), adY, nDof); @@ -311,30 +335,31 @@ void testBulkJacobianWenoForwardBackward(int wenoOrder) opAD.residual(DummyModel(), 0.0, 0u, adY, nullptr, adRes, false, cadet::WithoutParamSensitivity()); opAD.extractJacobianFromAD(adRes, 0); - const std::function anaResidual = [&](double const* lDir, double* res) -> void - { - opAna.residual(DummyModel(), 0.0, 0u, lDir - nComp, nullptr, res - nComp, false, cadet::WithoutParamSensitivity()); - }; + const std::function anaResidual = [&](double const* lDir, + double* res) -> void { + opAna.residual(DummyModel(), 0.0, 0u, lDir - nComp, nullptr, res - nComp, false, + cadet::WithoutParamSensitivity()); + }; - const std::function anaMultJac = [&](double const* lDir, double* res) -> void - { - opAna.jacobian().multiplyVector(lDir, 1.0, 0.0, res); - }; + const std::function anaMultJac = + [&](double const* lDir, double* res) -> void { opAna.jacobian().multiplyVector(lDir, 1.0, 0.0, res); }; - const std::function adMultJac = [&](double const* lDir, double* res) -> void - { - opAD.jacobian().multiplyVector(lDir, 1.0, 0.0, res); - }; + const std::function adMultJac = [&](double const* lDir, double* res) -> void { + opAD.jacobian().multiplyVector(lDir, 1.0, 0.0, res); + }; // Compare Jacobians // Check AD Jacobian pattern - cadet::test::checkJacobianPatternFD(anaResidual, adMultJac, y.data() + nComp, jacDir.data() + nComp, jacCol1.data() + nComp, jacCol2.data() + nComp, nDof - nComp); + cadet::test::checkJacobianPatternFD(anaResidual, adMultJac, y.data() + nComp, jacDir.data() + nComp, + jacCol1.data() + nComp, jacCol2.data() + nComp, nDof - nComp); // Check analytic Jacobian pattern - cadet::test::checkJacobianPatternFD(anaResidual, anaMultJac, y.data() + nComp, jacDir.data() + nComp, jacCol1.data() + nComp, jacCol2.data() + nComp, nDof - nComp); + cadet::test::checkJacobianPatternFD(anaResidual, anaMultJac, y.data() + nComp, jacDir.data() + nComp, + jacCol1.data() + nComp, jacCol2.data() + nComp, nDof - nComp); // Check analytic vs AD Jacobian - cadet::test::compareJacobian(anaMultJac, adMultJac, jacDir.data(), jacCol1.data(), jacCol2.data(), nDof - nComp); + cadet::test::compareJacobian(anaMultJac, adMultJac, jacDir.data(), jacCol1.data(), jacCol2.data(), + nDof - nComp); // Reverse flow anaVelocity->setValue(-anaVelocity->getValue()); @@ -345,7 +370,8 @@ void testBulkJacobianWenoForwardBackward(int wenoOrder) opAD.notifyDiscontinuousSectionTransition(0.0, 0u, cadet::AdJacobianParams{adRes, adY, 0u}); // Compute state Jacobian - opAna.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, jacDir.data(), true, cadet::WithoutParamSensitivity()); + opAna.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, jacDir.data(), true, + cadet::WithoutParamSensitivity()); std::fill(jacDir.begin(), jacDir.end(), 0.0); cadet::ad::copyToAd(y.data(), adY, nDof); @@ -355,13 +381,16 @@ void testBulkJacobianWenoForwardBackward(int wenoOrder) // Compare Jacobians // Check AD Jacobian pattern - cadet::test::checkJacobianPatternFD(anaResidual, adMultJac, y.data() + nComp, jacDir.data() + nComp, jacCol1.data() + nComp, jacCol2.data() + nComp, nDof - nComp); + cadet::test::checkJacobianPatternFD(anaResidual, adMultJac, y.data() + nComp, jacDir.data() + nComp, + jacCol1.data() + nComp, jacCol2.data() + nComp, nDof - nComp); // Check analytic Jacobian pattern - cadet::test::checkJacobianPatternFD(anaResidual, anaMultJac, y.data() + nComp, jacDir.data() + nComp, jacCol1.data() + nComp, jacCol2.data() + nComp, nDof - nComp); + cadet::test::checkJacobianPatternFD(anaResidual, anaMultJac, y.data() + nComp, jacDir.data() + nComp, + jacCol1.data() + nComp, jacCol2.data() + nComp, nDof - nComp); // Check analytic vs AD Jacobian - cadet::test::compareJacobian(anaMultJac, adMultJac, jacDir.data(), jacCol1.data(), jacCol2.data(), nDof - nComp); + cadet::test::compareJacobian(anaMultJac, adMultJac, jacDir.data(), jacCol1.data(), jacCol2.data(), + nDof - nComp); } delete[] adRes; @@ -369,25 +398,29 @@ void testBulkJacobianWenoForwardBackward(int wenoOrder) } } - struct AxialFlow { typedef cadet::model::parts::convdisp::AxialFlowParameters Params; - static void sparsityPattern(cadet::linalg::SparsityPatternRowIterator itBegin, unsigned int nComp, unsigned int nCol, int strideCell, double u, cadet::Weno& weno) + static void sparsityPattern(cadet::linalg::SparsityPatternRowIterator itBegin, unsigned int nComp, + unsigned int nCol, int strideCell, double u, cadet::Weno& weno) { cadet::model::parts::convdisp::sparsityPatternAxial(itBegin, nComp, nCol, strideCell, u, weno); } static void residual(double const* y, double const* yDot, double* res, const Params& fp) { - cadet::model::parts::convdisp::residualKernelAxial(cadet::SimulationTime{0.0, 0u}, y, yDot, res, cadet::linalg::BandedSparseRowIterator(), fp); + cadet::model::parts::convdisp::residualKernelAxial( + cadet::SimulationTime{0.0, 0u}, y, yDot, res, cadet::linalg::BandedSparseRowIterator(), fp); } template - static void residualWithJacobian(double const* y, double const* yDot, double* res, IteratorType jacBegin, const Params& fp) + static void residualWithJacobian(double const* y, double const* yDot, double* res, IteratorType jacBegin, + const Params& fp) { - cadet::model::parts::convdisp::residualKernelAxial(cadet::SimulationTime{0.0, 0u}, y, yDot, res, jacBegin, fp); + cadet::model::parts::convdisp::residualKernelAxial( + cadet::SimulationTime{0.0, 0u}, y, yDot, res, jacBegin, fp); } std::unique_ptr parDep; @@ -398,24 +431,23 @@ struct AxialFlow parDep.reset(paramDepFactory.createParameterDependence("CONSTANT_ONE")); } - Params makeParams(double u, cadet::active const* d_ax, double h, double* wenoDerivatives, cadet::Weno* weno, cadet::ArrayPool* stencilMemory, int strideCell, int nComp, int nCol) + Params makeParams(double u, cadet::active const* d_ax, double h, double* wenoDerivatives, cadet::Weno* weno, + cadet::ArrayPool* stencilMemory, int strideCell, int nComp, int nCol) { - return Params { - u, - d_ax, - h, - wenoDerivatives, - weno, - stencilMemory, - 1e-12, - strideCell, - static_cast(nComp), - static_cast(nCol), - 0u, - static_cast(nComp), - parDep.get(), - DummyModel() - }; + return Params{u, + d_ax, + h, + wenoDerivatives, + weno, + stencilMemory, + 1e-12, + strideCell, + static_cast(nComp), + static_cast(nCol), + 0u, + static_cast(nComp), + parDep.get(), + DummyModel()}; } }; @@ -423,20 +455,25 @@ struct RadialFlow { typedef cadet::model::parts::convdisp::RadialFlowParameters Params; - static void sparsityPattern(cadet::linalg::SparsityPatternRowIterator itBegin, unsigned int nComp, unsigned int nCol, int strideCell, double u, cadet::Weno& weno) + static void sparsityPattern(cadet::linalg::SparsityPatternRowIterator itBegin, unsigned int nComp, + unsigned int nCol, int strideCell, double u, cadet::Weno& weno) { cadet::model::parts::convdisp::sparsityPatternRadial(itBegin, nComp, nCol, strideCell, u, weno); } static void residual(double const* y, double const* yDot, double* res, const Params& fp) { - cadet::model::parts::convdisp::residualKernelRadial(cadet::SimulationTime{0.0, 0u}, y, yDot, res, cadet::linalg::BandedSparseRowIterator(), fp); + cadet::model::parts::convdisp::residualKernelRadial( + cadet::SimulationTime{0.0, 0u}, y, yDot, res, cadet::linalg::BandedSparseRowIterator(), fp); } template - static void residualWithJacobian(double const* y, double const* yDot, double* res, IteratorType jacBegin, const Params& fp) + static void residualWithJacobian(double const* y, double const* yDot, double* res, IteratorType jacBegin, + const Params& fp) { - cadet::model::parts::convdisp::residualKernelRadial(cadet::SimulationTime{0.0, 0u}, y, yDot, res, jacBegin, fp); + cadet::model::parts::convdisp::residualKernelRadial( + cadet::SimulationTime{0.0, 0u}, y, yDot, res, jacBegin, fp); } std::unique_ptr parDep; @@ -450,7 +487,8 @@ struct RadialFlow parDep.reset(paramDepFactory.createParameterDependence("CONSTANT_ONE")); } - Params makeParams(double u, cadet::active const* d_rad, double h, double* wenoDerivatives, cadet::Weno* weno, cadet::ArrayPool* stencilMemory, int strideCell, int nComp, int nCol) + Params makeParams(double u, cadet::active const* d_rad, double h, double* wenoDerivatives, cadet::Weno* weno, + cadet::ArrayPool* stencilMemory, int strideCell, int nComp, int nCol) { centers.resize(10); sizes.resize(10); @@ -464,27 +502,23 @@ struct RadialFlow } bounds.back() = 11 * h; - return Params { - u, - d_rad, - centers.data(), - sizes.data(), - bounds.data(), - stencilMemory, - strideCell, - static_cast(nComp), - static_cast(nCol), - 0u, - static_cast(nComp), - parDep.get(), - DummyModel() - }; + return Params{u, + d_rad, + centers.data(), + sizes.data(), + bounds.data(), + stencilMemory, + strideCell, + static_cast(nComp), + static_cast(nCol), + 0u, + static_cast(nComp), + parDep.get(), + DummyModel()}; } }; - -template -void testBulkJacobianSparsityWeno(int wenoOrder, bool forwardFlow) +template void testBulkJacobianSparsityWeno(int wenoOrder, bool forwardFlow) { SECTION("WENO=" + std::to_string(wenoOrder)) { @@ -504,23 +538,16 @@ void testBulkJacobianSparsityWeno(int wenoOrder, bool forwardFlow) weno.boundaryTreatment(cadet::Weno::BoundaryTreatment::ReduceOrder); cadet::ParameterDependenceFactory paramDepFactory; - std::unique_ptr parDep(paramDepFactory.createParameterDependence("CONSTANT_ONE")); + std::unique_ptr parDep( + paramDepFactory.createParameterDependence("CONSTANT_ONE")); FlowType ft; - typename FlowType::Params fp = ft.makeParams( - u, - d_c.data(), - h, - wenoDerivatives.data(), - &weno, - &stencilMemory, - strideCell, - nComp, - nCol - ); + typename FlowType::Params fp = + ft.makeParams(u, d_c.data(), h, wenoDerivatives.data(), &weno, &stencilMemory, strideCell, nComp, nCol); // Obtain sparsity pattern - cadet::linalg::SparsityPattern pattern(nComp * nCol, std::max(weno.lowerBandwidth() + 1u, 1u) + 1u + std::max(weno.upperBandwidth(), 1u)); + cadet::linalg::SparsityPattern pattern(nComp * nCol, std::max(weno.lowerBandwidth() + 1u, 1u) + 1u + + std::max(weno.upperBandwidth(), 1u)); FlowType::sparsityPattern(pattern.row(0), nComp, nCol, strideCell, u, weno); // Obtain memory for state, Jacobian columns @@ -530,7 +557,8 @@ void testBulkJacobianSparsityWeno(int wenoOrder, bool forwardFlow) std::vector jacCol2(nDof, 0.0); // Fill state vector with some values - cadet::test::util::populate(y.data() + nComp, [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof - nComp); + cadet::test::util::populate( + y.data() + nComp, [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof - nComp); for (int col = 0; col < pattern.rows(); ++col) { @@ -559,8 +587,7 @@ void testBulkJacobianSparsityWeno(int wenoOrder, bool forwardFlow) } } -template -void testBulkJacobianSparseBandedWeno(int wenoOrder, bool forwardFlow) +template void testBulkJacobianSparseBandedWeno(int wenoOrder, bool forwardFlow) { SECTION("WENO=" + std::to_string(wenoOrder)) { @@ -580,20 +607,12 @@ void testBulkJacobianSparseBandedWeno(int wenoOrder, bool forwardFlow) weno.boundaryTreatment(cadet::Weno::BoundaryTreatment::ReduceOrder); cadet::ParameterDependenceFactory paramDepFactory; - std::unique_ptr parDep(paramDepFactory.createParameterDependence("CONSTANT_ONE")); + std::unique_ptr parDep( + paramDepFactory.createParameterDependence("CONSTANT_ONE")); FlowType ft; - typename FlowType::Params fp = ft.makeParams( - u, - d_c.data(), - h, - wenoDerivatives.data(), - &weno, - &stencilMemory, - strideCell, - nComp, - nCol - ); + typename FlowType::Params fp = + ft.makeParams(u, d_c.data(), h, wenoDerivatives.data(), &weno, &stencilMemory, strideCell, nComp, nCol); // Obtain sparsity pattern const unsigned int lowerBandwidth = std::max(weno.lowerBandwidth() + 1u, 1u); @@ -607,11 +626,13 @@ void testBulkJacobianSparseBandedWeno(int wenoOrder, bool forwardFlow) std::vector res(nDof, 0.0); // Fill state vector with some values - cadet::test::util::populate(y.data() + nComp, [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof - nComp); + cadet::test::util::populate( + y.data() + nComp, [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof - nComp); // Populate sparse matrix cadet::linalg::CompressedSparseMatrix sparseMat(pattern); - FlowType::template residualWithJacobian(y.data(), nullptr, res.data(), sparseMat.row(0), fp); + FlowType::template residualWithJacobian(y.data(), nullptr, res.data(), + sparseMat.row(0), fp); // Populate dense matrix cadet::linalg::BandMatrix bandMat; @@ -620,7 +641,8 @@ void testBulkJacobianSparseBandedWeno(int wenoOrder, bool forwardFlow) else bandMat.resize(nComp * nCol, upperBandwidth * strideCell, lowerBandwidth * strideCell); - FlowType::template residualWithJacobian(y.data(), nullptr, res.data(), bandMat.row(0), fp); + FlowType::template residualWithJacobian(y.data(), nullptr, res.data(), + bandMat.row(0), fp); for (int col = 0; col < bandMat.rows(); ++col) { @@ -630,7 +652,8 @@ void testBulkJacobianSparseBandedWeno(int wenoOrder, bool forwardFlow) CAPTURE(col); const int diagonal = col - row; - if ((diagonal <= static_cast(bandMat.upperBandwidth())) && (-diagonal <= static_cast(bandMat.lowerBandwidth()))) + if ((diagonal <= static_cast(bandMat.upperBandwidth())) && + (-diagonal <= static_cast(bandMat.lowerBandwidth()))) CHECK(bandMat(row, diagonal) == sparseMat(row, col)); else CHECK(0.0 == sparseMat(row, col)); @@ -646,19 +669,22 @@ TEST_CASE("AxialConvectionDispersionOperator residual forward vs backward flow", testResidualBulkWenoForwardBackward(i); } -TEST_CASE("AxialConvectionDispersionOperator time derivative Jacobian vs FD", "[Operator],[AxialFlow],[Residual],[Jacobian]") +TEST_CASE("AxialConvectionDispersionOperator time derivative Jacobian vs FD", + "[Operator],[AxialFlow],[Residual],[Jacobian]") { testTimeDerivativeBulkJacobianFD(1e-6, 0.0, 1e-5); } -TEST_CASE("AxialConvectionDispersionOperator Jacobian forward vs backward flow", "[Operator],[AxialFlow],[Residual],[Jacobian],[AD]") +TEST_CASE("AxialConvectionDispersionOperator Jacobian forward vs backward flow", + "[Operator],[AxialFlow],[Residual],[Jacobian],[AD]") { // Test all WENO orders for (unsigned int i = 1; i <= cadet::Weno::maxOrder(); ++i) testBulkJacobianWenoForwardBackward(i); } -TEST_CASE("AxialConvectionDispersionKernel Jacobian sparsity pattern vs FD", "[Operator],[AxialFlow],[Residual],[Jacobian],[SparseMatrix]") +TEST_CASE("AxialConvectionDispersionKernel Jacobian sparsity pattern vs FD", + "[Operator],[AxialFlow],[Residual],[Jacobian],[SparseMatrix]") { SECTION("Forward flow") { @@ -674,7 +700,8 @@ TEST_CASE("AxialConvectionDispersionKernel Jacobian sparsity pattern vs FD", "[O } } -TEST_CASE("AxialConvectionDispersionKernel Jacobian sparse vs banded", "[Operator],[AxialFlow],[Jacobian],[SparseMatrix]") +TEST_CASE("AxialConvectionDispersionKernel Jacobian sparse vs banded", + "[Operator],[AxialFlow],[Jacobian],[SparseMatrix]") { SECTION("Forward flow") { @@ -690,7 +717,6 @@ TEST_CASE("AxialConvectionDispersionKernel Jacobian sparse vs banded", "[Operato } } - TEST_CASE("RadialConvectionDispersionOperator residual forward vs backward flow", "[Operator],[RadialFlow],[Residual]") { // Test all WENO orders @@ -698,19 +724,22 @@ TEST_CASE("RadialConvectionDispersionOperator residual forward vs backward flow" testResidualBulkWenoForwardBackward(i); } -TEST_CASE("RadialConvectionDispersionOperator time derivative Jacobian vs FD", "[Operator],[RadialFlow],[Residual],[Jacobian]") +TEST_CASE("RadialConvectionDispersionOperator time derivative Jacobian vs FD", + "[Operator],[RadialFlow],[Residual],[Jacobian]") { testTimeDerivativeBulkJacobianFD(1e-6, 0.0, 1e-5); } -TEST_CASE("RadialConvectionDispersionOperator Jacobian forward vs backward flow", "[Operator],[RadialFlow],[Residual],[Jacobian],[AD]") +TEST_CASE("RadialConvectionDispersionOperator Jacobian forward vs backward flow", + "[Operator],[RadialFlow],[Residual],[Jacobian],[AD]") { // Test all WENO orders for (unsigned int i = 1; i <= 1; ++i) testBulkJacobianWenoForwardBackward(i); } -TEST_CASE("RadialConvectionDispersionKernel Jacobian sparsity pattern vs FD", "[Operator],[RadialFlow],[Residual],[Jacobian],[SparseMatrix]") +TEST_CASE("RadialConvectionDispersionKernel Jacobian sparsity pattern vs FD", + "[Operator],[RadialFlow],[Residual],[Jacobian],[SparseMatrix]") { SECTION("Forward flow") { @@ -726,7 +755,8 @@ TEST_CASE("RadialConvectionDispersionKernel Jacobian sparsity pattern vs FD", "[ } } -TEST_CASE("RadialConvectionDispersionKernel Jacobian sparse vs banded", "[Operator],[RadialFlow],[Jacobian],[SparseMatrix]") +TEST_CASE("RadialConvectionDispersionKernel Jacobian sparse vs banded", + "[Operator],[RadialFlow],[Jacobian],[SparseMatrix]") { SECTION("Forward flow") { diff --git a/test/DenseMatrix.cpp b/test/DenseMatrix.cpp index 5ed3883a6..050df94d8 100644 --- a/test/DenseMatrix.cpp +++ b/test/DenseMatrix.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -76,8 +76,7 @@ inline std::vector randomVector(unsigned int n) * @tparam Matrix_t Type of banded matrix to create * @return Banded matrix of given shape */ -template -Matrix_t createBandMatrix(unsigned int rows, unsigned int lower, unsigned int upper) +template Matrix_t createBandMatrix(unsigned int rows, unsigned int lower, unsigned int upper) { Matrix_t bm; bm.resize(rows, lower, upper); @@ -107,9 +106,11 @@ Matrix_t createBandMatrix(unsigned int rows, unsigned int lower, unsigned int up * @tparam Matrix_t Type of banded matrix */ template -void testSubMatrixMultiply(const Matrix_t& bm, int startRow, int startDiag, int numRows, int numCols, const std::vector& ref) +void testSubMatrixMultiply(const Matrix_t& bm, int startRow, int startDiag, int numRows, int numCols, + const std::vector& ref) { - SECTION("From row " + std::to_string(startRow) + ", diagonal " + std::to_string(startDiag) + " extract " + std::to_string(numRows) + "x" + std::to_string(numCols) + " matrix") + SECTION("From row " + std::to_string(startRow) + ", diagonal " + std::to_string(startDiag) + " extract " + + std::to_string(numRows) + "x" + std::to_string(numCols) + " matrix") { cadet::linalg::DenseMatrix dm; dm.resize(numRows, numCols); @@ -122,17 +123,16 @@ void testSubMatrixMultiply(const Matrix_t& bm, int startRow, int startDiag, int * @brief Calls testSubMatrixMultiply() with various sizes and shifts * @tparam Matrix_t Type of banded matrix */ -template -void testDenseSubmatrixFromBanded() +template void testDenseSubmatrixFromBanded() { const Matrix_t bm = createBandMatrix(8, 2, 3); - testSubMatrixMultiply(bm, 0, 0, 4, 4, {1, 2, 3, 4,5, 6, 7, 8,10, 11, 12, 13,0, 16, 17, 18}); - testSubMatrixMultiply(bm, 4, 0, 4, 4, {24, 25, 26, 27,29, 30, 31, 32,33, 34, 35, 36,0, 37, 38, 39}); - testSubMatrixMultiply(bm, 2, 2, 4, 4, {14, 15, 0, 0,19, 20, 21, 0,24, 25, 26, 27,29, 30, 31, 32}); - testSubMatrixMultiply(bm, 2, -1, 4, 4, {11, 12, 13, 14,16, 17, 18, 19,0, 22, 23, 24,0, 0, 28, 29}); - testSubMatrixMultiply(bm, 1, 0, 4, 4, {6, 7, 8, 9,11, 12, 13, 14,16, 17, 18, 19,0, 22, 23, 24}); - testSubMatrixMultiply(bm, 0, 4, 4, 4, {0, 0, 0, 0,9, 0, 0, 0,14, 15, 0, 0,19, 20, 21, 0}); + testSubMatrixMultiply(bm, 0, 0, 4, 4, {1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 0, 16, 17, 18}); + testSubMatrixMultiply(bm, 4, 0, 4, 4, {24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 0, 37, 38, 39}); + testSubMatrixMultiply(bm, 2, 2, 4, 4, {14, 15, 0, 0, 19, 20, 21, 0, 24, 25, 26, 27, 29, 30, 31, 32}); + testSubMatrixMultiply(bm, 2, -1, 4, 4, {11, 12, 13, 14, 16, 17, 18, 19, 0, 22, 23, 24, 0, 0, 28, 29}); + testSubMatrixMultiply(bm, 1, 0, 4, 4, {6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 18, 19, 0, 22, 23, 24}); + testSubMatrixMultiply(bm, 0, 4, 4, 4, {0, 0, 0, 0, 9, 0, 0, 0, 14, 15, 0, 0, 19, 20, 21, 0}); testSubMatrixMultiply(bm, 2, 1, 1, 2, {13, 14}); testSubMatrixMultiply(bm, 2, -1, 1, 3, {11, 12, 13}); } @@ -154,7 +154,7 @@ TEST_CASE("DenseMatrix LU solves", "[DenseMatrix],[LinAlg]") // Probability of obtaining a non-invertible random matrix is 0 const DenseMatrix dm = randomMatrix(8, 8); DenseMatrix fdm = dm; - + REQUIRE(fdm.factorize()); // Prepare some right hand side @@ -176,7 +176,7 @@ TEST_CASE("DenseMatrix QR solves", "[DenseMatrix],[LinAlg]") // Probability of obtaining a non-invertible random matrix is 0 const DenseMatrix dm = randomMatrix(8, 8); DenseMatrix fdm = dm; - + std::vector workingMemory(fdm.robustWorkspaceSize(), 0.0); REQUIRE(fdm.robustFactorize(workingMemory.data())); diff --git a/test/Dummies.hpp b/test/Dummies.hpp index 6e92e7c69..d608b1650 100644 --- a/test/Dummies.hpp +++ b/test/Dummies.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -18,76 +18,190 @@ namespace { - class DummyConfigHelper : public cadet::IConfigHelper - { - public: - - DummyConfigHelper() { } - - virtual cadet::IInletProfile* createInletProfile(const std::string& type) const { return nullptr; } - virtual cadet::model::IBindingModel* createBindingModel(const std::string& name) const { return nullptr; } - virtual bool isValidBindingModel(const std::string& name) const { return false; } - virtual cadet::IExternalFunction* createExternalFunction(const std::string& type) const { return nullptr; } - virtual cadet::model::IDynamicReactionModel* createDynamicReactionModel(const std::string& name) const { return nullptr; } - virtual bool isValidDynamicReactionModel(const std::string& name) const { return false; } - virtual cadet::model::IParameterStateDependence* createParameterStateDependence(const std::string& name) const { return nullptr; } - virtual bool isValidParameterStateDependence(const std::string& name) const { return false; } - virtual cadet::model::IParameterParameterDependence* createParameterParameterDependence(const std::string& name) const { return nullptr; } - virtual bool isValidParameterParameterDependence(const std::string& name) const { return false; } - }; +class DummyConfigHelper : public cadet::IConfigHelper +{ +public: + DummyConfigHelper() + { + } + + virtual cadet::IInletProfile* createInletProfile(const std::string& type) const + { + return nullptr; + } + virtual cadet::model::IBindingModel* createBindingModel(const std::string& name) const + { + return nullptr; + } + virtual bool isValidBindingModel(const std::string& name) const + { + return false; + } + virtual cadet::IExternalFunction* createExternalFunction(const std::string& type) const + { + return nullptr; + } + virtual cadet::model::IDynamicReactionModel* createDynamicReactionModel(const std::string& name) const + { + return nullptr; + } + virtual bool isValidDynamicReactionModel(const std::string& name) const + { + return false; + } + virtual cadet::model::IParameterStateDependence* createParameterStateDependence(const std::string& name) const + { + return nullptr; + } + virtual bool isValidParameterStateDependence(const std::string& name) const + { + return false; + } + virtual cadet::model::IParameterParameterDependence* createParameterParameterDependence( + const std::string& name) const + { + return nullptr; + } + virtual bool isValidParameterParameterDependence(const std::string& name) const + { + return false; + } +}; - class DummyModel : public cadet::IModel +class DummyModel : public cadet::IModel +{ +public: + DummyModel() { - public: - DummyModel() { } - virtual ~DummyModel() CADET_NOEXCEPT { } + } + virtual ~DummyModel() CADET_NOEXCEPT + { + } - virtual cadet::UnitOpIdx unitOperationId() const CADET_NOEXCEPT { return 0; }; - virtual const char* unitOperationName() const CADET_NOEXCEPT { return "DUMMY"; } + virtual cadet::UnitOpIdx unitOperationId() const CADET_NOEXCEPT + { + return 0; + }; + virtual const char* unitOperationName() const CADET_NOEXCEPT + { + return "DUMMY"; + } - virtual bool setParameter(const cadet::ParameterId& pId, int value) { return false; } - virtual bool setParameter(const cadet::ParameterId& pId, double value) { return false; } - virtual bool setParameter(const cadet::ParameterId& pId, bool value) { return false; } + virtual bool setParameter(const cadet::ParameterId& pId, int value) + { + return false; + } + virtual bool setParameter(const cadet::ParameterId& pId, double value) + { + return false; + } + virtual bool setParameter(const cadet::ParameterId& pId, bool value) + { + return false; + } - virtual bool hasParameter(const cadet::ParameterId& pId) const { return false; } + virtual bool hasParameter(const cadet::ParameterId& pId) const + { + return false; + } - virtual std::unordered_map getAllParameterValues() const { return std::unordered_map(); } + virtual std::unordered_map getAllParameterValues() const + { + return std::unordered_map(); + } - virtual double getParameterDouble(const cadet::ParameterId& pId) const { return 0.0; } + virtual double getParameterDouble(const cadet::ParameterId& pId) const + { + return 0.0; + } - virtual void useAnalyticJacobian(const bool analyticJac) { } + virtual void useAnalyticJacobian(const bool analyticJac) + { + } #ifdef CADET_BENCHMARK_MODE - virtual std::vector benchmarkTimings() const { return std::vector(0); } - virtual char const* const* benchmarkDescriptions() const { return nullptr; } + virtual std::vector benchmarkTimings() const + { + return std::vector(0); + } + virtual char const* const* benchmarkDescriptions() const + { + return nullptr; + } #endif - }; +}; - class DummyParameterProvider : public cadet::IParameterProvider +class DummyParameterProvider : public cadet::IParameterProvider +{ +public: + DummyParameterProvider() { - public: - - DummyParameterProvider() { } - virtual ~DummyParameterProvider() CADET_NOEXCEPT { } + } + virtual ~DummyParameterProvider() CADET_NOEXCEPT + { + } - virtual double getDouble(const std::string& paramName) { return 0.0; } - virtual int getInt(const std::string& paramName) { return 0; } - virtual uint64_t getUint64(const std::string& paramName) { return 0; } - virtual bool getBool(const std::string& paramName) { return false; } - virtual std::string getString(const std::string& paramName) { return ""; } + virtual double getDouble(const std::string& paramName) + { + return 0.0; + } + virtual int getInt(const std::string& paramName) + { + return 0; + } + virtual uint64_t getUint64(const std::string& paramName) + { + return 0; + } + virtual bool getBool(const std::string& paramName) + { + return false; + } + virtual std::string getString(const std::string& paramName) + { + return ""; + } - virtual std::vector getDoubleArray(const std::string& paramName) { return std::vector(); } - virtual std::vector getIntArray(const std::string& paramName) { return std::vector(); } - virtual std::vector getUint64Array(const std::string& paramName) { return std::vector(); } - virtual std::vector getBoolArray(const std::string& paramName) { return std::vector(); } - virtual std::vector getStringArray(const std::string& paramName) { return std::vector(); } + virtual std::vector getDoubleArray(const std::string& paramName) + { + return std::vector(); + } + virtual std::vector getIntArray(const std::string& paramName) + { + return std::vector(); + } + virtual std::vector getUint64Array(const std::string& paramName) + { + return std::vector(); + } + virtual std::vector getBoolArray(const std::string& paramName) + { + return std::vector(); + } + virtual std::vector getStringArray(const std::string& paramName) + { + return std::vector(); + } - virtual bool exists(const std::string& paramName) { return false; } - virtual bool isArray(const std::string& paramName) { return false; } + virtual bool exists(const std::string& paramName) + { + return false; + } + virtual bool isArray(const std::string& paramName) + { + return false; + } - virtual std::size_t numElements(const std::string& paramName) { return 0; } + virtual std::size_t numElements(const std::string& paramName) + { + return 0; + } - virtual void pushScope(const std::string& scope) { } - virtual void popScope() { } - }; -} + virtual void pushScope(const std::string& scope) + { + } + virtual void popScope() + { + } +}; +} // namespace diff --git a/test/GeneralRateModel.cpp b/test/GeneralRateModel.cpp index ff3acc230..9c824eb8b 100644 --- a/test/GeneralRateModel.cpp +++ b/test/GeneralRateModel.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -34,17 +34,23 @@ TEST_CASE("GRM LWE forward vs backward flow", "[GRM],[FV],[Simulation],[CI]") TEST_CASE("GRM linear pulse vs analytic solution", "[GRM],[FV],[Simulation],[Analytic],[CI]") { cadet::test::column::FVparams disc(512); - cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", true, true, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", true, false, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", false, true, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", false, false, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", true, true, disc, + 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", true, false, disc, + 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", false, true, disc, + 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", false, false, + disc, 6e-5, 1e-7); } TEST_CASE("GRM non-binding linear pulse vs analytic solution", "[GRM],[FV],[Simulation],[Analytic],[NonBinding],[CI]") { cadet::test::column::FVparams disc(512); - cadet::test::column::testAnalyticNonBindingBenchmark("GENERAL_RATE_MODEL", "/data/grm-nonBinding.data", true, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticNonBindingBenchmark("GENERAL_RATE_MODEL", "/data/grm-nonBinding.data", false, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticNonBindingBenchmark("GENERAL_RATE_MODEL", "/data/grm-nonBinding.data", true, disc, + 6e-5, 1e-7); + cadet::test::column::testAnalyticNonBindingBenchmark("GENERAL_RATE_MODEL", "/data/grm-nonBinding.data", false, disc, + 6e-5, 1e-7); } TEST_CASE("GRM Jacobian forward vs backward flow", "[GRM],[FV],[UnitOp],[Residual],[Jacobian],[AD],[CI]") @@ -59,50 +65,58 @@ TEST_CASE("GRM Jacobian forward vs backward flow", "[GRM],[FV],[UnitOp],[Residua } } -TEST_CASE("GRM numerical Benchmark with parameter sensitivities for linear case", "[GRM],[FV],[Simulation],[Reference],[Sensitivity],[dddff]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE("GRM numerical Benchmark with parameter sensitivities for linear case", + "[GRM],[FV],[Simulation],[Reference],[Sensitivity],[dddff]") // todo CI flag: currently only runs locally but + // fails on server { const std::string& modelFilePath = std::string("/data/model_GRM_dynLin_1comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_GRM_dynLin_1comp_sensbenchmark1_FV_Z32parZ4.h5"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1e-4, 1e-4, 1e-4, 1e-4 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1e-4, 1e-4, 1e-4, 1e-4}; cadet::test::column::FVparams disc(32, 4); cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, true); } -TEST_CASE("GRM numerical Benchmark with parameter sensitivities for SMA LWE case", "[GRM],[FV],[Simulation],[Reference],[Sensitivity],[dddff]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE("GRM numerical Benchmark with parameter sensitivities for SMA LWE case", + "[GRM],[FV],[Simulation],[Reference],[Sensitivity],[dddff]") // todo CI flag: currently only runs locally but + // fails on server { const std::string& modelFilePath = std::string("/data/model_GRM_reqSMA_4comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_GRM_reqSMA_4comp_sensbenchmark1_FV_Z16parZ2.h5"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1e-4, 1e-4, 1e-4, 1e-4 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1e-4, 1e-4, 1e-4, 1e-4}; cadet::test::column::FVparams disc(16, 2); cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "000", absTol, relTol, disc, true); } -TEST_CASE("GRM numerical EOC Benchmark with parameter sensitivities for linear case", "[GRM],[FV],[releaseCI],[EOC],[EOC_GRM_FV],[dddff]") +TEST_CASE("GRM numerical EOC Benchmark with parameter sensitivities for linear case", + "[GRM],[FV],[releaseCI],[EOC],[EOC_GRM_FV],[dddff]") { const std::string& modelFilePath = std::string("/data/model_GRM_dynLin_1comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_GRM_dynLin_1comp_sensbenchmark1_FV_Z1024parZ128.h5"); const std::string& convFilePath = std::string("/data/convergence_GRM_dynLin_1comp_sensbenchmark1.json"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1e-4, 1e-4, 1e-4, 1e-4 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1e-4, 1e-4, 1e-4, 1e-4}; cadet::test::column::FVparams disc(8, 1); - cadet::test::column::testEOCReferenceBenchmark(modelFilePath, refFilePath, convFilePath, "001", absTol, relTol, 3, disc, true); + cadet::test::column::testEOCReferenceBenchmark(modelFilePath, refFilePath, convFilePath, "001", absTol, relTol, 3, + disc, true); } -TEST_CASE("GRM numerical EOC Benchmark with parameter sensitivities for SMA LWE case", "[GRM],[FV],[releaseCI],[EOC],[EOC_GRM_FV],[dddff]") +TEST_CASE("GRM numerical EOC Benchmark with parameter sensitivities for SMA LWE case", + "[GRM],[FV],[releaseCI],[EOC],[EOC_GRM_FV],[dddff]") { const std::string& modelFilePath = std::string("/data/model_GRM_reqSMA_4comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_GRM_reqSMA_4comp_sensbenchmark1_FV_Z512parZ64.h5"); const std::string& convFilePath = std::string("/data/convergence_GRM_reqSMA_4comp_sensbenchmark1.json"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1e-4, 1e-4, 1e-4, 1e-4 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1e-4, 1e-4, 1e-4, 1e-4}; cadet::test::column::FVparams disc(8, 1); - cadet::test::column::testEOCReferenceBenchmark(modelFilePath, refFilePath, convFilePath, "000", absTol, relTol, 2, disc, true); + cadet::test::column::testEOCReferenceBenchmark(modelFilePath, refFilePath, convFilePath, "000", absTol, relTol, 2, + disc, true); } TEST_CASE("GRM time derivative Jacobian vs FD", "[GRM],[FV],[UnitOp],[Residual],[Jacobian],[CI],[FD]") @@ -115,17 +129,20 @@ TEST_CASE("GRM rapid-equilibrium binding flux Jacobian vs FD", "[GRM],[FV],[Unit cadet::test::column::testArrowHeadJacobianFD("GENERAL_RATE_MODEL", false, 1e-6, 2e-9); } -TEST_CASE("GRM rapid-equilibrium binding with surf diff par dep flux Jacobian vs FD", "[GRM],[FV],[UnitOp],[Residual],[Jacobian],[ParameterDependence],[CI],[FD]") +TEST_CASE("GRM rapid-equilibrium binding with surf diff par dep flux Jacobian vs FD", + "[GRM],[FV],[UnitOp],[Residual],[Jacobian],[ParameterDependence],[CI],[FD]") { cadet::test::column::testArrowHeadJacobianFDVariableParSurfDiff("GENERAL_RATE_MODEL", 1e-6, 5e-9); } -TEST_CASE("GRM dynamic binding with surf diff par dep Jacobian vs AD", "[GRM],[FV],[UnitOp],[Residual],[Jacobian],[ParameterDependence],[CI]") +TEST_CASE("GRM dynamic binding with surf diff par dep Jacobian vs AD", + "[GRM],[FV],[UnitOp],[Residual],[Jacobian],[ParameterDependence],[CI]") { cadet::test::column::testJacobianADVariableParSurfDiff("GENERAL_RATE_MODEL", "FV", true); } -TEST_CASE("GRM rapid-equilibrium binding with surf diff par dep Jacobian vs AD", "[GRM],[FV],[UnitOp],[Residual],[Jacobian],[ParameterDependence],[CI]") +TEST_CASE("GRM rapid-equilibrium binding with surf diff par dep Jacobian vs AD", + "[GRM],[FV],[UnitOp],[Residual],[Jacobian],[ParameterDependence],[CI]") { cadet::test::column::testJacobianADVariableParSurfDiff("GENERAL_RATE_MODEL", "FV", false); } @@ -142,7 +159,8 @@ TEST_CASE("GRM sensitivity Jacobians", "[GRM],[FV],[UnitOp],[Sensitivity],[CI]") cadet::test::column::testFwdSensJacobians(jpp, 1e-4, 6e-7); } -//TEST_CASE("GRM forward sensitivity vs FD", "[GRM],[FV],[Sensitivity],[Simulation],[failedFDtestGRM]") // todo fix. fails for SMA_KA for both binding modes +// TEST_CASE("GRM forward sensitivity vs FD", "[GRM],[FV],[Sensitivity],[Simulation],[failedFDtestGRM]") // todo fix. +// fails for SMA_KA for both binding modes //{ // // todo comment // // Relative error is checked first, we use high absolute error for letting @@ -153,49 +171,56 @@ TEST_CASE("GRM sensitivity Jacobians", "[GRM],[FV],[UnitOp],[Sensitivity],[CI]") // const double relTols[] = {5e-3, 7e-2, 8e-2, 1e-4}; // const double passRatio[] = {0.95, 0.9, 0.91, 0.83}; // cadet::test::column::testFwdSensSolutionFD("GENERAL_RATE_MODEL", false, fdStepSize, absTols, relTols, passRatio); -//} +// } // -//TEST_CASE("GRM forward sensitivity forward vs backward flow", "[GRM],[FV],[Sensitivity],[Simulation],[fixGRM]") // todo fix. fails for COL_DISPERION for both binding modes +// TEST_CASE("GRM forward sensitivity forward vs backward flow", "[GRM],[FV],[Sensitivity],[Simulation],[fixGRM]") // +// todo fix. fails for COL_DISPERION for both binding modes //{ // const double absTols[] = {4e-5, 1e-11, 1e-11, 8e-9}; // const double relTols[] = {6e-9, 5e-8, 5e-6, 5e-10}; // const double passRatio[] = {0.99, 0.95, 0.98, 0.98}; // cadet::test::column::testFwdSensSolutionForwardBackward("GENERAL_RATE_MODEL", absTols, relTols, passRatio); -//} +// } TEST_CASE("GRM consistent initialization with linear binding", "[GRM],[FV],[ConsistentInit],[CI]") { cadet::test::column::testConsistentInitializationLinearBinding("GENERAL_RATE_MODEL", "FV", 1e-12, 1e-12); } -//TEST_CASE("GRM consistent initialization with SMA binding", "[GRM],[FV],[ConsistentInit],[fixGRM]") // todo fix +// TEST_CASE("GRM consistent initialization with SMA binding", "[GRM],[FV],[ConsistentInit],[fixGRM]") // todo fix //{ // std::vector y(4 + 4 * 16 + 16 * 4 * (4 + 4) + 4 * 16, 0.0); //// Optimal values: -//// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, +//// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, //// 1.0, 1.8, 1.5, 1.6, 856.173, 64.457, 5.73227, 2.85286}; -// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, +// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, // 1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0}; -// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 16); -// cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 16, 4 * 16 / 2); -// cadet::test::util::populate(y.data() + 4 + 4 * 16 + 16 * 4 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); +// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 +//* 16); cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 16, 4 * 16 / 2); +// cadet::test::util::populate(y.data() + 4 + 4 * 16 + 16 * 4 * (4 + 4), [](unsigned int idx) { return +// std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); // // cadet::test::column::testConsistentInitializationSMABinding("GENERAL_RATE_MODEL", y.data(), 1e-14, 1e-5); //} -TEST_CASE("GRM consistent sensitivity initialization with linear binding", "[GRM],[FV],[ConsistentInit],[Sensitivity],[CI]") +TEST_CASE("GRM consistent sensitivity initialization with linear binding", + "[GRM],[FV],[ConsistentInit],[Sensitivity],[CI]") { // Fill state vector with given initial values const unsigned int numDofs = 4 + 4 * 16 + 16 * 4 * (4 + 4) + 4 * 16; std::vector y(numDofs, 0.0); std::vector yDot(numDofs, 0.0); - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL", "FV", y.data(), yDot.data(), true, 1e-14); + cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL", "FV", y.data(), yDot.data(), + true, 1e-14); } -TEST_CASE("GRM consistent sensitivity initialization with SMA binding", "[GRM],[FV],[ConsistentInit],[Sensitivity],[CI]") +TEST_CASE("GRM consistent sensitivity initialization with SMA binding", + "[GRM],[FV],[ConsistentInit],[Sensitivity],[CI]") { // Fill state vector with given initial values const unsigned int numDofs = 4 + 4 * 16 + 16 * 4 * (4 + 4) + 4 * 16; @@ -203,13 +228,18 @@ TEST_CASE("GRM consistent sensitivity initialization with SMA binding", "[GRM],[ std::vector yDot(numDofs, 0.0); const double bindingCell[] = {1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0}; - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 16); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 16); cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 8, 4 * 16); - cadet::test::util::populate(y.data() + 4 + 4 * 16 + 16 * 4 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); + cadet::test::util::populate( + y.data() + 4 + 4 * 16 + 16 * 4 * (4 + 4), + [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL", "FV", y.data(), yDot.data(), false, 1e-9); + cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL", "FV", y.data(), yDot.data(), + false, 1e-9); } TEST_CASE("GRM inlet DOF Jacobian", "[GRM],[FV],[UnitOp],[Jacobian],[Inlet],[CI]") @@ -239,7 +269,8 @@ TEST_CASE("GRM LWE separate identical particle types match", "[GRM],[FV],[Simula cadet::test::particle::testSeparateIdenticalParticleTypes("GENERAL_RATE_MODEL", "FV", 1e-15, 1e-15); } -TEST_CASE("GRM linear binding single particle matches particle distribution", "[GRM],[FV],[Simulation],[ParticleType],[CI]") +TEST_CASE("GRM linear binding single particle matches particle distribution", + "[GRM],[FV],[Simulation],[ParticleType],[CI]") { cadet::test::particle::testLinearMixedParticleTypes("GENERAL_RATE_MODEL", "FV", 5e-8, 5e-5); } @@ -249,22 +280,26 @@ TEST_CASE("GRM multiple particle types Jacobian analytic vs AD", "[GRM],[FV],[Ja cadet::test::particle::testJacobianMixedParticleTypes("GENERAL_RATE_MODEL", "FV"); } -TEST_CASE("GRM multiple particle types time derivative Jacobian vs FD", "[GRM],[FV],[UnitOp],[Residual],[Jacobian],[ParticleType],[CI]") +TEST_CASE("GRM multiple particle types time derivative Jacobian vs FD", + "[GRM],[FV],[UnitOp],[Residual],[Jacobian],[ParticleType],[CI]") { cadet::test::particle::testTimeDerivativeJacobianMixedParticleTypesFD("GENERAL_RATE_MODEL", "FV", 1e-6, 0.0, 9e-4); } -TEST_CASE("GRM multiple spatially dependent particle types Jacobian analytic vs AD", "[GRM],[FV],[Jacobian],[AD],[ParticleType],[CI]") +TEST_CASE("GRM multiple spatially dependent particle types Jacobian analytic vs AD", + "[GRM],[FV],[Jacobian],[AD],[ParticleType],[CI]") { cadet::test::particle::testJacobianSpatiallyMixedParticleTypes("GENERAL_RATE_MODEL", "FV"); } -TEST_CASE("GRM linear binding single particle matches spatially dependent particle distribution", "[GRM],[FV],[Simulation],[ParticleType],[CI]") +TEST_CASE("GRM linear binding single particle matches spatially dependent particle distribution", + "[GRM],[FV],[Simulation],[ParticleType],[CI]") { cadet::test::particle::testLinearSpatiallyMixedParticleTypes("GENERAL_RATE_MODEL", "FV", 5e-8, 5e-5); } -TEST_CASE("GRM multiple spatially dependent particle types flux Jacobian vs FD", "[GRM],[FV],[UnitOp],[Residual],[Jacobian],[ParticleType],[CI],[FD]") +TEST_CASE("GRM multiple spatially dependent particle types flux Jacobian vs FD", + "[GRM],[FV],[UnitOp],[Residual],[Jacobian],[ParticleType],[CI],[FD]") { cadet::test::particle::testArrowHeadJacobianSpatiallyMixedParticleTypes("GENERAL_RATE_MODEL", 1e-6, 1e-8, 1e-5); } @@ -289,34 +324,45 @@ TEST_CASE("GRM dynamic reactions Jacobian vs AD bulk and particle", "[GRM],[FV], cadet::test::reaction::testUnitJacobianDynamicReactionsAD("GENERAL_RATE_MODEL", "FV", true, true, false); } -TEST_CASE("GRM dynamic reactions Jacobian vs AD bulk and modified particle", "[GRM],[FV],[Jacobian],[AD],[ReactionModel],[CI]") +TEST_CASE("GRM dynamic reactions Jacobian vs AD bulk and modified particle", + "[GRM],[FV],[Jacobian],[AD],[ReactionModel],[CI]") { cadet::test::reaction::testUnitJacobianDynamicReactionsAD("GENERAL_RATE_MODEL", "FV", true, true, true); } -TEST_CASE("GRM dynamic reactions time derivative Jacobian vs FD bulk", "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("GRM dynamic reactions time derivative Jacobian vs FD bulk", + "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "FV", true, false, false, 1e-6, 1e-14, 9e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "FV", true, false, false, + 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM dynamic reactions time derivative Jacobian vs FD particle", "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("GRM dynamic reactions time derivative Jacobian vs FD particle", + "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "FV", false, true, false, 1e-6, 1e-14, 9e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "FV", false, true, false, + 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM dynamic reactions time derivative Jacobian vs FD modified particle", "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("GRM dynamic reactions time derivative Jacobian vs FD modified particle", + "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "FV", false, true, true, 1e-6, 1e-14, 9e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "FV", false, true, true, + 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM dynamic reactions time derivative Jacobian vs FD bulk and particle", "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("GRM dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "FV", true, true, false, 1e-6, 1e-14, 9e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "FV", true, true, false, + 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("GRM dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "FV", true, true, true, 1e-6, 1e-14, 9e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "FV", true, true, true, + 1e-6, 1e-14, 9e-4); } inline cadet::JsonParameterProvider createColumnWithTwoCompLinearBindingThreeParticleTypes() @@ -330,61 +376,71 @@ inline cadet::JsonParameterProvider createColumnWithTwoCompLinearBindingThreePar return jpp; } -TEST_CASE("GRM multi particle types dynamic reactions Jacobian vs AD bulk", "[GRM],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("GRM multi particle types dynamic reactions Jacobian vs AD bulk", + "[GRM],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, false, false); } -TEST_CASE("GRM multi particle types dynamic reactions Jacobian vs AD particle", "[GRM],[Jacobian],[FV],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("GRM multi particle types dynamic reactions Jacobian vs AD particle", + "[GRM],[Jacobian],[FV],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, false, true, false); } -TEST_CASE("GRM multi particle types dynamic reactions Jacobian vs AD modified particle", "[GRM],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("GRM multi particle types dynamic reactions Jacobian vs AD modified particle", + "[GRM],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, false, true, true); } -TEST_CASE("GRM multi particle types dynamic reactions Jacobian vs AD bulk and particle", "[GRM],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("GRM multi particle types dynamic reactions Jacobian vs AD bulk and particle", + "[GRM],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, true, false); } -TEST_CASE("GRM multi particle types dynamic reactions Jacobian vs AD bulk and modified particle", "[GRM],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("GRM multi particle types dynamic reactions Jacobian vs AD bulk and modified particle", + "[GRM],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, true, true); } -TEST_CASE("GRM multi particle types dynamic reactions time derivative Jacobian vs FD bulk", "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") +TEST_CASE("GRM multi particle types dynamic reactions time derivative Jacobian vs FD bulk", + "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, false, false, 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM multi particle types dynamic reactions time derivative Jacobian vs FD particle", "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") +TEST_CASE("GRM multi particle types dynamic reactions time derivative Jacobian vs FD particle", + "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, false, 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") +TEST_CASE("GRM multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", + "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, true, 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") +TEST_CASE("GRM multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, false, 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") +TEST_CASE("GRM multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[GRM],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, true, 1e-6, 1e-14, 9e-4); diff --git a/test/GeneralRateModel2D.cpp b/test/GeneralRateModel2D.cpp index c5ceae120..fe14389d7 100644 --- a/test/GeneralRateModel2D.cpp +++ b/test/GeneralRateModel2D.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -43,7 +43,8 @@ TEST_CASE("GRM2D LWE forward vs backward flow", "[GRM2D],[Simulation],[fixGRM2D] } } -TEST_CASE("GRM2D Jacobian forward vs backward flow", "[GRM2D],[UnitOp],[Residual],[Jacobian],[fixGRM2D]") // todo fix. off by some tolerance +TEST_CASE("GRM2D Jacobian forward vs backward flow", + "[GRM2D],[UnitOp],[Residual],[Jacobian],[fixGRM2D]") // todo fix. off by some tolerance { // Test all WENO orders for (unsigned int i = 1; i <= cadet::Weno::maxOrder(); ++i) @@ -72,7 +73,8 @@ TEST_CASE("GRM2D sensitivity Jacobians", "[GRM2D],[UnitOp],[Sensitivity],[CIgrm2 cadet::test::column::testFwdSensJacobians(jpp, 1e-4, 6e-7); } -TEST_CASE("GRM2D forward sensitivity vs FD", "[GRM2D],[Sensitivity],[Simulation],[failedFDtestGRM2D]") // todo fix. off by a bigger tolerance +TEST_CASE("GRM2D forward sensitivity vs FD", + "[GRM2D],[Sensitivity],[Simulation],[failedFDtestGRM2D]") // todo fix. off by a bigger tolerance { // Relative error is checked first, we use high absolute error for letting // some points that are far off pass the error test, too. This is required @@ -81,10 +83,12 @@ TEST_CASE("GRM2D forward sensitivity vs FD", "[GRM2D],[Sensitivity],[Simulation] const double absTols[] = {3e5, 2e-3, 2e-4, 5.0}; const double relTols[] = {5e-3, 7e-2, 8e-2, 1e-4}; const double passRatio[] = {0.95, 0.9, 0.91, 0.83}; - cadet::test::column::testFwdSensSolutionFD("GENERAL_RATE_MODEL_2D", "FV", false, fdStepSize, absTols, relTols, passRatio); + cadet::test::column::testFwdSensSolutionFD("GENERAL_RATE_MODEL_2D", "FV", false, fdStepSize, absTols, relTols, + passRatio); } -TEST_CASE("GRM2D forward sensitivity forward vs backward flow", "[GRM2D],[Sensitivity],[Simulation],[fixGRM2D]") // todo fix. off by a lot +TEST_CASE("GRM2D forward sensitivity forward vs backward flow", + "[GRM2D],[Sensitivity],[Simulation],[fixGRM2D]") // todo fix. off by a lot { const double absTols[] = {4e-5, 1e-11, 1e-11, 8e-9}; const double relTols[] = {6e-9, 5e-8, 5e-6, 5e-10}; @@ -97,34 +101,42 @@ TEST_CASE("GRM2D consistent initialization with linear binding", "[GRM2D],[Consi cadet::test::column::testConsistentInitializationLinearBinding("GENERAL_RATE_MODEL_2D", "FV", 1e-12, 1e-12); } -TEST_CASE("GRM2D consistent initialization with SMA binding", "[GRM2D],[ConsistentInit],[fixGRM2D]") // todo fix. adjust tolerances? +TEST_CASE("GRM2D consistent initialization with SMA binding", + "[GRM2D],[ConsistentInit],[fixGRM2D]") // todo fix. adjust tolerances? { std::vector y(4 * 3 + 4 * 8 * 3 + 8 * 3 * 3 * (4 + 4) + 4 * 8 * 3, 0.0); -// Optimal values: -// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, -// 1.0, 1.8, 1.5, 1.6, 856.173, 64.457, 5.73227, 2.85286}; - const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, - 1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0}; - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 3 + 4 * 8 * 3); + // Optimal values: + // const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, + // 1.0, 1.8, 1.5, 1.6, 856.173, 64.457, 5.73227, 2.85286}; + const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, 1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0}; + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 3 + 4 * 8 * 3); cadet::test::util::repeat(y.data() + 4 * 3 + 4 * 8 * 3, bindingCell, 16, 3 * 8 * 3 / 2); - cadet::test::util::populate(y.data() + 4 * 3 + 4 * 8 * 3 + 8 * 3 * 3 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 8 * 3); + cadet::test::util::populate( + y.data() + 4 * 3 + 4 * 8 * 3 + 8 * 3 * 3 * (4 + 4), + [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 8 * 3); cadet::test::column::testConsistentInitializationSMABinding("GENERAL_RATE_MODEL_2D", "FV", y.data(), 1e-14, 1e-5); } -TEST_CASE("GRM2D consistent sensitivity initialization with linear binding", "[GRM2D],[ConsistentInit],[Sensitivity],[CIgrm2d]") +TEST_CASE("GRM2D consistent sensitivity initialization with linear binding", + "[GRM2D],[ConsistentInit],[Sensitivity],[CIgrm2d]") { // Fill state vector with given initial values const unsigned int numDofs = 2 * 3 + 2 * 8 * 3 + 8 * 3 * 3 * (2 + 2) + 2 * 8 * 3; std::vector y(numDofs, 0.0); std::vector yDot(numDofs, 0.0); - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL_2D", "FV", y.data(), yDot.data(), true, 1e-14); + cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL_2D", "FV", y.data(), yDot.data(), + true, 1e-14); } -TEST_CASE("GRM2D consistent sensitivity initialization with SMA binding", "[GRM2D],[ConsistentInit],[Sensitivity],[CIgrm2d]") +TEST_CASE("GRM2D consistent sensitivity initialization with SMA binding", + "[GRM2D],[ConsistentInit],[Sensitivity],[CIgrm2d]") { // Fill state vector with given initial values const unsigned int numDofs = 4 * 3 + 4 * 8 * 3 + 8 * 3 * 3 * (4 + 4) + 4 * 8 * 3; @@ -132,13 +144,18 @@ TEST_CASE("GRM2D consistent sensitivity initialization with SMA binding", "[GRM2 std::vector yDot(numDofs, 0.0); const double bindingCell[] = {1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0}; - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 3 + 4 * 8 * 3); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 3 + 4 * 8 * 3); cadet::test::util::repeat(y.data() + 4 * 3 + 4 * 8 * 3, bindingCell, 8, 3 * 8 * 3); - cadet::test::util::populate(y.data() + 4 * 3 + 4 * 8 * 3 + 8 * 3 * 3 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 8 * 3); + cadet::test::util::populate( + y.data() + 4 * 3 + 4 * 8 * 3 + 8 * 3 * 3 * (4 + 4), + [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 8 * 3); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL_2D", "FV", y.data(), yDot.data(), false, 1e-9); + cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL_2D", "FV", y.data(), yDot.data(), + false, 1e-9); } TEST_CASE("GRM2D inlet DOF Jacobian", "[GRM2D],[UnitOp],[Jacobian],[Inlet],[CIgrm2d]") @@ -156,54 +173,71 @@ TEST_CASE("GRM2D LWE separate identical particle types match", "[GRM2D],[Simulat cadet::test::particle::testSeparateIdenticalParticleTypes("GENERAL_RATE_MODEL_2D", "FV", 1e-15, 1e-15); } -TEST_CASE("GRM2D linear binding single particle matches particle distribution", "[GRM2D],[Simulation],[ParticleType],[CIgrm2d]") +TEST_CASE("GRM2D linear binding single particle matches particle distribution", + "[GRM2D],[Simulation],[ParticleType],[CIgrm2d]") { cadet::test::particle::testLinearMixedParticleTypes("GENERAL_RATE_MODEL_2D", "FV", 5e-8, 5e-5); } -TEST_CASE("GRM2D multiple particle types Jacobian analytic vs AD", "[GRM2D],[Jacobian],[AD],[ParticleType],[fixGRM2D]") // todo fix. AD and analytical Jacobians dont match +TEST_CASE("GRM2D multiple particle types Jacobian analytic vs AD", + "[GRM2D],[Jacobian],[AD],[ParticleType],[fixGRM2D]") // todo fix. AD and analytical Jacobians dont match { cadet::test::particle::testJacobianMixedParticleTypes("GENERAL_RATE_MODEL_2D", "FV"); } -TEST_CASE("GRM2D multiple particle types time derivative Jacobian vs FD", "[GRM2D],[UnitOp],[Residual],[Jacobian],[ParticleType],[FDtestGRM2D]") +TEST_CASE("GRM2D multiple particle types time derivative Jacobian vs FD", + "[GRM2D],[UnitOp],[Residual],[Jacobian],[ParticleType],[FDtestGRM2D]") { - cadet::test::particle::testTimeDerivativeJacobianMixedParticleTypesFD("GENERAL_RATE_MODEL_2D", "FV", 1e-6, 0.0, 5e-3); + cadet::test::particle::testTimeDerivativeJacobianMixedParticleTypesFD("GENERAL_RATE_MODEL_2D", "FV", 1e-6, 0.0, + 5e-3); } -TEST_CASE("GRM2D linear binding single particle matches spatially dependent particle distribution", "[GRM2D],[Simulation],[ParticleType],[CIgrm2d]") +TEST_CASE("GRM2D linear binding single particle matches spatially dependent particle distribution", + "[GRM2D],[Simulation],[ParticleType],[CIgrm2d]") { cadet::test::particle::testLinearSpatiallyMixedParticleTypes("GENERAL_RATE_MODEL_2D", "FV", 5e-8, 5e-5); } -TEST_CASE("GRM2D multiple spatially dependent particle types flux Jacobian vs FD", "[GRM2D],[UnitOp],[Residual],[Jacobian],[ParticleType],[FDtestGRM2D]") // todo fix. only one assertion is slightly off (5.0401-6 != 5.0247-6) +TEST_CASE("GRM2D multiple spatially dependent particle types flux Jacobian vs FD", + "[GRM2D],[UnitOp],[Residual],[Jacobian],[ParticleType],[FDtestGRM2D]") // todo fix. only one assertion is + // slightly off (5.0401-6 != 5.0247-6) { cadet::test::particle::testArrowHeadJacobianSpatiallyMixedParticleTypes("GENERAL_RATE_MODEL_2D", 1e-6, 1e-7, 1e-5); } -TEST_CASE("GRM2D dynamic reactions time derivative Jacobian vs FD bulk", "[GRM2D],[Jacobian],[Residual],[ReactionModel],[FDtestGRM2D]") +TEST_CASE("GRM2D dynamic reactions time derivative Jacobian vs FD bulk", + "[GRM2D],[Jacobian],[Residual],[ReactionModel],[FDtestGRM2D]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL_2D", "FV", true, false, false, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL_2D", "FV", true, false, + false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("GRM2D dynamic reactions time derivative Jacobian vs FD particle", "[GRM2D],[Jacobian],[Residual],[ReactionModel],[FDtestGRM2D]") +TEST_CASE("GRM2D dynamic reactions time derivative Jacobian vs FD particle", + "[GRM2D],[Jacobian],[Residual],[ReactionModel],[FDtestGRM2D]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL_2D", "FV", false, true, false, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL_2D", "FV", false, true, + false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("GRM2D dynamic reactions time derivative Jacobian vs FD modified particle", "[GRM2D],[Jacobian],[Residual],[ReactionModel],[FDtestGRM2D]") +TEST_CASE("GRM2D dynamic reactions time derivative Jacobian vs FD modified particle", + "[GRM2D],[Jacobian],[Residual],[ReactionModel],[FDtestGRM2D]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL_2D", "FV", false, true, true, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL_2D", "FV", false, true, + true, 1e-6, 1e-14, 8e-4); } -TEST_CASE("GRM2D dynamic reactions time derivative Jacobian vs FD bulk and particle", "[GRM2D],[Jacobian],[Residual],[ReactionModel],[FDtestGRM2D]") +TEST_CASE("GRM2D dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[GRM2D],[Jacobian],[Residual],[ReactionModel],[FDtestGRM2D]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL_2D", "FV", true, true, false, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL_2D", "FV", true, true, + false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("GRM2D dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[GRM2D],[Jacobian],[Residual],[ReactionModel],[FDtestGRM2D]") +TEST_CASE("GRM2D dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[GRM2D],[Jacobian],[Residual],[ReactionModel],[FDtestGRM2D]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL_2D", "FV", true, true, true, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL_2D", "FV", true, true, true, + 1e-6, 1e-14, 8e-4); } inline cadet::JsonParameterProvider createColumnWithTwoCompLinearBindingThreeParticleTypes() @@ -217,31 +251,36 @@ inline cadet::JsonParameterProvider createColumnWithTwoCompLinearBindingThreePar return jpp; } -TEST_CASE("GRM2D multi particle types dynamic reactions time derivative Jacobian vs FD bulk", "[GRM2D],[Jacobian],[Residual],[ReactionModel],[ParticleType],[FDtestGRM2D]") +TEST_CASE("GRM2D multi particle types dynamic reactions time derivative Jacobian vs FD bulk", + "[GRM2D],[Jacobian],[Residual],[ReactionModel],[ParticleType],[FDtestGRM2D]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, false, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("GRM2D multi particle types dynamic reactions time derivative Jacobian vs FD particle", "[GRM2D],[Jacobian],[Residual],[ReactionModel],[ParticleType],[FDtestGRM2D]") +TEST_CASE("GRM2D multi particle types dynamic reactions time derivative Jacobian vs FD particle", + "[GRM2D],[Jacobian],[Residual],[ReactionModel],[ParticleType],[FDtestGRM2D]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("GRM2D multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", "[GRM2D],[Jacobian],[Residual],[ReactionModel],[ParticleType],[FDtestGRM2D]") +TEST_CASE("GRM2D multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", + "[GRM2D],[Jacobian],[Residual],[ReactionModel],[ParticleType],[FDtestGRM2D]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, true, 1e-6, 1e-14, 8e-4); } -TEST_CASE("GRM2D multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", "[GRM2D],[Jacobian],[Residual],[ReactionModel],[ParticleType],[FDtestGRM2D]") +TEST_CASE("GRM2D multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[GRM2D],[Jacobian],[Residual],[ReactionModel],[ParticleType],[FDtestGRM2D]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("GRM2D multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[GRM2D],[Jacobian],[Residual],[ReactionModel],[ParticleType],[FDtestGRM2D]") +TEST_CASE("GRM2D multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[GRM2D],[Jacobian],[Residual],[ReactionModel],[ParticleType],[FDtestGRM2D]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, true, 1e-6, 1e-14, 8e-4); @@ -308,8 +347,10 @@ TEST_CASE("GRM2D with 1 radial zone matches GRM", "[GRM],[GRM2D],[UnitOp],[Jacob tls.resize(std::max(grm2d->threadLocalMemorySize(), grm->threadLocalMemorySize())); // Fill state vectors with some values - cadet::test::util::populate(y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); - cadet::test::util::populate(yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); // Setup matrices const cadet::AdJacobianParams noAdParams{nullptr, nullptr, 0u}; @@ -329,10 +370,12 @@ TEST_CASE("GRM2D with 1 radial zone matches GRM", "[GRM],[GRM2D],[UnitOp],[Jacob } // Compare Jacobian - cadet::test::compareJacobian(grm, grm2d, y.data(), yDot.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), 0.0, 1e-15); + cadet::test::compareJacobian(grm, grm2d, y.data(), yDot.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), 0.0, + 1e-15); // Compare Jacobian solutions - cadet::test::util::populate(jacCol1.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + 2 * nDof) * 0.17)) + 1e-4; }, nDof); + cadet::test::util::populate( + jacCol1.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + 2 * nDof) * 0.17)) + 1e-4; }, nDof); std::copy(jacCol1.begin(), jacCol1.end(), jacCol2.begin()); std::fill(jacDir.begin(), jacDir.end(), 1.0); diff --git a/test/GeneralRateModelDG.cpp b/test/GeneralRateModelDG.cpp index 5983f9110..9d24f7eaf 100644 --- a/test/GeneralRateModelDG.cpp +++ b/test/GeneralRateModelDG.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -38,21 +38,28 @@ TEST_CASE("GRM_DG LWE forward vs backward flow", "[GRM],[DG],[Simulation],[CI]") TEST_CASE("GRM_DG linear pulse vs analytic solution", "[GRM],[DG],[Simulation],[Analytic],[CI]") { cadet::test::column::DGparams disc; - cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", true, true, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", true, false, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", false, true, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", false, false, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", true, true, disc, + 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", true, false, disc, + 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", false, true, disc, + 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("GENERAL_RATE_MODEL", "/data/grm-pulseBenchmark.data", false, false, + disc, 6e-5, 1e-7); } -TEST_CASE("GRM_DG non-binding linear pulse vs analytic solution", "[GRM],[DG],[Simulation],[Analytic],[NonBinding],[CI]") +TEST_CASE("GRM_DG non-binding linear pulse vs analytic solution", + "[GRM],[DG],[Simulation],[Analytic],[NonBinding],[CI]") { cadet::test::column::DGparams disc; - cadet::test::column::testAnalyticNonBindingBenchmark("GENERAL_RATE_MODEL", "/data/grm-nonBinding.data", true, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticNonBindingBenchmark("GENERAL_RATE_MODEL", "/data/grm-nonBinding.data", false, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticNonBindingBenchmark("GENERAL_RATE_MODEL", "/data/grm-nonBinding.data", true, disc, + 6e-5, 1e-7); + cadet::test::column::testAnalyticNonBindingBenchmark("GENERAL_RATE_MODEL", "/data/grm-nonBinding.data", false, disc, + 6e-5, 1e-7); } // todo FIX (scheitert bei backward flow jacobian vs AD) -//TEST_CASE("GRM_DG Jacobian forward vs backward flow", "[GRM],[DG],[UnitOp],[Residual],[Jacobian],[AD],[todo]") +// TEST_CASE("GRM_DG Jacobian forward vs backward flow", "[GRM],[DG],[UnitOp],[Residual],[Jacobian],[AD],[todo]") //{ // cadet::test::column::DGparams disc; // @@ -60,27 +67,34 @@ TEST_CASE("GRM_DG non-binding linear pulse vs analytic solution", "[GRM],[DG],[S // for (int i = 0; i <= 1; i++) // { // disc.setIntegrationMode(i); -// cadet::test::column::testJacobianForwardBackward("GENERAL_RATE_MODEL", disc, std::numeric_limits::epsilon() * 100.0); +// cadet::test::column::testJacobianForwardBackward("GENERAL_RATE_MODEL", disc, +// std::numeric_limits::epsilon() * 100.0); // } //} -TEST_CASE("GRM_DG numerical Benchmark with parameter sensitivities for linear case", "[GRM],[DG],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE("GRM_DG numerical Benchmark with parameter sensitivities for linear case", + "[GRM],[DG],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on + // server { const std::string& modelFilePath = std::string("/data/model_GRM_dynLin_1comp_benchmark1.json"); - const std::string& refFilePath = std::string("/data/ref_GRM_dynLin_1comp_sensbenchmark1_cDG_P3Z8_GSM_parP3parZ1.h5"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1.0, 1.0, 1.0, 1.0 }; + const std::string& refFilePath = + std::string("/data/ref_GRM_dynLin_1comp_sensbenchmark1_cDG_P3Z8_GSM_parP3parZ1.h5"); + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1.0, 1.0, 1.0, 1.0}; cadet::test::column::DGparams disc(0, 3, 8, 3, 1); cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, true); } -TEST_CASE("GRM_DG numerical Benchmark with parameter sensitivities for SMA LWE case", "[GRM],[DG],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE("GRM_DG numerical Benchmark with parameter sensitivities for SMA LWE case", + "[GRM],[DG],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on + // server { const std::string& modelFilePath = std::string("/data/model_GRM_reqSMA_4comp_benchmark1.json"); - const std::string& refFilePath = std::string("/data/ref_GRM_reqSMA_4comp_sensbenchmark1_cDG_P3Z8_GSM_parP3parZ1.h5"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1.0, 1.0, 1.0, 1.0 }; + const std::string& refFilePath = + std::string("/data/ref_GRM_reqSMA_4comp_sensbenchmark1_cDG_P3Z8_GSM_parP3parZ1.h5"); + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1.0, 1.0, 1.0, 1.0}; cadet::test::column::DGparams disc(0, 3, 8, 3, 1); cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "000", absTol, relTol, disc, true); @@ -120,7 +134,8 @@ TEST_CASE("GRM_DG LWE DGSEM and GSM particle discretization yields similar accur double const* DGSEMOutlet = DGSEMData->outlet(); const unsigned int nComp = GSMData->numComponents(); - for (unsigned int i = 0; i < GSMData->numDataPoints() * GSMData->numInletPorts() * nComp; ++i, ++GSMOutlet, ++DGSEMOutlet) + for (unsigned int i = 0; i < GSMData->numDataPoints() * GSMData->numInletPorts() * nComp; + ++i, ++GSMOutlet, ++DGSEMOutlet) { // Forward flow inlet = backward flow outlet CAPTURE(i); @@ -133,17 +148,21 @@ TEST_CASE("GRM_DG time derivative Jacobian vs FD", "[GRM],[DG],[UnitOp],[Residua cadet::test::column::testTimeDerivativeJacobianFD("GENERAL_RATE_MODEL", "DG", 1e-6, 0.0, 9e-4); } -//TEST_CASE("GRM_DG dynamic binding with surf diff par dep Jacobian vs AD", "[GRM],[DG],[UnitOp],[Residual],[Jacobian],[ParameterDependence],[fix]") +// TEST_CASE("GRM_DG dynamic binding with surf diff par dep Jacobian vs AD", +// "[GRM],[DG],[UnitOp],[Residual],[Jacobian],[ParameterDependence],[fix]") //{ // cadet::test::column::testJacobianADVariableParSurfDiff("GENERAL_RATE_MODEL", "DG", true); -//} +// } -TEST_CASE("GRM_DG rapid-equilibrium binding with surf diff par dep Jacobian vs AD", "[GRM],[DG],[UnitOp],[Residual],[Jacobian],[ParameterDependence]") // todo include in CI (runs locally but fails on server with linux) +TEST_CASE("GRM_DG rapid-equilibrium binding with surf diff par dep Jacobian vs AD", + "[GRM],[DG],[UnitOp],[Residual],[Jacobian],[ParameterDependence]") // todo include in CI (runs locally but + // fails on server with linux) { cadet::test::column::testJacobianADVariableParSurfDiff("GENERAL_RATE_MODEL", "DG", false); } -TEST_CASE("GRM_DG sensitivity Jacobians", "[GRM],[DG],[UnitOp],[Sensitivity]") // todo does not run on CI but locally on windows +TEST_CASE("GRM_DG sensitivity Jacobians", + "[GRM],[DG],[UnitOp],[Sensitivity]") // todo does not run on CI but locally on windows { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding("GENERAL_RATE_MODEL", "DG"); @@ -151,7 +170,7 @@ TEST_CASE("GRM_DG sensitivity Jacobians", "[GRM],[DG],[UnitOp],[Sensitivity]") / } //// todo fix: not just adjust tolerances as in FV but theres an actual error here: access violation in densematrix -//TEST_CASE("GRM_DG forward sensitivity vs FD", "[GRM],[DG],[Sensitivity],[Simulation],[todo]") +// TEST_CASE("GRM_DG forward sensitivity vs FD", "[GRM],[DG],[Sensitivity],[Simulation],[todo]") //{ // // Relative error is checked first, we use high absolute error for letting // // some points that are far off pass the error test, too. This is required @@ -160,17 +179,18 @@ TEST_CASE("GRM_DG sensitivity Jacobians", "[GRM],[DG],[UnitOp],[Sensitivity]") / // const double absTols[] = { 3e5, 2e-3, 2e-4, 5.0 }; // const double relTols[] = { 5e-3, 7e-2, 8e-2, 1e-4 }; // const double passRatio[] = { 0.95, 0.9, 0.91, 0.83 }; -// cadet::test::column::testFwdSensSolutionFD("GENERAL_RATE_MODEL", "DG", false, fdStepSize, absTols, relTols, passRatio); -//} +// cadet::test::column::testFwdSensSolutionFD("GENERAL_RATE_MODEL", "DG", false, fdStepSize, absTols, relTols, +// passRatio); +// } //// todo fix: not just adjust tolerances as in FV but theres an actual error here: access violation in densematrix -//TEST_CASE("GRM_DG forward sensitivity forward vs backward flow", "[GRM],[DG],[Sensitivity],[Simulation],[todo]") +// TEST_CASE("GRM_DG forward sensitivity forward vs backward flow", "[GRM],[DG],[Sensitivity],[Simulation],[todo]") //{ // const double absTols[] = { 4e-5, 1e-11, 1e-11, 8e-9 }; // const double relTols[] = { 6e-9, 5e-8, 5e-6, 5e-10 }; // const double passRatio[] = { 0.99, 0.95, 0.98, 0.98 }; // cadet::test::column::testFwdSensSolutionForwardBackward("GENERAL_RATE_MODEL", "DG", absTols, relTols, passRatio); -//} +// } // todo fix consistent initialization for AD with req binding TEST_CASE("GRM_DG consistent initialization with linear binding", "[GRM],[DG],[ConsistentInit],[CI]") @@ -178,43 +198,52 @@ TEST_CASE("GRM_DG consistent initialization with linear binding", "[GRM],[DG],[C cadet::test::column::testConsistentInitializationLinearBinding("GENERAL_RATE_MODEL", "DG", 1e-12, 1e-14, 0, 0); cadet::test::column::testConsistentInitializationLinearBinding("GENERAL_RATE_MODEL", "DG", 1e-12, 1e-12, 1, 0); cadet::test::column::testConsistentInitializationLinearBinding("GENERAL_RATE_MODEL", "DG", 1e-12, 1e-14, 0, 1); - //cadet::test::column::testConsistentInitializationLinearBinding("GENERAL_RATE_MODEL", "DG", 1e-12, 1e-14, 1, 1); + // cadet::test::column::testConsistentInitializationLinearBinding("GENERAL_RATE_MODEL", "DG", 1e-12, 1e-14, 1, 1); } //// todo fix consistent initialization for SMA (initialization not completely correct; AD gives assertion error) -//TEST_CASE("GRM_DG consistent initialization with SMA binding", "[GRM],[DG],[ConsistentInit],[todo]") +// TEST_CASE("GRM_DG consistent initialization with SMA binding", "[GRM],[DG],[ConsistentInit],[todo]") //{ // std::vector y(4 + 4 * 16 + 16 * 4 * (4 + 4) + 4 * 16, 0.0); // // Optimal values: -// // const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, +// // const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, // // 1.0, 1.8, 1.5, 1.6, 856.173, 64.457, 5.73227, 2.85286}; // const double bindingCell[] = { 1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, // 1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0 }; -// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 16); -// cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 16, 4 * 16 / 2); -// cadet::test::util::populate(y.data() + 4 + 4 * 16 + 16 * 4 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); +// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 +//* 16); cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 16, 4 * 16 / 2); +// cadet::test::util::populate(y.data() + 4 + 4 * 16 + 16 * 4 * (4 + 4), [](unsigned int idx) { return +// std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); // // cadet::test::column::testConsistentInitializationSMABinding("GENERAL_RATE_MODEL", "DG", y.data(), 1e-14, 1e-5); -//} +// } // todo fix kinetic binding sensitivity init -TEST_CASE("GRM_DG consistent sensitivity initialization with linear binding", "[GRM],[DG],[ConsistentInit],[Sensitivity],[CI]") +TEST_CASE("GRM_DG consistent sensitivity initialization with linear binding", + "[GRM],[DG],[ConsistentInit],[Sensitivity],[CI]") { // Fill state vector with given initial values const unsigned int numDofs = 4 + 4 * 16 + 16 * 4 * (4 + 4) + 4 * 16; std::vector y(numDofs, 0.0); std::vector yDot(numDofs, 0.0); - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - - //cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL", "DG", y.data(), yDot.data(), true, 1e-14, 0, 0); - cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL", "DG", y.data(), yDot.data(), true, 1e-14, 1, 0); - //cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL", "DG", y.data(), yDot.data(), true, 1e-14, 0, 1); - cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL", "DG", y.data(), yDot.data(), true, 1e-14, 1, 1); -} - -//// todo fix memory stuff (works for FV) -//TEST_CASE("GRM_DG consistent sensitivity initialization with SMA binding", "[GRM],[DG],[ConsistentInit],[Sensitivity],[fffffffiujbnlk]") + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + + // cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL", "DG", y.data(), yDot.data(), + // true, 1e-14, 0, 0); + cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL", "DG", y.data(), yDot.data(), + true, 1e-14, 1, 0); + // cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL", "DG", y.data(), yDot.data(), + // true, 1e-14, 0, 1); + cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL", "DG", y.data(), yDot.data(), + true, 1e-14, 1, 1); +} + +//// todo fix memory stuff (works for FV) +// TEST_CASE("GRM_DG consistent sensitivity initialization with SMA binding", +// "[GRM],[DG],[ConsistentInit],[Sensitivity],[fffffffiujbnlk]") //{ // // Fill state vector with given initial values // const unsigned int numDofs = 4 + 4 * 16 + 16 * 4 * (4 + 4) + 4 * 16; @@ -222,14 +251,17 @@ TEST_CASE("GRM_DG consistent sensitivity initialization with linear binding", "[ // std::vector yDot(numDofs, 0.0); // // const double bindingCell[] = { 1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0 }; -// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 16); -// cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 8, 4 * 16); -// cadet::test::util::populate(y.data() + 4 + 4 * 16 + 16 * 4 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); +// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 +//* 16); cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 8, 4 * 16); +// cadet::test::util::populate(y.data() +//+ 4 + 4 * 16 + 16 * 4 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); // -// cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); +// cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, +// numDofs); // -// cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL", "DG", y.data(), yDot.data(), false, 1e-9); -//} +// cadet::test::column::testConsistentInitializationSensitivity("GENERAL_RATE_MODEL", "DG", y.data(), yDot.data(), +// false, 1e-9); +// } TEST_CASE("GRM_DG inlet DOF Jacobian", "[GRM],[DG],[UnitOp],[Jacobian],[Inlet],[CI]") { @@ -249,41 +281,46 @@ TEST_CASE("GRM_DG with two component linear binding Jacobian", "[GRM],[DG],[Unit } //// todo fix cant load library with debug info -//TEST_CASE("GRM_DG LWE one vs two identical particle types match", "[GRM],[DG],[Simulation],[ParticleType],[todo]") +// TEST_CASE("GRM_DG LWE one vs two identical particle types match", "[GRM],[DG],[Simulation],[ParticleType],[todo]") //{ // cadet::test::particle::testOneVsTwoIdenticalParticleTypes("GENERAL_RATE_MODEL", "DG", 2e-8, 5e-5); -//} +// } //// todo fix cant load library with debug info in all of the following tests -//TEST_CASE("GRM_DG LWE separate identical particle types match", "[GRM],[DG],[Simulation],[ParticleType],[todo]") +// TEST_CASE("GRM_DG LWE separate identical particle types match", "[GRM],[DG],[Simulation],[ParticleType],[todo]") //{ // cadet::test::particle::testSeparateIdenticalParticleTypes("GENERAL_RATE_MODEL", "DG", 1e-15, 1e-15); -//} +// } // -//TEST_CASE("GRM_DG linear binding single particle matches particle distribution", "[GRM],[DG],[Simulation],[ParticleType],[todo]") +// TEST_CASE("GRM_DG linear binding single particle matches particle distribution", +// "[GRM],[DG],[Simulation],[ParticleType],[todo]") //{ // cadet::test::particle::testLinearMixedParticleTypes("GENERAL_RATE_MODEL", "DG", 5e-8, 5e-5); -//} +// } // -//TEST_CASE("GRM_DG multiple particle types Jacobian analytic vs AD", "[GRM],[DG],[Jacobian],[AD],[ParticleType],[todo]") +// TEST_CASE("GRM_DG multiple particle types Jacobian analytic vs AD", +// "[GRM],[DG],[Jacobian],[AD],[ParticleType],[todo]") //{ // cadet::test::particle::testJacobianMixedParticleTypes("GENERAL_RATE_MODEL", "DG"); -//} +// } // -//TEST_CASE("GRM_DG multiple particle types time derivative Jacobian vs FD", "[GRM],[DG],[UnitOp],[Residual],[Jacobian],[ParticleType],[todo]") +// TEST_CASE("GRM_DG multiple particle types time derivative Jacobian vs FD", +// "[GRM],[DG],[UnitOp],[Residual],[Jacobian],[ParticleType],[todo]") //{ // cadet::test::particle::testTimeDerivativeJacobianMixedParticleTypesFD("GENERAL_RATE_MODEL", "DG", 1e-6, 0.0, 9e-4); -//} +// } // -//TEST_CASE("GRM_DG multiple spatially dependent particle types Jacobian analytic vs AD", "[GRM],[DG],[Jacobian],[AD],[ParticleType],[todo]") +// TEST_CASE("GRM_DG multiple spatially dependent particle types Jacobian analytic vs AD", +// "[GRM],[DG],[Jacobian],[AD],[ParticleType],[todo]") //{ // cadet::test::particle::testJacobianSpatiallyMixedParticleTypes("GENERAL_RATE_MODEL", "DG"); -//} +// } // -//TEST_CASE("GRM_DG linear binding single particle matches spatially dependent particle distribution", "[GRM],[DG],[Simulation],[ParticleType],[todo]") +// TEST_CASE("GRM_DG linear binding single particle matches spatially dependent particle distribution", +// "[GRM],[DG],[Simulation],[ParticleType],[todo]") //{ // cadet::test::particle::testLinearSpatiallyMixedParticleTypes("GENERAL_RATE_MODEL", "DG", 5e-8, 5e-5); -//} +// } TEST_CASE("GRM_DG dynamic reactions Jacobian vs AD bulk", "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[CI]") { @@ -295,112 +332,135 @@ TEST_CASE("GRM_DG dynamic reactions Jacobian vs AD particle", "[GRM],[DG],[Jacob cadet::test::reaction::testUnitJacobianDynamicReactionsAD("GENERAL_RATE_MODEL", "DG", false, true, false); } -TEST_CASE("GRM_DG dynamic reactions Jacobian vs AD modified particle", "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[CI]") +TEST_CASE("GRM_DG dynamic reactions Jacobian vs AD modified particle", + "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[CI]") { cadet::test::reaction::testUnitJacobianDynamicReactionsAD("GENERAL_RATE_MODEL", "DG", false, true, true); } -TEST_CASE("GRM_DG dynamic reactions Jacobian vs AD bulk and particle", "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[CI]") +TEST_CASE("GRM_DG dynamic reactions Jacobian vs AD bulk and particle", + "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[CI]") { cadet::test::reaction::testUnitJacobianDynamicReactionsAD("GENERAL_RATE_MODEL", "DG", true, true, false); } -TEST_CASE("GRM_DG dynamic reactions Jacobian vs AD bulk and modified particle", "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[CI]") +TEST_CASE("GRM_DG dynamic reactions Jacobian vs AD bulk and modified particle", + "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[CI]") { cadet::test::reaction::testUnitJacobianDynamicReactionsAD("GENERAL_RATE_MODEL", "DG", true, true, true); } -TEST_CASE("GRM_DG dynamic reactions time derivative Jacobian vs FD bulk", "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("GRM_DG dynamic reactions time derivative Jacobian vs FD bulk", + "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "DG", true, false, false, 1e-6, 1e-14, 9e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "DG", true, false, false, + 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM_DG dynamic reactions time derivative Jacobian vs FD particle", "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("GRM_DG dynamic reactions time derivative Jacobian vs FD particle", + "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "DG", false, true, false, 1e-6, 1e-14, 9e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "DG", false, true, false, + 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM_DG dynamic reactions time derivative Jacobian vs FD modified particle", "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("GRM_DG dynamic reactions time derivative Jacobian vs FD modified particle", + "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "DG", false, true, true, 1e-6, 1e-14, 9e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "DG", false, true, true, + 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM_DG dynamic reactions time derivative Jacobian vs FD bulk and particle", "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("GRM_DG dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "DG", true, true, false, 1e-6, 1e-14, 9e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "DG", true, true, false, + 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM_DG dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("GRM_DG dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "DG", true, true, true, 1e-6, 1e-14, 9e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("GENERAL_RATE_MODEL", "DG", true, true, true, + 1e-6, 1e-14, 9e-4); } inline cadet::JsonParameterProvider createColumnWithTwoCompLinearBindingThreeParticleTypes() { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding("GENERAL_RATE_MODEL", "DG"); - const double parVolFrac[] = { 0.3, 0.6, 0.1 }; - const double parFactor[] = { 0.9, 0.8 }; + const double parVolFrac[] = {0.3, 0.6, 0.1}; + const double parFactor[] = {0.9, 0.8}; cadet::test::particle::extendModelToManyParticleTypes(jpp, 3, parFactor, parVolFrac); return jpp; } -TEST_CASE("GRM_DG multi particle types dynamic reactions Jacobian vs AD bulk", "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("GRM_DG multi particle types dynamic reactions Jacobian vs AD bulk", + "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, false, false); } -TEST_CASE("GRM_DG multi particle types dynamic reactions Jacobian vs AD particle", "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("GRM_DG multi particle types dynamic reactions Jacobian vs AD particle", + "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, false, true, false); } -TEST_CASE("GRM_DG multi particle types dynamic reactions Jacobian vs AD modified particle", "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("GRM_DG multi particle types dynamic reactions Jacobian vs AD modified particle", + "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, false, true, true); } -TEST_CASE("GRM_DG multi particle types dynamic reactions Jacobian vs AD bulk and particle", "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("GRM_DG multi particle types dynamic reactions Jacobian vs AD bulk and particle", + "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, true, false); } -TEST_CASE("GRM_DG multi particle types dynamic reactions Jacobian vs AD bulk and modified particle", "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("GRM_DG multi particle types dynamic reactions Jacobian vs AD bulk and modified particle", + "[GRM],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, true, true); } -TEST_CASE("GRM_DG multi particle types dynamic reactions time derivative Jacobian vs FD bulk", "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("GRM_DG multi particle types dynamic reactions time derivative Jacobian vs FD bulk", + "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, false, false, 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM_DG multi particle types dynamic reactions time derivative Jacobian vs FD particle", "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("GRM_DG multi particle types dynamic reactions time derivative Jacobian vs FD particle", + "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, false, 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM_DG multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("GRM_DG multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", + "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, true, 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM_DG multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("GRM_DG multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, false, 1e-6, 1e-14, 9e-4); } -TEST_CASE("GRM_DG multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("GRM_DG multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[GRM],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, true, 1e-6, 1e-14, 9e-4); diff --git a/test/Graph.cpp b/test/Graph.cpp index 6b369fd62..24b14f065 100644 --- a/test/Graph.cpp +++ b/test/Graph.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -18,89 +18,87 @@ namespace { - bool dependsOn(const int nUnits, const std::vector& connections, const int unit, const int unitDep) +bool dependsOn(const int nUnits, const std::vector& connections, const int unit, const int unitDep) +{ + // Checks whether unit depends on unitDep + for (std::size_t i = 0; i < connections.size() / 6; ++i) { - // Checks whether unit depends on unitDep - for (std::size_t i = 0; i < connections.size() / 6; ++i) - { - if ((connections[i * 6] == unitDep) && (connections[i * 6 + 1] == unit)) - return true; - } - return false; + if ((connections[i * 6] == unitDep) && (connections[i * 6 + 1] == unit)) + return true; } + return false; +} + +void checkTopoOrdering(const int nUnits, const std::vector& connections, const std::vector& topoOrder) +{ + CHECK(nUnits == topoOrder.size()); - void checkTopoOrdering(const int nUnits, const std::vector& connections, const std::vector& topoOrder) + // Check if each unit is in topoOrder + for (int i = 0; i < nUnits; ++i) { - CHECK(nUnits == topoOrder.size()); + CHECK(std::find(topoOrder.begin(), topoOrder.end(), i) != topoOrder.end()); + } - // Check if each unit is in topoOrder - for (int i = 0; i < nUnits; ++i) - { - CHECK(std::find(topoOrder.begin(), topoOrder.end(), i) != topoOrder.end()); - } + // Check order + for (int i = topoOrder.size() - 1; i >= 0; --i) + { + const int u = topoOrder[i]; - // Check order - for (int i = topoOrder.size() - 1; i >= 0; --i) + // Unit u must not depend on all units before it in topoOrder + for (int j = 0; j < i; ++j) { - const int u = topoOrder[i]; - - // Unit u must not depend on all units before it in topoOrder - for (int j = 0; j < i; ++j) - { - CHECK(!dependsOn(nUnits, connections, u, topoOrder[j])); - } + CHECK(!dependsOn(nUnits, connections, u, topoOrder[j])); } } +} - bool contains(const int* data, int size, int val) +bool contains(const int* data, int size, int val) +{ + for (int i = 0; i < size; ++i) { - for (int i = 0; i < size; ++i) - { - if (data[i] == val) - return true; - } - - return false; + if (data[i] == val) + return true; } - void checkAdjacencyList(const int nUnits, const std::vector& connections, const cadet::util::SlicedVector& adjList) - { - REQUIRE(adjList.slices() == nUnits); + return false; +} + +void checkAdjacencyList(const int nUnits, const std::vector& connections, + const cadet::util::SlicedVector& adjList) +{ + REQUIRE(adjList.slices() == nUnits); - for (int i = 0; i < nUnits; ++i) + for (int i = 0; i < nUnits; ++i) + { + for (int j = 0; j < nUnits; ++j) { - for (int j = 0; j < nUnits; ++j) + const int s = adjList.sliceSize(i); + int const* const list = adjList[i]; + + // Check connection from i to j + if (dependsOn(nUnits, connections, j, i)) { - const int s = adjList.sliceSize(i); - int const* const list = adjList[i]; - - // Check connection from i to j - if (dependsOn(nUnits, connections, j, i)) - { - // i connects to j, so list for i should contain j - CHECK(contains(list, s, j)); - } - else - { - // i does not connect to j, so list for i should not contain j - CHECK(!contains(list, s, j)); - } + // i connects to j, so list for i should contain j + CHECK(contains(list, s, j)); + } + else + { + // i does not connect to j, so list for i should not contain j + CHECK(!contains(list, s, j)); } } } } +} // namespace TEST_CASE("Linear graph all ports all comps no cycles", "[Graph]") { const int nUnits = 4; - const std::vector connections = { - 0, 1, -1, -1, -1, -1, - 1, 2, -1, -1, -1, -1, - 2, 3, -1, -1, -1, -1 - }; - cadet::util::SlicedVector adjList = cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); + const std::vector connections = {0, 1, -1, -1, -1, -1, 1, 2, -1, -1, -1, -1, 2, 3, -1, -1, -1, -1}; + cadet::util::SlicedVector adjList = + cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); checkAdjacencyList(nUnits, connections, adjList); - + std::vector topoOrder; const bool cycle = cadet::graph::topologicalSort(adjList, topoOrder); checkTopoOrdering(nUnits, connections, topoOrder); @@ -111,12 +109,9 @@ TEST_CASE("Linear graph all ports all comps no cycles", "[Graph]") TEST_CASE("Linear graph specific ports all comps no cycles", "[Graph]") { const int nUnits = 4; - const std::vector connections = { - 0, 1, 0, 0, -1, -1, - 1, 2, 0, 0, -1, -1, - 2, 3, 0, 0, -1, -1 - }; - cadet::util::SlicedVector adjList = cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); + const std::vector connections = {0, 1, 0, 0, -1, -1, 1, 2, 0, 0, -1, -1, 2, 3, 0, 0, -1, -1}; + cadet::util::SlicedVector adjList = + cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); checkAdjacencyList(nUnits, connections, adjList); std::vector topoOrder; @@ -129,18 +124,11 @@ TEST_CASE("Linear graph specific ports all comps no cycles", "[Graph]") TEST_CASE("Linear graph all ports specific comps no cycles", "[Graph]") { const int nUnits = 4; - const std::vector connections = { - 0, 1, -1, -1, 0, 0, - 0, 1, -1, -1, 1, 1, - 0, 1, -1, -1, 2, 2, - 1, 2, -1, -1, 0, 0, - 1, 2, -1, -1, 1, 1, - 1, 2, -1, -1, 2, 2, - 2, 3, -1, -1, 0, 0, - 2, 3, -1, -1, 1, 1, - 2, 3, -1, -1, 2, 2 - }; - cadet::util::SlicedVector adjList = cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); + const std::vector connections = {0, 1, -1, -1, 0, 0, 0, 1, -1, -1, 1, 1, 0, 1, -1, -1, 2, 2, + 1, 2, -1, -1, 0, 0, 1, 2, -1, -1, 1, 1, 1, 2, -1, -1, 2, 2, + 2, 3, -1, -1, 0, 0, 2, 3, -1, -1, 1, 1, 2, 3, -1, -1, 2, 2}; + cadet::util::SlicedVector adjList = + cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); checkAdjacencyList(nUnits, connections, adjList); std::vector topoOrder; @@ -153,18 +141,11 @@ TEST_CASE("Linear graph all ports specific comps no cycles", "[Graph]") TEST_CASE("Linear graph specific ports and comps no cycles", "[Graph]") { const int nUnits = 4; - const std::vector connections = { - 0, 1, 0, 0, 0, 0, - 0, 1, 0, 0, 1, 1, - 0, 1, 0, 0, 2, 2, - 1, 2, 0, 0, 0, 0, - 1, 2, 0, 0, 1, 1, - 1, 2, 0, 0, 2, 2, - 2, 3, 0, 0, 0, 0, - 2, 3, 0, 0, 1, 1, - 2, 3, 0, 0, 2, 2 - }; - cadet::util::SlicedVector adjList = cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); + const std::vector connections = {0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 2, 2, + 1, 2, 0, 0, 0, 0, 1, 2, 0, 0, 1, 1, 1, 2, 0, 0, 2, 2, + 2, 3, 0, 0, 0, 0, 2, 3, 0, 0, 1, 1, 2, 3, 0, 0, 2, 2}; + cadet::util::SlicedVector adjList = + cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); checkAdjacencyList(nUnits, connections, adjList); std::vector topoOrder; @@ -177,23 +158,18 @@ TEST_CASE("Linear graph specific ports and comps no cycles", "[Graph]") TEST_CASE("Two ports linear graph specific ports all comps no cycles", "[Graph]") { /* - 1 1 1 - /-->--O-->--O-->--\ + 1 1 1 + /-->--O-->--O-->--\ 2 -->O O--> 2 - \-->--O-->--O-->--/ - 1 1 1 + \-->--O-->--O-->--/ + 1 1 1 */ const int nUnits = 4; - const std::vector connections = { - 0, 1, 0, 0, -1, -1, - 0, 1, 0, 1, -1, -1, - 1, 2, 0, 1, -1, -1, - 1, 2, 1, 0, -1, -1, - 2, 3, 0, 0, -1, -1, - 2, 3, 1, 0, -1, -1 - }; - cadet::util::SlicedVector adjList = cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); + const std::vector connections = {0, 1, 0, 0, -1, -1, 0, 1, 0, 1, -1, -1, 1, 2, 0, 1, -1, -1, + 1, 2, 1, 0, -1, -1, 2, 3, 0, 0, -1, -1, 2, 3, 1, 0, -1, -1}; + cadet::util::SlicedVector adjList = + cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); checkAdjacencyList(nUnits, connections, adjList); std::vector topoOrder; @@ -206,35 +182,20 @@ TEST_CASE("Two ports linear graph specific ports all comps no cycles", "[Graph]" TEST_CASE("Two ports linear graph specific ports and comps no cycles", "[Graph]") { /* - 1 1 1 - /-->--O-->--O-->--\ + 1 1 1 + /-->--O-->--O-->--\ 2 -->O O--> 2 - \-->--O-->--O-->--/ - 1 1 1 + \-->--O-->--O-->--/ + 1 1 1 */ const int nUnits = 4; const std::vector connections = { - 0, 1, 0, 0, 0, 0, - 0, 1, 0, 0, 1, 1, - 0, 1, 0, 0, 2, 2, - 0, 1, 0, 1, 0, 0, - 0, 1, 0, 1, 1, 1, - 0, 1, 0, 1, 2, 2, - 1, 2, 0, 1, 0, 0, - 1, 2, 0, 1, 1, 1, - 1, 2, 0, 1, 2, 2, - 1, 2, 1, 0, 0, 0, - 1, 2, 1, 0, 1, 1, - 1, 2, 1, 0, 2, 2, - 2, 3, 0, 0, 0, 0, - 2, 3, 0, 0, 1, 1, - 2, 3, 0, 0, 2, 2, - 2, 3, 1, 0, 0, 0, - 2, 3, 1, 0, 1, 1, - 2, 3, 1, 0, 2, 2 - }; - cadet::util::SlicedVector adjList = cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); + 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 2, 2, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 2, 2, + 1, 2, 0, 1, 0, 0, 1, 2, 0, 1, 1, 1, 1, 2, 0, 1, 2, 2, 1, 2, 1, 0, 0, 0, 1, 2, 1, 0, 1, 1, 1, 2, 1, 0, 2, 2, + 2, 3, 0, 0, 0, 0, 2, 3, 0, 0, 1, 1, 2, 3, 0, 0, 2, 2, 2, 3, 1, 0, 0, 0, 2, 3, 1, 0, 1, 1, 2, 3, 1, 0, 2, 2}; + cadet::util::SlicedVector adjList = + cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); checkAdjacencyList(nUnits, connections, adjList); std::vector topoOrder; @@ -247,19 +208,16 @@ TEST_CASE("Two ports linear graph specific ports and comps no cycles", "[Graph]" TEST_CASE("N to 1 to N graph specific ports all comps no cycles", "[Graph]") { /* - O--\ /--O - --O-- - O--/ \--O + O--\ /--O + --O-- + O--/ \--O */ const int nUnits = 5; - const std::vector connections = { - 0, 2, 0, 0, -1, -1, - 1, 2, 0, 0, -1, -1, - 2, 3, 0, 0, -1, -1, - 2, 4, 0, 0, -1, -1 - }; - cadet::util::SlicedVector adjList = cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); + const std::vector connections = {0, 2, 0, 0, -1, -1, 1, 2, 0, 0, -1, -1, + 2, 3, 0, 0, -1, -1, 2, 4, 0, 0, -1, -1}; + cadet::util::SlicedVector adjList = + cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); checkAdjacencyList(nUnits, connections, adjList); std::vector topoOrder; @@ -272,19 +230,16 @@ TEST_CASE("N to 1 to N graph specific ports all comps no cycles", "[Graph]") TEST_CASE("N to 1 to N graph all ports specific comps no cycles", "[Graph]") { /* - O--\ /--O - --O-- - O--/ \--O + O--\ /--O + --O-- + O--/ \--O */ const int nUnits = 5; - const std::vector connections = { - 0, 2, 0, 0, -1, -1, - 1, 2, 0, 1, -1, -1, - 2, 3, 1, 0, -1, -1, - 2, 4, 0, 0, -1, -1 - }; - cadet::util::SlicedVector adjList = cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); + const std::vector connections = {0, 2, 0, 0, -1, -1, 1, 2, 0, 1, -1, -1, + 2, 3, 1, 0, -1, -1, 2, 4, 0, 0, -1, -1}; + cadet::util::SlicedVector adjList = + cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); checkAdjacencyList(nUnits, connections, adjList); std::vector topoOrder; @@ -297,18 +252,11 @@ TEST_CASE("N to 1 to N graph all ports specific comps no cycles", "[Graph]") TEST_CASE("Linear graph specific ports twisted comps no cycles", "[Graph]") { const int nUnits = 4; - const std::vector connections = { - 0, 1, 0, 0, 0, 0, - 0, 1, 0, 0, 1, 1, - 0, 1, 0, 0, 2, 2, - 1, 2, 0, 0, 0, 2, - 1, 2, 0, 0, 1, 1, - 1, 2, 0, 0, 2, 0, - 2, 3, 0, 0, 0, 0, - 2, 3, 0, 0, 1, 1, - 2, 3, 0, 0, 2, 2 - }; - cadet::util::SlicedVector adjList = cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); + const std::vector connections = {0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 2, 2, + 1, 2, 0, 0, 0, 2, 1, 2, 0, 0, 1, 1, 1, 2, 0, 0, 2, 0, + 2, 3, 0, 0, 0, 0, 2, 3, 0, 0, 1, 1, 2, 3, 0, 0, 2, 2}; + cadet::util::SlicedVector adjList = + cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); checkAdjacencyList(nUnits, connections, adjList); std::vector topoOrder; @@ -321,26 +269,20 @@ TEST_CASE("Linear graph specific ports twisted comps no cycles", "[Graph]") TEST_CASE("Two cycles graph mixed ports all comps", "[Graph]") { /* - ___________________ - | | + ___________________ + | | 0---2--\ /--5--- | - --4-- | | + --4-- | | 1---3--/ \--6----- - |______________| + |______________| */ const int nUnits = 7; - const std::vector connections = { - 0, 2, 0, 0, -1, -1, - 1, 3, 0, 0, -1, -1, - 2, 4, -1, -1, -1, -1, - 3, 4, -1, -1, -1, -1, - 4, 5, 0, 0, -1, -1, - 4, 6, -1, -1, -1, -1, - 5, 3, 0, 1, -1, -1, - 6, 2, 0, 1, -1, -1 - }; - cadet::util::SlicedVector adjList = cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); + const std::vector connections = {0, 2, 0, 0, -1, -1, 1, 3, 0, 0, -1, -1, 2, 4, -1, -1, + -1, -1, 3, 4, -1, -1, -1, -1, 4, 5, 0, 0, -1, -1, 4, 6, + -1, -1, -1, -1, 5, 3, 0, 1, -1, -1, 6, 2, 0, 1, -1, -1}; + cadet::util::SlicedVector adjList = + cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); checkAdjacencyList(nUnits, connections, adjList); std::vector topoOrder; @@ -351,20 +293,16 @@ TEST_CASE("Two cycles graph mixed ports all comps", "[Graph]") TEST_CASE("N to 1 graph specific ports mixed comps no cycles", "[Graph]") { /* - O--\ - --O--O - O--/ + O--\ + --O--O + O--/ */ const int nUnits = 4; - const std::vector connections = { - 0, 2, 0, 0, 0, 0, - 0, 2, 0, 0, 1, 1, - 1, 2, 0, 0, 0, 2, - 1, 2, 0, 0, 0, 3, - 2, 3, 0, 0, -1, -1 - }; - cadet::util::SlicedVector adjList = cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); + const std::vector connections = {0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 1, 1, 1, 2, 0, + 0, 0, 2, 1, 2, 0, 0, 0, 3, 2, 3, 0, 0, -1, -1}; + cadet::util::SlicedVector adjList = + cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); checkAdjacencyList(nUnits, connections, adjList); std::vector topoOrder; @@ -377,20 +315,17 @@ TEST_CASE("N to 1 graph specific ports mixed comps no cycles", "[Graph]") TEST_CASE("Self loop graph all ports all comps with cycles", "[Graph]") { /* - O--\ - --O--O /-O-\ - O--/ \ / - --- + O--\ + --O--O /-O-\ + O--/ \ / + --- */ const int nUnits = 5; - const std::vector connections = { - 0, 3, -1, -1, -1, -1, - 1, 3, -1, -1, -1, -1, - 2, 2, -1, -1, -1, -1, - 3, 4, -1, -1, -1, -1 - }; - cadet::util::SlicedVector adjList = cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); + const std::vector connections = {0, 3, -1, -1, -1, -1, 1, 3, -1, -1, -1, -1, + 2, 2, -1, -1, -1, -1, 3, 4, -1, -1, -1, -1}; + cadet::util::SlicedVector adjList = + cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); checkAdjacencyList(nUnits, connections, adjList); std::vector topoOrder; @@ -401,21 +336,17 @@ TEST_CASE("Self loop graph all ports all comps with cycles", "[Graph]") TEST_CASE("Two connected components graph all ports all comps no cycles", "[Graph]") { /* - O--\ - --O--O O--O--O O - O--/ + O--\ + --O--O O--O--O O + O--/ */ const int nUnits = 8; - const std::vector connections = { - 0, 2, -1, -1, -1, -1, - 1, 2, -1, -1, -1, -1, - 2, 3, -1, -1, -1, -1, - - 4, 5, -1, -1, -1, -1, - 5, 6, -1, -1, -1, -1 - }; - cadet::util::SlicedVector adjList = cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); + const std::vector connections = {0, 2, -1, -1, -1, -1, 1, 2, -1, -1, -1, -1, 2, 3, -1, -1, -1, -1, + + 4, 5, -1, -1, -1, -1, 5, 6, -1, -1, -1, -1}; + cadet::util::SlicedVector adjList = + cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); checkAdjacencyList(nUnits, connections, adjList); std::vector topoOrder; @@ -433,14 +364,10 @@ TEST_CASE("Multi connected components graph all ports all comps no cycles", "[Gr */ const int nUnits = 9; - const std::vector connections = { - 4, 1, -1, -1, -1, -1, - 0, 3, -1, -1, -1, -1, - 1, 7, -1, -1, -1, -1, - 3, 4, -1, -1, -1, -1, - 7, 2, -1, -1, -1, -1 - }; - cadet::util::SlicedVector adjList = cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); + const std::vector connections = {4, 1, -1, -1, -1, -1, 0, 3, -1, -1, -1, -1, 1, 7, -1, + -1, -1, -1, 3, 4, -1, -1, -1, -1, 7, 2, -1, -1, -1, -1}; + cadet::util::SlicedVector adjList = + cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); checkAdjacencyList(nUnits, connections, adjList); std::vector topoOrder; @@ -458,15 +385,10 @@ TEST_CASE("Multi connected components graph all ports all comps no cycles 2", "[ */ const int nUnits = 9; - const std::vector connections = { - 1, 7, -1, -1, -1, -1, - 0, 5, -1, -1, -1, -1, - 5, 6, -1, -1, -1, -1, - 6, 1, -1, -1, -1, -1, - 8, 2, -1, -1, -1, -1, - 7, 8, -1, -1, -1, -1 - }; - cadet::util::SlicedVector adjList = cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); + const std::vector connections = {1, 7, -1, -1, -1, -1, 0, 5, -1, -1, -1, -1, 5, 6, -1, -1, -1, -1, + 6, 1, -1, -1, -1, -1, 8, 2, -1, -1, -1, -1, 7, 8, -1, -1, -1, -1}; + cadet::util::SlicedVector adjList = + cadet::graph::adjacencyListFromConnectionList(connections.data(), nUnits, connections.size() / 6); checkAdjacencyList(nUnits, connections, adjList); std::vector topoOrder; diff --git a/test/Histogram.cpp b/test/Histogram.cpp index 810adcbae..3efa10db0 100644 --- a/test/Histogram.cpp +++ b/test/Histogram.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -21,105 +21,106 @@ namespace cadet namespace test { - void printHistogram(const std::vector& data, int nBins, unsigned int skip, unsigned int lane) - { - double minVal = std::numeric_limits::infinity(); - double maxVal = -std::numeric_limits::infinity(); - unsigned int nValid = 0; +void printHistogram(const std::vector& data, int nBins, unsigned int skip, unsigned int lane) +{ + double minVal = std::numeric_limits::infinity(); + double maxVal = -std::numeric_limits::infinity(); + unsigned int nValid = 0; - // Find min and max - for (std::size_t i = 0; i < data.size() / skip; ++i) + // Find min and max + for (std::size_t i = 0; i < data.size() / skip; ++i) + { + const double v = std::log10(data[i * skip + lane]); + if (std::isfinite(v)) { - const double v = std::log10(data[i * skip + lane]); - if (std::isfinite(v)) - { - ++nValid; - minVal = std::min(minVal, v); - maxVal = std::max(maxVal, v); - } + ++nValid; + minVal = std::min(minVal, v); + maxVal = std::max(maxVal, v); } + } - // Do binning - const double binWidth = (maxVal - minVal) / static_cast(nBins); - std::vector bins(nBins, 0); - for (std::size_t i = 0; i < data.size() / skip; ++i) - { - const double v = std::log10(data[i * skip + lane]); - if (!std::isfinite(v)) - continue; - - if (v == maxVal) - ++bins.back(); - else - ++bins[static_cast((v - minVal) / binWidth)]; - } + // Do binning + const double binWidth = (maxVal - minVal) / static_cast(nBins); + std::vector bins(nBins, 0); + for (std::size_t i = 0; i < data.size() / skip; ++i) + { + const double v = std::log10(data[i * skip + lane]); + if (!std::isfinite(v)) + continue; + + if (v == maxVal) + ++bins.back(); + else + ++bins[static_cast((v - minVal) / binWidth)]; + } - const unsigned int consoleWidth = 72; - const unsigned int width = consoleWidth - (6 + 3 + 6 + 2); + const unsigned int consoleWidth = 72; + const unsigned int width = consoleWidth - (6 + 3 + 6 + 2); - // Plot - for (int i = 0; i < nBins; ++i) - { - const double low = minVal + binWidth * i; - const double high = minVal + binWidth * (i + 1); - std::cout << std::setw(6) << std::setprecision(2) << std::fixed << low << " = " << std::setw(6) << std::setprecision(2) << std::fixed << high; - std::cout << " |"; + // Plot + for (int i = 0; i < nBins; ++i) + { + const double low = minVal + binWidth * i; + const double high = minVal + binWidth * (i + 1); + std::cout << std::setw(6) << std::setprecision(2) << std::fixed << low << " = " << std::setw(6) + << std::setprecision(2) << std::fixed << high; + std::cout << " |"; - const int len = static_cast(std::ceil(static_cast(width * bins[i]) / static_cast(nValid))); + const int len = static_cast(std::ceil(static_cast(width * bins[i]) / static_cast(nValid))); - for (int j = 0; j < len; ++j) - std::cout << "*"; - std::cout << "\n"; - } - std::cout << " N = " << (data.size() / skip) << " (" << nValid << "), * = " << std::ceil(static_cast(nValid) / static_cast(width)) << std::endl; + for (int j = 0; j < len; ++j) + std::cout << "*"; + std::cout << "\n"; } + std::cout << " N = " << (data.size() / skip) << " (" << nValid + << "), * = " << std::ceil(static_cast(nValid) / static_cast(width)) << std::endl; +} +template inline double Lerp(T v0, T v1, T t) +{ + return (1 - t) * v0 + t * v1; +} - template - inline double Lerp(T v0, T v1, T t) - { - return (1 - t) * v0 + t * v1; - } +template +std::vector quantile(std::vector& data, const std::vector& probs, unsigned int skip, unsigned int lane) +{ + std::vector d(data.size() / skip); + for (std::size_t i = 0; i < d.size(); ++i) + d[i] = data[i * skip + lane]; - template - std::vector quantile(std::vector& data, const std::vector& probs, unsigned int skip, unsigned int lane) + std::sort(d.begin(), d.end()); + std::vector quantiles; + quantiles.reserve(probs.size()); + + for (std::size_t i = 0; i < probs.size(); ++i) { - std::vector d(data.size() / skip); - for (std::size_t i = 0; i < d.size(); ++i) - d[i] = data[i * skip + lane]; + const T poi = Lerp(-0.5, d.size() - 0.5, probs[i]); - std::sort(d.begin(), d.end()); - std::vector quantiles; - quantiles.reserve(probs.size()); + std::size_t left = std::max(int64_t(std::floor(poi)), int64_t(0)); + std::size_t right = std::min(int64_t(std::ceil(poi)), int64_t(d.size() - 1)); - for (std::size_t i = 0; i < probs.size(); ++i) - { - const T poi = Lerp(-0.5, d.size() - 0.5, probs[i]); + const T datLeft = d.at(left); + const T datRight = d.at(right); - std::size_t left = std::max(int64_t(std::floor(poi)), int64_t(0)); - std::size_t right = std::min(int64_t(std::ceil(poi)), int64_t(d.size() - 1)); + const T quantile = Lerp(datLeft, datRight, poi - left); - const T datLeft = d.at(left); - const T datRight = d.at(right); - - const T quantile = Lerp(datLeft, datRight, poi - left); + quantiles.push_back(quantile); + } - quantiles.push_back(quantile); - } + return quantiles; +} - return quantiles; - } +void printQuantiles(std::vector& data, unsigned int skip, unsigned int lane) +{ + const std::vector probs = {0.5, 0.66, 0.75, 0.9, 0.925, 0.95, 0.975}; + const std::vector quants = quantile(data, probs, skip, lane); - void printQuantiles(std::vector& data, unsigned int skip, unsigned int lane) + for (std::size_t i = 0; i < probs.size(); ++i) { - const std::vector probs = {0.5, 0.66, 0.75, 0.9, 0.925, 0.95, 0.975}; - const std::vector quants = quantile(data, probs, skip, lane); - - for (std::size_t i = 0; i < probs.size(); ++i) - { - std::cout << " " << std::setw(4) << std::setprecision(1) << std::fixed << probs[i] * 100.0 << " => " << std::setw(10) << std::setprecision(3) << std::scientific << quants[i] << "\n"; - } - std::cout << std::endl; + std::cout << " " << std::setw(4) << std::setprecision(1) << std::fixed << probs[i] * 100.0 << " => " + << std::setw(10) << std::setprecision(3) << std::scientific << quants[i] << "\n"; } + std::cout << std::endl; } -} +} // namespace test +} // namespace cadet diff --git a/test/JacobianHelper.hpp b/test/JacobianHelper.hpp index f38f7a4d2..f55fb74d4 100644 --- a/test/JacobianHelper.hpp +++ b/test/JacobianHelper.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines helper functions for checking Jacobians. */ @@ -32,7 +32,7 @@ namespace cadet namespace util { - class ThreadLocalStorage; +class ThreadLocalStorage; } namespace test @@ -53,8 +53,9 @@ namespace test * @param [in] absTol Absolute error tolerance * @param [in] relTol Relative error tolerance */ -inline void compareJacobian(const std::function multJacA, const std::function multJacB, double* dir, double* colA, double* colB, - unsigned int n, unsigned int m, double absTol, double relTol) +inline void compareJacobian(const std::function multJacA, + const std::function multJacB, double* dir, double* colA, + double* colB, unsigned int n, unsigned int m, double absTol, double relTol) { std::fill(dir, dir + n, 0.0); for (unsigned int col = 0; col < n; ++col) @@ -88,8 +89,9 @@ inline void compareJacobian(const std::function mu * @param [in] n Number of DOFs in the model * @param [in] m Number of equations in the model */ -inline void compareJacobian(const std::function multJacA, const std::function multJacB, double* dir, double* colA, double* colB, - unsigned int n, unsigned int m) +inline void compareJacobian(const std::function multJacA, + const std::function multJacB, double* dir, double* colA, + double* colB, unsigned int n, unsigned int m) { compareJacobian(multJacA, multJacB, dir, colA, colB, n, m, RelApprox::defaultEpsilon(), RelApprox::defaultMargin()); } @@ -106,7 +108,9 @@ inline void compareJacobian(const std::function mu * @param [in] colB Memory for Jacobian column * @param [in] n Number of DOFs in the model */ -inline void compareJacobian(const std::function multJacA, const std::function multJacB, double* dir, double* colA, double* colB, unsigned int n) +inline void compareJacobian(const std::function multJacA, + const std::function multJacB, double* dir, double* colA, + double* colB, unsigned int n) { compareJacobian(multJacA, multJacB, dir, colA, colB, n, n); } @@ -124,13 +128,17 @@ inline void compareJacobian(const std::function mu * @param [in] absTol Absolute error tolerance * @param [in] relTol Relative error tolerance */ -inline void compareJacobian(cadet::IUnitOperation* modelA, cadet::IUnitOperation* modelB, double const* y, double const* yDot, double* dir, double* colA, double* colB, double absTol, double relTol) +inline void compareJacobian(cadet::IUnitOperation* modelA, cadet::IUnitOperation* modelB, double const* y, + double const* yDot, double* dir, double* colA, double* colB, double absTol, double relTol) { compareJacobian( - [=](double const* lDir, double* res) -> void { modelA->multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y, yDot}, lDir, 1.0, 0.0, res); }, - [=](double const* lDir, double* res) -> void { modelB->multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y, yDot}, lDir, 1.0, 0.0, res); }, - dir, colA, colB, modelA->numDofs(), modelA->numDofs(), absTol, relTol - ); + [=](double const* lDir, double* res) -> void { + modelA->multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y, yDot}, lDir, 1.0, 0.0, res); + }, + [=](double const* lDir, double* res) -> void { + modelB->multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y, yDot}, lDir, 1.0, 0.0, res); + }, + dir, colA, colB, modelA->numDofs(), modelA->numDofs(), absTol, relTol); } /** @@ -144,7 +152,8 @@ inline void compareJacobian(cadet::IUnitOperation* modelA, cadet::IUnitOperation * @param [in] colA Memory for Jacobian column of @p modelA * @param [in] colB Memory for Jacobian column of @p modelB */ -inline void compareJacobian(cadet::IUnitOperation* modelA, cadet::IUnitOperation* modelB, double const* y, double const* yDot, double* dir, double* colA, double* colB) +inline void compareJacobian(cadet::IUnitOperation* modelA, cadet::IUnitOperation* modelB, double const* y, + double const* yDot, double* dir, double* colA, double* colB) { compareJacobian(modelA, modelB, y, yDot, dir, colA, colB, RelApprox::defaultEpsilon(), RelApprox::defaultMargin()); } @@ -155,7 +164,7 @@ inline void compareJacobian(cadet::IUnitOperation* modelA, cadet::IUnitOperation * are compared column by column. A column is extracted by calling multiplyJacobian(). * Only the bottom macro row and the right macro column of the arrow head are compared. * @param [in] residual Function that returns a residual vector - * @param [in] multiplyJacobian Function that multiplies the (time derivative) Jacobian with a vector + * @param [in] multiplyJacobian Function that multiplies the (time derivative) Jacobian with a vector * @param [in] y State vector * @param [in] dir Memory for computing finite differences * @param [in] colA Memory for Jacobian column @@ -166,8 +175,11 @@ inline void compareJacobian(cadet::IUnitOperation* modelA, cadet::IUnitOperation * @param [in] absTol Absolute error tolerance * @param [in] relTol Relative error tolerance */ -inline void compareJacobianArrowHeadFD(const std::function& residual, const std::function& multiplyJacobian, double const* y, double* dir, - double* colA, double* colB, unsigned int n, unsigned int offset, double h = 1e-6, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0) +inline void compareJacobianArrowHeadFD(const std::function& residual, + const std::function& multiplyJacobian, + double const* y, double* dir, double* colA, double* colB, unsigned int n, + unsigned int offset, double h = 1e-6, double absTol = 0.0, + double relTol = std::numeric_limits::epsilon() * 100.0) { // Compare bottom macro row until we hit right macro column for (unsigned int col = 0; col < offset; ++col) @@ -253,7 +265,7 @@ inline void compareJacobianArrowHeadFD(const std::function& residual, const std::function& multiplyJacobian, double const* y, double* dir, - double* colA, double* colB, unsigned int n, unsigned int m, double h = 1e-6, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0) +inline void compareJacobianFD(const std::function& residual, + const std::function& multiplyJacobian, double const* y, + double* dir, double* colA, double* colB, unsigned int n, unsigned int m, double h = 1e-6, + double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0) { for (unsigned int col = 0; col < n; ++col) { @@ -311,7 +325,7 @@ inline void compareJacobianFD(const std::function& * @details Uses finite differences to determine the Jacobian. The two Jacobians * are compared column by column. A column is extracted by calling multiplyJacobian(). * @param [in] residual Function that returns a residual vector - * @param [in] multiplyJacobian Function that multiplies the (time derivative) Jacobian with a vector + * @param [in] multiplyJacobian Function that multiplies the (time derivative) Jacobian with a vector * @param [in] y State vector * @param [in] dir Memory for computing finite differences * @param [in] colA Memory for Jacobian column @@ -321,8 +335,10 @@ inline void compareJacobianFD(const std::function& * @param [in] absTol Absolute error tolerance * @param [in] relTol Relative error tolerance */ -inline void compareJacobianFD(const std::function& residual, const std::function& multiplyJacobian, double const* y, double* dir, - double* colA, double* colB, unsigned int n, double h = 1e-6, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0) +inline void compareJacobianFD(const std::function& residual, + const std::function& multiplyJacobian, double const* y, + double* dir, double* colA, double* colB, unsigned int n, double h = 1e-6, + double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0) { compareJacobianFD(residual, multiplyJacobian, y, dir, colA, colB, n, n, h, absTol, relTol); } @@ -345,12 +361,18 @@ inline void compareJacobianFD(const std::function& * @param [in] absTol Absolute error tolerance * @param [in] relTol Relative error tolerance */ -inline void compareJacobianFD(cadet::IUnitOperation* modelA, cadet::IUnitOperation* modelB, double const* y, double const* yDot, double* dir, double* colA, double* colB, cadet::util::ThreadLocalStorage& tls, - double h = 1e-6, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0) +inline void compareJacobianFD(cadet::IUnitOperation* modelA, cadet::IUnitOperation* modelB, double const* y, + double const* yDot, double* dir, double* colA, double* colB, + cadet::util::ThreadLocalStorage& tls, double h = 1e-6, double absTol = 0.0, + double relTol = std::numeric_limits::epsilon() * 100.0) { compareJacobianFD( - [=, &tls](double const* lDir, double* res) -> void { modelA->residual(SimulationTime{0.0, 0u}, ConstSimulationState{lDir, yDot}, res, tls); }, - [=](double const* lDir, double* res) -> void { modelB->multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y, yDot}, lDir, 1.0, 0.0, res); }, + [=, &tls](double const* lDir, double* res) -> void { + modelA->residual(SimulationTime{0.0, 0u}, ConstSimulationState{lDir, yDot}, res, tls); + }, + [=](double const* lDir, double* res) -> void { + modelB->multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y, yDot}, lDir, 1.0, 0.0, res); + }, y, dir, colA, colB, modelA->numDofs(), modelA->numDofs(), h, absTol, relTol); } @@ -359,7 +381,7 @@ inline void compareJacobianFD(cadet::IUnitOperation* modelA, cadet::IUnitOperati * @details Uses finite differences to determine the Jacobian structure. The Jacobians * are compared column by column. A column is extracted by calling multiplyWithJacobian(). * @param [in] residual Function that returns a residual vector - * @param [in] multiplyJacobian Function that multiplies the Jacobian with a vector + * @param [in] multiplyJacobian Function that multiplies the Jacobian with a vector * @param [in] y State vector * @param [in] dir Memory for computing finite differences * @param [in] colA Memory for Jacobian column @@ -368,8 +390,10 @@ inline void compareJacobianFD(cadet::IUnitOperation* modelA, cadet::IUnitOperati * @param [in] m Number of equations in the model * @param [in] absTol absolute tolerance when comparing the sign in the pattern */ -inline void checkJacobianPatternFD(const std::function& residual, const std::function& multiplyJacobian, double const* y, double* dir, - double* colA, double* colB, unsigned int n, unsigned int m, const double absTol = 0.0) +inline void checkJacobianPatternFD(const std::function& residual, + const std::function& multiplyJacobian, double const* y, + double* dir, double* colA, double* colB, unsigned int n, unsigned int m, + const double absTol = 0.0) { const double h = 1e-5; for (unsigned int col = 0; col < n; ++col) @@ -423,7 +447,7 @@ inline void checkJacobianPatternFD(const std::function& residual, const std::function& multiplyJacobian, double const* y, - double* dir, double* colA, double* colB, unsigned int n, const double absTol=0.0) +inline void checkJacobianPatternFD(const std::function& residual, + const std::function& multiplyJacobian, double const* y, + double* dir, double* colA, double* colB, unsigned int n, const double absTol = 0.0) { checkJacobianPatternFD(residual, multiplyJacobian, y, dir, colA, colB, n, n, absTol); } @@ -452,13 +477,18 @@ inline void checkJacobianPatternFD(const std::function void { modelA->residual(SimulationTime{0.0, 0u}, ConstSimulationState{lDir, yDot}, res, tls); }, - [=](double const* lDir, double* res) -> void { modelB->multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y, yDot}, lDir, 1.0, 0.0, res); }, - y, dir, colA, colB, modelA->numDofs(), modelA->numDofs(), - absTol); + [=, &tls](double const* lDir, double* res) -> void { + modelA->residual(SimulationTime{0.0, 0u}, ConstSimulationState{lDir, yDot}, res, tls); + }, + [=](double const* lDir, double* res) -> void { + modelB->multiplyWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y, yDot}, lDir, 1.0, 0.0, res); + }, + y, dir, colA, colB, modelA->numDofs(), modelA->numDofs(), absTol); } /** @@ -472,7 +502,8 @@ inline void checkJacobianPatternFD(cadet::IUnitOperation* modelA, cadet::IUnitOp * @param [in] colA Memory for Jacobian column of @p modelA * @param [in] colB Memory for Jacobian column of @p modelB */ -inline void compareTimeDerivativeJacobian(cadet::IUnitOperation* modelA, cadet::IUnitOperation* modelB, double const* y, double const* yDot, double* dir, double* colA, double* colB) +inline void compareTimeDerivativeJacobian(cadet::IUnitOperation* modelA, cadet::IUnitOperation* modelB, double const* y, + double const* yDot, double* dir, double* colA, double* colB) { const unsigned int n = modelA->numDofs(); std::fill(dir, dir + n, 0.0); @@ -491,7 +522,7 @@ inline void compareTimeDerivativeJacobian(cadet::IUnitOperation* modelA, cadet:: } dir[col] = 0.0; - } + } } /** @@ -511,16 +542,23 @@ inline void compareTimeDerivativeJacobian(cadet::IUnitOperation* modelA, cadet:: * @param [in] absTol Absolute error tolerance * @param [in] relTol Relative error tolerance */ -inline void compareTimeDerivativeJacobianFD(cadet::IUnitOperation* modelA, cadet::IUnitOperation* modelB, double const* y, double const* yDot, double* dir, double* colA, double* colB, - cadet::util::ThreadLocalStorage& tls, double h = 1e-6, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0) +inline void compareTimeDerivativeJacobianFD(cadet::IUnitOperation* modelA, cadet::IUnitOperation* modelB, + double const* y, double const* yDot, double* dir, double* colA, + double* colB, cadet::util::ThreadLocalStorage& tls, double h = 1e-6, + double absTol = 0.0, + double relTol = std::numeric_limits::epsilon() * 100.0) { compareJacobianFD( - [=, &tls](double const* lDir, double* res) -> void { modelA->residual(SimulationTime{0.0, 0u}, ConstSimulationState{y, lDir}, res, tls); }, - [=](double const* lDir, double* res) -> void { modelB->multiplyWithDerivativeJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y, yDot}, lDir, res); }, + [=, &tls](double const* lDir, double* res) -> void { + modelA->residual(SimulationTime{0.0, 0u}, ConstSimulationState{y, lDir}, res, tls); + }, + [=](double const* lDir, double* res) -> void { + modelB->multiplyWithDerivativeJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y, yDot}, lDir, res); + }, yDot, dir, colA, colB, modelA->numDofs(), modelA->numDofs(), h, absTol, relTol); } } // namespace test } // namespace cadet -#endif // CADETTEST_JACOBIANHELPER_HPP_ +#endif // CADETTEST_JACOBIANHELPER_HPP_ diff --git a/test/JsonTestModels.cpp b/test/JsonTestModels.cpp index c75b5ca51..a5dca17c9 100644 --- a/test/JsonTestModels.cpp +++ b/test/JsonTestModels.cpp @@ -51,9 +51,9 @@ json createColumnWithSMAJson(const std::string& uoType, const std::string& spati if (uoType == "MULTI_CHANNEL_TRANSPORT") { - config["CHANNEL_CROSS_SECTION_AREAS"] = { 1.0 }; + config["CHANNEL_CROSS_SECTION_AREAS"] = {1.0}; // Channel exchange - config["EXCHANGE_MATRIX"] = { 0.0 }; + config["EXCHANGE_MATRIX"] = {0.0}; } // Initial conditions @@ -62,7 +62,7 @@ json createColumnWithSMAJson(const std::string& uoType, const std::string& spati // Adsorption config["ADSORPTION_MODEL"] = std::string("STERIC_MASS_ACTION"); - config["NBOUND"] = { 1, 1, 1, 1 }; + config["NBOUND"] = {1, 1, 1, 1}; { json ads; ads["IS_KINETIC"] = 1; @@ -127,52 +127,52 @@ json createColumnWithSMAJson(const std::string& uoType, const std::string& spati return config; -/* - return R"json({ - "UNIT_TYPE": "GENERAL_RATE_MODEL", - "NCOMP": 4, - "VELOCITY": 5.75e-4, - "COL_DISPERSION": 5.75e-8, - "FILM_DIFFUSION": [6.9e-6, 6.9e-6, 6.9e-6, 6.9e-6], - "PAR_DIFFUSION": [7e-10, 6.07e-11, 6.07e-11, 6.07e-11], - "PAR_SURFDIFFUSION": [0.0, 0.0, 0.0, 0.0], - "COL_LENGTH": 0.014, - "PAR_RADIUS": 4.5e-5, - "COL_POROSITY": 0.37, - "PAR_POROSITY": 0.75, - "TOTAL_POROSITY": 0.8425, - "INIT_C": [50.0, 0.0, 0.0, 0.0], - "INIT_Q": [1.2e3, 0.0, 0.0, 0.0], - "ADSORPTION_MODEL": "STERIC_MASS_ACTION", - "NBOUND": [1, 1, 1, 1], - "adsorption": - { - "IS_KINETIC": 1, - "SMA_LAMBDA": 1.2e3, - "SMA_KA": [0.0, 35.5, 1.59, 7.7], - "SMA_KD": [0.0, 1000.0, 1000.0, 1000.0], - "SMA_NU": [0.0, 4.7, 5.29, 3.7], - "SMA_SIGMA": [0.0, 11.83, 10.6, 10.0] - }, - "discretization": - { - "NCOL": 16, - "NPAR": 4, - "PAR_DISC_TYPE": "EQUIDISTANT_PAR", - "USE_ANALYTIC_JACOBIAN": true, - "MAX_KRYLOV": 0, - "GS_TYPE": 1, - "MAX_RESTARTS": 10, - "SCHUR_SAFETY": 1e-8, - "weno": + /* + return R"json({ + "UNIT_TYPE": "GENERAL_RATE_MODEL", + "NCOMP": 4, + "VELOCITY": 5.75e-4, + "COL_DISPERSION": 5.75e-8, + "FILM_DIFFUSION": [6.9e-6, 6.9e-6, 6.9e-6, 6.9e-6], + "PAR_DIFFUSION": [7e-10, 6.07e-11, 6.07e-11, 6.07e-11], + "PAR_SURFDIFFUSION": [0.0, 0.0, 0.0, 0.0], + "COL_LENGTH": 0.014, + "PAR_RADIUS": 4.5e-5, + "COL_POROSITY": 0.37, + "PAR_POROSITY": 0.75, + "TOTAL_POROSITY": 0.8425, + "INIT_C": [50.0, 0.0, 0.0, 0.0], + "INIT_Q": [1.2e3, 0.0, 0.0, 0.0], + "ADSORPTION_MODEL": "STERIC_MASS_ACTION", + "NBOUND": [1, 1, 1, 1], + "adsorption": + { + "IS_KINETIC": 1, + "SMA_LAMBDA": 1.2e3, + "SMA_KA": [0.0, 35.5, 1.59, 7.7], + "SMA_KD": [0.0, 1000.0, 1000.0, 1000.0], + "SMA_NU": [0.0, 4.7, 5.29, 3.7], + "SMA_SIGMA": [0.0, 11.83, 10.6, 10.0] + }, + "discretization": { - "WENO_ORDER": 3, - "BOUNDARY_MODEL": 0, - "WENO_EPS": 1e-10 + "NCOL": 16, + "NPAR": 4, + "PAR_DISC_TYPE": "EQUIDISTANT_PAR", + "USE_ANALYTIC_JACOBIAN": true, + "MAX_KRYLOV": 0, + "GS_TYPE": 1, + "MAX_RESTARTS": 10, + "SCHUR_SAFETY": 1e-8, + "weno": + { + "WENO_ORDER": 3, + "BOUNDARY_MODEL": 0, + "WENO_EPS": 1e-10 + } } - } - })json"; -*/ + })json"; + */ } cadet::JsonParameterProvider createColumnWithSMA(const std::string& uoType, const std::string& spatialMethod) @@ -214,9 +214,10 @@ json createColumnWithTwoCompLinearJson(const std::string& uoType, const std::str if (uoType == "MULTI_CHANNEL_TRANSPORT") { - config["CHANNEL_CROSS_SECTION_AREAS"] = { 1.0, 1.0, 1.0 }; + config["CHANNEL_CROSS_SECTION_AREAS"] = {1.0, 1.0, 1.0}; // Channel exchange - config["EXCHANGE_MATRIX"] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; + config["EXCHANGE_MATRIX"] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; } // Initial conditions @@ -225,7 +226,7 @@ json createColumnWithTwoCompLinearJson(const std::string& uoType, const std::str // Adsorption config["ADSORPTION_MODEL"] = std::string("LINEAR"); - config["NBOUND"] = { 1, 1 }; + config["NBOUND"] = {1, 1}; { json ads; ads["IS_KINETIC"] = 1; @@ -287,7 +288,8 @@ json createColumnWithTwoCompLinearJson(const std::string& uoType, const std::str return config; } -cadet::JsonParameterProvider createColumnWithTwoCompLinearBinding(const std::string& uoType, const std::string& spatialMethod) +cadet::JsonParameterProvider createColumnWithTwoCompLinearBinding(const std::string& uoType, + const std::string& spatialMethod) { return cadet::JsonParameterProvider(createColumnWithTwoCompLinearJson(uoType, spatialMethod)); } @@ -362,8 +364,8 @@ json createLWEJson(const std::string& uoType, const std::string& spatialMethod) // Connection list is 3x7 since we have 1 connection between // the two unit operations with 3 ports (and we need to have 7 columns) sw["CONNECTIONS"] = {1.0, 0.0, 0.0, 0.0, -1.0, -1.0, 7.42637597e-09, - 1.0, 0.0, 0.0, 1.0, -1.0, -1.0, 2.22791279e-08, - 1.0, 0.0, 0.0, 2.0, -1.0, -1.0, 3.71318798e-08}; + 1.0, 0.0, 0.0, 1.0, -1.0, -1.0, 2.22791279e-08, + 1.0, 0.0, 0.0, 2.0, -1.0, -1.0, 3.71318798e-08}; // Connections: From unit operation 1 port 0 // to unit operation 0 port 0, // connect component -1 (i.e., all components) @@ -490,7 +492,8 @@ cadet::JsonParameterProvider createLWE(const std::string& uoType, const std::str return cadet::JsonParameterProvider(createLWEJson(uoType, spatialMethod)); } -cadet::JsonParameterProvider createPulseInjectionColumn(const std::string& uoType, const std::string& spatialMethod, bool dynamicBinding) +cadet::JsonParameterProvider createPulseInjectionColumn(const std::string& uoType, const std::string& spatialMethod, + bool dynamicBinding) { json config; // Model @@ -533,9 +536,9 @@ cadet::JsonParameterProvider createPulseInjectionColumn(const std::string& uoTyp grm["TOTAL_POROSITY"] = 0.37 + (1.0 - 0.37) * 0.75; if (uoType == "MULTI_CHANNEL_TRANSPORT") { - grm["CHANNEL_CROSS_SECTION_AREAS"] = { 1.0, 1.0, 1.0 }; + grm["CHANNEL_CROSS_SECTION_AREAS"] = {1.0, 1.0, 1.0}; // Channel exchange - grm["EXCHANGE_MATRIX"] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; + grm["EXCHANGE_MATRIX"] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; } // Initial conditions @@ -545,7 +548,7 @@ cadet::JsonParameterProvider createPulseInjectionColumn(const std::string& uoTyp // Adsorption { grm["ADSORPTION_MODEL"] = std::string("LINEAR"); - grm["NBOUND"] = { 1 }; + grm["NBOUND"] = {1}; json ads; ads["IS_KINETIC"] = (dynamicBinding ? 1 : 0); @@ -652,8 +655,8 @@ cadet::JsonParameterProvider createPulseInjectionColumn(const std::string& uoTyp // Connection list is 3x7 since we have 1 connection between // the two unit operations with 3 ports (and we need to have 7 columns) sw["CONNECTIONS"] = {1.0, 0.0, 0.0, 0.0, -1.0, -1.0, 7.42637597e-09, - 1.0, 0.0, 0.0, 1.0, -1.0, -1.0, 2.22791279e-08, - 1.0, 0.0, 0.0, 2.0, -1.0, -1.0, 3.71318798e-08}; + 1.0, 0.0, 0.0, 1.0, -1.0, -1.0, 2.22791279e-08, + 1.0, 0.0, 0.0, 2.0, -1.0, -1.0, 3.71318798e-08}; // Connections: From unit operation 1 port 0 // to unit operation 0 port 0, // connect component -1 (i.e., all components) @@ -760,7 +763,8 @@ cadet::JsonParameterProvider createPulseInjectionColumn(const std::string& uoTyp return cadet::JsonParameterProvider(config); } -json createLinearBenchmarkColumnJson(bool dynamicBinding, bool nonBinding, const std::string& uoType, const std::string& spatialMethod) +json createLinearBenchmarkColumnJson(bool dynamicBinding, bool nonBinding, const std::string& uoType, + const std::string& spatialMethod) { json grm; grm["UNIT_TYPE"] = uoType; @@ -794,25 +798,25 @@ json createLinearBenchmarkColumnJson(bool dynamicBinding, bool nonBinding, const if (uoType == "MULTI_CHANNEL_TRANSPORT") { - grm["CHANNEL_CROSS_SECTION_AREAS"] = { 1.0, 1.0, 1.0 }; + grm["CHANNEL_CROSS_SECTION_AREAS"] = {1.0, 1.0, 1.0}; // Channel exchange - grm["EXCHANGE_MATRIX"] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; + grm["EXCHANGE_MATRIX"] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; } // Initial conditions grm["INIT_C"] = {0.0}; grm["INIT_Q"] = {0.0}; - // Adsorption - if (nonBinding) - { - grm["ADSORPTION_MODEL"] = std::string("NONE"); - grm["NBOUND"] = { 0 }; - } - else - { - grm["ADSORPTION_MODEL"] = std::string("LINEAR"); - grm["NBOUND"] = { 1 }; + // Adsorption + if (nonBinding) + { + grm["ADSORPTION_MODEL"] = std::string("NONE"); + grm["NBOUND"] = {0}; + } + else + { + grm["ADSORPTION_MODEL"] = std::string("LINEAR"); + grm["NBOUND"] = {1}; json ads; ads["IS_KINETIC"] = (dynamicBinding ? 1 : 0); @@ -821,37 +825,37 @@ json createLinearBenchmarkColumnJson(bool dynamicBinding, bool nonBinding, const grm["adsorption"] = ads; } - // Discretization - { - json disc; - disc["SPATIAL_METHOD"] = spatialMethod; + // Discretization + { + json disc; + disc["SPATIAL_METHOD"] = spatialMethod; - if (spatialMethod == "FV") - { - disc["NCOL"] = 512; - disc["NPAR"] = 4; + if (spatialMethod == "FV") + { + disc["NCOL"] = 512; + disc["NPAR"] = 4; - disc["MAX_KRYLOV"] = 0; - disc["GS_TYPE"] = 1; - disc["MAX_RESTARTS"] = 10; - disc["SCHUR_SAFETY"] = 1e-8; - { - json weno; - weno["WENO_ORDER"] = 3; - weno["BOUNDARY_MODEL"] = 0; - weno["WENO_EPS"] = 1e-10; - disc["weno"] = weno; - } - } - else if (spatialMethod == "DG") - { - disc["EXACT_INTEGRATION"] = 0; - disc["POLYDEG"] = 5; - disc["NELEM"] = 15; - disc["PAR_EXACT_INTEGRATION"] = 1; - disc["PAR_POLYDEG"] = 3; - disc["PAR_NELEM"] = 1; - } + disc["MAX_KRYLOV"] = 0; + disc["GS_TYPE"] = 1; + disc["MAX_RESTARTS"] = 10; + disc["SCHUR_SAFETY"] = 1e-8; + { + json weno; + weno["WENO_ORDER"] = 3; + weno["BOUNDARY_MODEL"] = 0; + weno["WENO_EPS"] = 1e-10; + disc["weno"] = weno; + } + } + else if (spatialMethod == "DG") + { + disc["EXACT_INTEGRATION"] = 0; + disc["POLYDEG"] = 5; + disc["NELEM"] = 15; + disc["PAR_EXACT_INTEGRATION"] = 1; + disc["PAR_POLYDEG"] = 3; + disc["PAR_NELEM"] = 1; + } if (uoType == "GENERAL_RATE_MODEL_2D") { @@ -861,20 +865,23 @@ json createLinearBenchmarkColumnJson(bool dynamicBinding, bool nonBinding, const disc["PAR_DISC_TYPE"] = std::string("EQUIDISTANT_PAR"); - disc["USE_ANALYTIC_JACOBIAN"] = true; + disc["USE_ANALYTIC_JACOBIAN"] = true; - grm["discretization"] = disc; - } + grm["discretization"] = disc; + } return grm; } -cadet::JsonParameterProvider createColumnLinearBenchmark(bool dynamicBinding, bool nonBinding, const std::string& uoType, const std::string& spatialMethod) +cadet::JsonParameterProvider createColumnLinearBenchmark(bool dynamicBinding, bool nonBinding, + const std::string& uoType, const std::string& spatialMethod) { - return cadet::JsonParameterProvider(createLinearBenchmarkColumnJson(dynamicBinding, nonBinding, uoType, spatialMethod)); + return cadet::JsonParameterProvider( + createLinearBenchmarkColumnJson(dynamicBinding, nonBinding, uoType, spatialMethod)); } -cadet::JsonParameterProvider createLinearBenchmark(bool dynamicBinding, bool nonBinding, const std::string& uoType, const std::string& spatialMethod) +cadet::JsonParameterProvider createLinearBenchmark(bool dynamicBinding, bool nonBinding, const std::string& uoType, + const std::string& spatialMethod) { json config; // Model @@ -935,8 +942,8 @@ cadet::JsonParameterProvider createLinearBenchmark(bool dynamicBinding, bool non // Connection list is 3x7 since we have 1 connection between // the two unit operations with 3 ports (and we need to have 7 columns) sw["CONNECTIONS"] = {1.0, 0.0, 0.0, 0.0, -1.0, -1.0, 1.16355283e-09, - 1.0, 0.0, 0.0, 1.0, -1.0, -1.0, 3.49065850e-09, - 1.0, 0.0, 0.0, 2.0, -1.0, -1.0, 5.81776417e-09}; + 1.0, 0.0, 0.0, 1.0, -1.0, -1.0, 3.49065850e-09, + 1.0, 0.0, 0.0, 2.0, -1.0, -1.0, 5.81776417e-09}; // Connections: From unit operation 1 port 0 // to unit operation 0 port 0, // connect component -1 (i.e., all components) @@ -1133,8 +1140,7 @@ cadet::JsonParameterProvider createCSTRBenchmark(unsigned int nSec, double endTi // Connection list is 2x7 since we have 2 connection between // the three unit operations (and we need to have 7 columns) - sw["CONNECTIONS"] = {1.0, 0.0, -1.0, -1.0, -1.0, -1.0, 1.0, - 0.0, 2.0, -1.0, -1.0, -1.0, -1.0, 1.0}; + sw["CONNECTIONS"] = {1.0, 0.0, -1.0, -1.0, -1.0, -1.0, 1.0, 0.0, 2.0, -1.0, -1.0, -1.0, -1.0, 1.0}; // Connections: From unit operation 1 port -1 (i.e., all ports) // to unit operation 0 port -1 (i.e., all ports), // connect component -1 (i.e., all components) diff --git a/test/JsonTestModels.hpp b/test/JsonTestModels.hpp index 30cfcef1c..d89597b0a 100644 --- a/test/JsonTestModels.hpp +++ b/test/JsonTestModels.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines a ParameterProvider that uses JSON. */ @@ -21,13 +21,17 @@ #include "common/JsonParameterProvider.hpp" cadet::JsonParameterProvider createColumnWithSMA(const std::string& uoType, const std::string& spatialScheme); -cadet::JsonParameterProvider createColumnWithTwoCompLinearBinding(const std::string& uoType, const std::string& spatialScheme); -cadet::JsonParameterProvider createColumnLinearBenchmark(bool dynamicBinding, bool nonBinding, const std::string& uoType, const std::string& spatialScheme); +cadet::JsonParameterProvider createColumnWithTwoCompLinearBinding(const std::string& uoType, + const std::string& spatialScheme); +cadet::JsonParameterProvider createColumnLinearBenchmark(bool dynamicBinding, bool nonBinding, + const std::string& uoType, const std::string& spatialScheme); nlohmann::json createLWEJson(const std::string& uoType, const std::string& spatialMethod); cadet::JsonParameterProvider createLWE(const std::string& uoType, const std::string& spatialScheme); -cadet::JsonParameterProvider createPulseInjectionColumn(const std::string& uoType, const std::string& spatialScheme, bool dynamicBinding); -cadet::JsonParameterProvider createLinearBenchmark(bool dynamicBinding, bool nonBinding, const std::string& uoType, const std::string& spatialScheme); +cadet::JsonParameterProvider createPulseInjectionColumn(const std::string& uoType, const std::string& spatialScheme, + bool dynamicBinding); +cadet::JsonParameterProvider createLinearBenchmark(bool dynamicBinding, bool nonBinding, const std::string& uoType, + const std::string& spatialScheme); cadet::JsonParameterProvider createCSTR(unsigned int nComp); cadet::JsonParameterProvider createCSTRBenchmark(unsigned int nSec, double endTime, double interval); -#endif // CADETTEST_JSONTESTMODELS_HPP_ +#endif // CADETTEST_JSONTESTMODELS_HPP_ diff --git a/test/LogUtils.cpp b/test/LogUtils.cpp index 22b66a8b6..66c09713a 100644 --- a/test/LogUtils.cpp +++ b/test/LogUtils.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -20,7 +20,6 @@ #include #include - TEST_CASE("Log matrix output from linear array", "[Logging]") { std::stringstream ss; diff --git a/test/LumpedRateModelWithPores.cpp b/test/LumpedRateModelWithPores.cpp index e6e4b5e44..0012e161d 100644 --- a/test/LumpedRateModelWithPores.cpp +++ b/test/LumpedRateModelWithPores.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -34,17 +34,23 @@ TEST_CASE("LRMP LWE forward vs backward flow", "[LRMP],[FV],[Simulation],[CI]") TEST_CASE("LRMP linear pulse vs analytic solution", "[LRMP],[FV],[Simulation],[Analytic],[CI]") { cadet::test::column::FVparams disc(512); - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", true, true, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", true, false, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", false, true, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", false, false, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", true, + true, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", true, + false, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", false, + true, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", false, + false, disc, 6e-5, 1e-7); } TEST_CASE("LRMP non-binding linear pulse vs analytic solution", "[LRMP],[FV],[Simulation],[Analytic],[NonBinding],[CI]") { cadet::test::column::FVparams disc(512); - cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-nonBinding.data", true, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-nonBinding.data", false, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-nonBinding.data", + true, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-nonBinding.data", + false, disc, 6e-5, 1e-7); } TEST_CASE("LRMP Jacobian forward vs backward flow", "[LRMP],[FV],[UnitOp],[Residual],[Jacobian],[AD],[CI]") @@ -59,50 +65,58 @@ TEST_CASE("LRMP Jacobian forward vs backward flow", "[LRMP],[FV],[UnitOp],[Resid } } -TEST_CASE("LRMP numerical Benchmark with parameter sensitivities for linear case", "[LRMP],[FV],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE("LRMP numerical Benchmark with parameter sensitivities for linear case", + "[LRMP],[FV],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails + // on server { const std::string& modelFilePath = std::string("/data/model_LRMP_dynLin_1comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_LRMP_dynLin_1comp_sensbenchmark1_FV_Z32.h5"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1e-4, 1e-4, 1e-4, 1e-4 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1e-4, 1e-4, 1e-4, 1e-4}; cadet::test::column::FVparams disc(32); cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, true); } -TEST_CASE("LRMP numerical Benchmark with parameter sensitivities for SMA LWE case", "[LRMP],[FV],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE("LRMP numerical Benchmark with parameter sensitivities for SMA LWE case", + "[LRMP],[FV],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails + // on server { const std::string& modelFilePath = std::string("/data/model_LRMP_reqSMA_4comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_LRMP_reqSMA_4comp_sensbenchmark1_FV_Z32.h5"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1e-4, 1e-4, 1e-4, 1e-4 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1e-4, 1e-4, 1e-4, 1e-4}; cadet::test::column::FVparams disc(32); cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "000", absTol, relTol, disc, true); } -TEST_CASE("LRMP numerical EOC Benchmark with parameter sensitivities for linear case", "[releaseCI],[EOC],[EOC_LRMP_FV]") +TEST_CASE("LRMP numerical EOC Benchmark with parameter sensitivities for linear case", + "[releaseCI],[EOC],[EOC_LRMP_FV]") { const std::string& modelFilePath = std::string("/data/model_LRMP_dynLin_1comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_LRMP_dynLin_1comp_sensbenchmark1_FV_Z32768.h5"); const std::string& convFilePath = std::string("/data/convergence_LRMP_dynLin_1comp_sensbenchmark1.json"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1e-4, 1e-4, 1e-4, 1e-4 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1e-4, 1e-4, 1e-4, 1e-4}; cadet::test::column::FVparams disc(16); - cadet::test::column::testEOCReferenceBenchmark(modelFilePath, refFilePath, convFilePath, "001", absTol, relTol, 4, disc, true); + cadet::test::column::testEOCReferenceBenchmark(modelFilePath, refFilePath, convFilePath, "001", absTol, relTol, 4, + disc, true); } -TEST_CASE("LRMP numerical EOC Benchmark with parameter sensitivities for SMA LWE case", "[releaseCI],[EOC],[EOC_LRMP_FV]") +TEST_CASE("LRMP numerical EOC Benchmark with parameter sensitivities for SMA LWE case", + "[releaseCI],[EOC],[EOC_LRMP_FV]") { const std::string& modelFilePath = std::string("/data/model_LRMP_reqSMA_4comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_LRMP_reqSMA_4comp_sensbenchmark1_FV_Z2048.h5"); const std::string& convFilePath = std::string("/data/convergence_LRMP_reqSMA_4comp_sensbenchmark1.json"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1e-4, 1e-4, 1e-4, 1e-4 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1e-4, 1e-4, 1e-4, 1e-4}; cadet::test::column::FVparams disc(8); - cadet::test::column::testEOCReferenceBenchmark(modelFilePath, refFilePath, convFilePath, "000", absTol, relTol, 2, disc, true); + cadet::test::column::testEOCReferenceBenchmark(modelFilePath, refFilePath, convFilePath, "000", absTol, relTol, 2, + disc, true); } TEST_CASE("LRMP time derivative Jacobian vs FD", "[LRMP],[UnitOp],[Residual],[Jacobian],[CI]") @@ -122,7 +136,7 @@ TEST_CASE("LRMP sensitivity Jacobians", "[LRMP],[FV],[UnitOp],[Sensitivity],[CI] cadet::test::column::testFwdSensJacobians(jpp, 1e-4, 6e-7); } -//TEST_CASE("LRMP forward sensitivity vs FD", "[LRMP],[FV],[Sensitivity],[Simulation],[failedFDtestLRMP]") // todo fix +// TEST_CASE("LRMP forward sensitivity vs FD", "[LRMP],[FV],[Sensitivity],[Simulation],[failedFDtestLRMP]") // todo fix //{ // // todo comment on FD issue // // Relative error is checked first, we use high absolute error for letting @@ -132,51 +146,60 @@ TEST_CASE("LRMP sensitivity Jacobians", "[LRMP],[FV],[UnitOp],[Sensitivity],[CI] // const double absTols[] = {6e5, 2e-2, 2e-2, 1.0}; // const double relTols[] = {5e-3, 1e-1, 5e-1, 6e-3}; // const double passRatio[] = {0.87, 0.84, 0.88, 0.95}; -// cadet::test::column::testFwdSensSolutionFD("LUMPED_RATE_MODEL_WITH_PORES", true, fdStepSize, absTols, relTols, passRatio); -//} +// cadet::test::column::testFwdSensSolutionFD("LUMPED_RATE_MODEL_WITH_PORES", true, fdStepSize, absTols, relTols, +// passRatio); +// } -//TEST_CASE("LRMP forward sensitivity forward vs backward flow", "[LRMP],[FV],[Sensitivity],[Simulation],[fixLRMP]") // todo fix +// TEST_CASE("LRMP forward sensitivity forward vs backward flow", "[LRMP],[FV],[Sensitivity],[Simulation],[fixLRMP]") // +// todo fix //{ // // todo why is there a pass ratio when we compare fwd and bwd flow? // const double absTols[] = {50.0, 2e-10, 1.0, 5e-7}; // const double relTols[] = {2e-4, 9e-6, 5e-7, 1e-7}; // const double passRatio[] = {1.0, 0.99, 0.98, 0.99}; // todo? works for 0.79, 0.99, 0.98, 0.99 -// cadet::test::column::testFwdSensSolutionForwardBackward("LUMPED_RATE_MODEL_WITH_PORES", absTols, relTols, passRatio); -//} +// cadet::test::column::testFwdSensSolutionForwardBackward("LUMPED_RATE_MODEL_WITH_PORES", absTols, relTols, +// passRatio); +// } TEST_CASE("LRMP consistent initialization with linear binding", "[LRMP],[FV],[ConsistentInit],[CI]") { cadet::test::column::testConsistentInitializationLinearBinding("LUMPED_RATE_MODEL_WITH_PORES", "FV", 1e-12, 1e-12); } -//TEST_CASE("LRMP consistent initialization with SMA binding", "[LRMP],[FV],[ConsistentInit],[fixLRMP]") +// TEST_CASE("LRMP consistent initialization with SMA binding", "[LRMP],[FV],[ConsistentInit],[fixLRMP]") //{ // std::vector y(4 + 4 * 16 + 16 * (4 + 4) + 4 * 16, 0.0); //// Optimal values: -//// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, +//// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, //// 1.0, 1.8, 1.5, 1.6, 856.173, 64.457, 5.73227, 2.85286}; -// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, +// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, // 1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0}; -// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 16); -// cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 16, 8); -// cadet::test::util::populate(y.data() + 4 + 4 * 16 + 16 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); +// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 +//* 16); cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 16, 8); cadet::test::util::populate(y.data() +//+ 4 +//+ 4 * 16 + 16 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); // // cadet::test::column::testConsistentInitializationSMABinding("LUMPED_RATE_MODEL_WITH_PORES", y.data(), 1e-14, 1e-5); //} -TEST_CASE("LRMP consistent sensitivity initialization with linear binding", "[LRMP],[FV],[ConsistentInit],[Sensitivity],[CI]") +TEST_CASE("LRMP consistent sensitivity initialization with linear binding", + "[LRMP],[FV],[ConsistentInit],[Sensitivity],[CI]") { // Fill state vector with given initial values const unsigned int numDofs = 4 + 4 * 16 + 16 * (4 + 4) + 4 * 16; std::vector y(numDofs, 0.0); std::vector yDot(numDofs, 0.0); - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "FV", y.data(), yDot.data(), true, 1e-14); + cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "FV", y.data(), + yDot.data(), true, 1e-14); } -TEST_CASE("LRMP consistent sensitivity initialization with SMA binding", "[LRMP],[FV],[ConsistentInit],[Sensitivity],[CI]") +TEST_CASE("LRMP consistent sensitivity initialization with SMA binding", + "[LRMP],[FV],[ConsistentInit],[Sensitivity],[CI]") { // Fill state vector with given initial values const unsigned int numDofs = 4 + 4 * 16 + 16 * (4 + 4) + 4 * 16; @@ -184,13 +207,18 @@ TEST_CASE("LRMP consistent sensitivity initialization with SMA binding", "[LRMP] std::vector yDot(numDofs, 0.0); const double bindingCell[] = {1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0}; - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 16); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 16); cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 8, 16); - cadet::test::util::populate(y.data() + 4 + 4 * 16 + 16 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); + cadet::test::util::populate( + y.data() + 4 + 4 * 16 + 16 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, + 4 * 16); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "FV", y.data(), yDot.data(), false, 1e-10); + cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "FV", y.data(), + yDot.data(), false, 1e-10); } TEST_CASE("LRMP inlet DOF Jacobian", "[LRMP],[FV],[UnitOp],[Jacobian],[Inlet],[CI]") @@ -220,7 +248,8 @@ TEST_CASE("LRMP LWE separate identical particle types match", "[LRMP],[FV],[Simu cadet::test::particle::testSeparateIdenticalParticleTypes("LUMPED_RATE_MODEL_WITH_PORES", "FV", 1e-15, 1e-15); } -TEST_CASE("LRMP linear binding single particle matches particle distribution", "[LRMP],[FV],[Simulation],[ParticleType],[CI]") +TEST_CASE("LRMP linear binding single particle matches particle distribution", + "[LRMP],[FV],[Simulation],[ParticleType],[CI]") { cadet::test::particle::testLinearMixedParticleTypes("LUMPED_RATE_MODEL_WITH_PORES", "FV", 5e-8, 5e-5); } @@ -230,24 +259,30 @@ TEST_CASE("LRMP multiple particle types Jacobian analytic vs AD", "[LRMP],[FV],[ cadet::test::particle::testJacobianMixedParticleTypes("LUMPED_RATE_MODEL_WITH_PORES", "FV"); } -TEST_CASE("LRMP multiple particle types time derivative Jacobian vs FD", "[LRMP],[FV],[UnitOp],[Residual],[Jacobian],[ParticleType],[CI],[FD]") +TEST_CASE("LRMP multiple particle types time derivative Jacobian vs FD", + "[LRMP],[FV],[UnitOp],[Residual],[Jacobian],[ParticleType],[CI],[FD]") { - cadet::test::particle::testTimeDerivativeJacobianMixedParticleTypesFD("LUMPED_RATE_MODEL_WITH_PORES", "FV", 1e-6, 0.0, 9e-4); + cadet::test::particle::testTimeDerivativeJacobianMixedParticleTypesFD("LUMPED_RATE_MODEL_WITH_PORES", "FV", 1e-6, + 0.0, 9e-4); } -TEST_CASE("LRMP multiple spatially dependent particle types Jacobian analytic vs AD", "[LRMP],[FV],[Jacobian],[AD],[ParticleType],[CI]") +TEST_CASE("LRMP multiple spatially dependent particle types Jacobian analytic vs AD", + "[LRMP],[FV],[Jacobian],[AD],[ParticleType],[CI]") { cadet::test::particle::testJacobianSpatiallyMixedParticleTypes("LUMPED_RATE_MODEL_WITH_PORES", "FV"); } -TEST_CASE("LRMP linear binding single particle matches spatially dependent particle distribution", "[LRMP],[FV],[Simulation],[ParticleType],[CI]") +TEST_CASE("LRMP linear binding single particle matches spatially dependent particle distribution", + "[LRMP],[FV],[Simulation],[ParticleType],[CI]") { cadet::test::particle::testLinearSpatiallyMixedParticleTypes("LUMPED_RATE_MODEL_WITH_PORES", "FV", 5e-8, 5e-5); } -TEST_CASE("LRMP multiple spatially dependent particle types flux Jacobian vs FD", "[LRMP],[FV],[UnitOp],[Residual],[Jacobian],[ParticleType],[CI],[FD]") +TEST_CASE("LRMP multiple spatially dependent particle types flux Jacobian vs FD", + "[LRMP],[FV],[UnitOp],[Residual],[Jacobian],[ParticleType],[CI],[FD]") { - cadet::test::particle::testArrowHeadJacobianSpatiallyMixedParticleTypes("LUMPED_RATE_MODEL_WITH_PORES", 1e-6, 1e-8, 1e-5); + cadet::test::particle::testArrowHeadJacobianSpatiallyMixedParticleTypes("LUMPED_RATE_MODEL_WITH_PORES", 1e-6, 1e-8, + 1e-5); } TEST_CASE("LRMP dynamic reactions Jacobian vs AD bulk", "[LRMP],[FV],[Jacobian],[AD],[ReactionModel],[CI]") @@ -270,34 +305,45 @@ TEST_CASE("LRMP dynamic reactions Jacobian vs AD bulk and particle", "[LRMP],[FV cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITH_PORES", "FV", true, true, false); } -TEST_CASE("LRMP dynamic reactions Jacobian vs AD bulk and modified particle", "[LRMP],[FV],[Jacobian],[AD],[ReactionModel],[CI]") +TEST_CASE("LRMP dynamic reactions Jacobian vs AD bulk and modified particle", + "[LRMP],[FV],[Jacobian],[AD],[ReactionModel],[CI]") { cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITH_PORES", "FV", true, true, true); } -TEST_CASE("LRMP dynamic reactions time derivative Jacobian vs FD bulk", "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("LRMP dynamic reactions time derivative Jacobian vs FD bulk", + "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "FV", true, false, false, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "FV", true, + false, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP dynamic reactions time derivative Jacobian vs FD particle", "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("LRMP dynamic reactions time derivative Jacobian vs FD particle", + "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "FV", false, true, false, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "FV", false, + true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP dynamic reactions time derivative Jacobian vs FD modified particle", "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("LRMP dynamic reactions time derivative Jacobian vs FD modified particle", + "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "FV", false, true, true, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "FV", false, + true, true, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP dynamic reactions time derivative Jacobian vs FD bulk and particle", "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("LRMP dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "FV", true, true, false, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "FV", true, + true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("LRMP dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "FV", true, true, true, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "FV", true, + true, true, 1e-6, 1e-14, 8e-4); } inline cadet::JsonParameterProvider createColumnWithTwoCompLinearBindingThreeParticleTypes() @@ -311,61 +357,71 @@ inline cadet::JsonParameterProvider createColumnWithTwoCompLinearBindingThreePar return jpp; } -TEST_CASE("LRMP multi particle types dynamic reactions Jacobian vs AD bulk", "[LRMP],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("LRMP multi particle types dynamic reactions Jacobian vs AD bulk", + "[LRMP],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, false, false); } -TEST_CASE("LRMP multi particle types dynamic reactions Jacobian vs AD particle", "[LRMP],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("LRMP multi particle types dynamic reactions Jacobian vs AD particle", + "[LRMP],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, false, true, false); } -TEST_CASE("LRMP multi particle types dynamic reactions Jacobian vs AD modified particle", "[LRMP],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("LRMP multi particle types dynamic reactions Jacobian vs AD modified particle", + "[LRMP],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, false, true, true); } -TEST_CASE("LRMP multi particle types dynamic reactions Jacobian vs AD bulk and particle", "[LRMP],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("LRMP multi particle types dynamic reactions Jacobian vs AD bulk and particle", + "[LRMP],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, true, false); } -TEST_CASE("LRMP multi particle types dynamic reactions Jacobian vs AD bulk and modified particle", "[LRMP],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("LRMP multi particle types dynamic reactions Jacobian vs AD bulk and modified particle", + "[LRMP],[FV],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, true, true); } -TEST_CASE("LRMP multi particle types dynamic reactions time derivative Jacobian vs FD bulk", "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") +TEST_CASE("LRMP multi particle types dynamic reactions time derivative Jacobian vs FD bulk", + "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, false, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP multi particle types dynamic reactions time derivative Jacobian vs FD particle", "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") +TEST_CASE("LRMP multi particle types dynamic reactions time derivative Jacobian vs FD particle", + "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") +TEST_CASE("LRMP multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", + "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, true, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") +TEST_CASE("LRMP multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") +TEST_CASE("LRMP multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[LRMP],[FV],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, true, 1e-6, 1e-14, 8e-4); diff --git a/test/LumpedRateModelWithPoresDG.cpp b/test/LumpedRateModelWithPoresDG.cpp index 657247fd3..7a0aeaf58 100644 --- a/test/LumpedRateModelWithPoresDG.cpp +++ b/test/LumpedRateModelWithPoresDG.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -33,20 +33,27 @@ TEST_CASE("LRMP_DG LWE forward vs backward flow", "[LRMP],[DG],[Simulation],[CI] TEST_CASE("LRMP_DG linear pulse vs analytic solution", "[LRMP],[DG],[Simulation],[Analytic],[CI]") { cadet::test::column::DGparams disc; - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", true, true, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", true, false, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", false, true, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", false, false, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", true, + true, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", true, + false, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", false, + true, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-pulseBenchmark.data", false, + false, disc, 6e-5, 1e-7); } -TEST_CASE("LRMP_DG non-binding linear pulse vs analytic solution", "[LRMP],[DG],[Simulation],[Analytic],[NonBinding],[CI]") +TEST_CASE("LRMP_DG non-binding linear pulse vs analytic solution", + "[LRMP],[DG],[Simulation],[Analytic],[NonBinding],[CI]") { cadet::test::column::DGparams disc; - cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-nonBinding.data", true, disc, 6e-5, 1e-7); - cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-nonBinding.data", false, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-nonBinding.data", + true, disc, 6e-5, 1e-7); + cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITH_PORES", "/data/lrmp-nonBinding.data", + false, disc, 6e-5, 1e-7); } -//TEST_CASE("LRMP_DG Jacobian forward vs backward flow", "[LRMP],[DG],[UnitOp],[Residual],[Jacobian],[AD],[fix]") +// TEST_CASE("LRMP_DG Jacobian forward vs backward flow", "[LRMP],[DG],[UnitOp],[Residual],[Jacobian],[AD],[fix]") //{ // cadet::test::column::DGparams disc; // @@ -54,27 +61,30 @@ TEST_CASE("LRMP_DG non-binding linear pulse vs analytic solution", "[LRMP],[DG], // for (int i = 0; i <= 1; i++) // { // disc.setIntegrationMode(i); -// cadet::test::column::testJacobianForwardBackward("LUMPED_RATE_MODEL_WITH_PORES", disc, std::numeric_limits::epsilon() * 100.0); +// cadet::test::column::testJacobianForwardBackward("LUMPED_RATE_MODEL_WITH_PORES", disc, +// std::numeric_limits::epsilon() * 100.0); // } -//} +// } -TEST_CASE("LRMP_DG numerical Benchmark with parameter sensitivities for linear case", "[LRMP],[DG],[Simulation],[Reference],[Sensitivity],[CI]") +TEST_CASE("LRMP_DG numerical Benchmark with parameter sensitivities for linear case", + "[LRMP],[DG],[Simulation],[Reference],[Sensitivity],[CI]") { const std::string& modelFilePath = std::string("/data/model_LRMP_dynLin_1comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_LRMP_dynLin_1comp_sensbenchmark1_DG_P3Z8.h5"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1.0, 1.0, 1.0, 1.0 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1.0, 1.0, 1.0, 1.0}; cadet::test::column::DGparams disc(0, 3, 8); cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, true); } -TEST_CASE("LRMP_DG numerical Benchmark with parameter sensitivities for SMA LWE case", "[LRMP],[DG],[Simulation],[Reference],[Sensitivity],[CI]") +TEST_CASE("LRMP_DG numerical Benchmark with parameter sensitivities for SMA LWE case", + "[LRMP],[DG],[Simulation],[Reference],[Sensitivity],[CI]") { const std::string& modelFilePath = std::string("/data/model_LRMP_reqSMA_4comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_LRMP_reqSMA_4comp_sensbenchmark1_DG_P3Z8.h5"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1.0, 1.0, 1.0, 1.0 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1.0, 1.0, 1.0, 1.0}; cadet::test::column::DGparams disc(0, 3, 8); cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "000", absTol, relTol, disc, true); @@ -91,9 +101,9 @@ TEST_CASE("LRMP_DG sensitivity Jacobians", "[LRMP],[DG],[UnitOp],[Sensitivity],[ cadet::test::column::testFwdSensJacobians(jpp, 1e-4, 6e-7); } - + //// todo fix: not just adjust tolerances as in FV but theres an actual error here: access violation in densematrix -//TEST_CASE("LRMP_DG forward sensitivity vs FD", "[LRMP],[DG],[Sensitivity],[Simulation],[todo]") +// TEST_CASE("LRMP_DG forward sensitivity vs FD", "[LRMP],[DG],[Sensitivity],[Simulation],[todo]") //{ // // Relative error is checked first, we use high absolute error for letting // // some points that are far off pass the error test, too. This is required @@ -102,79 +112,104 @@ TEST_CASE("LRMP_DG sensitivity Jacobians", "[LRMP],[DG],[UnitOp],[Sensitivity],[ // const double absTols[] = { 6e5, 2e-2, 2e-2, 1.0 }; // const double relTols[] = { 5e-3, 1e-1, 5e-1, 6e-3 }; // const double passRatio[] = { 0.87, 0.84, 0.88, 0.95 }; -// cadet::test::column::testFwdSensSolutionFD("LUMPED_RATE_MODEL_WITH_PORES", "DG", true, fdStepSize, absTols, relTols, passRatio); -//} - +// cadet::test::column::testFwdSensSolutionFD("LUMPED_RATE_MODEL_WITH_PORES", "DG", true, fdStepSize, absTols, relTols, +// passRatio); +// } + // todo fix: not just adjust tolerances as in FV but theres an actual error here: access violation in densematrix -//TEST_CASE("LRMP_DG forward sensitivity forward vs backward flow", "[LRMP],[DG],[Sensitivity],[Simulation],[todo]") +// TEST_CASE("LRMP_DG forward sensitivity forward vs backward flow", "[LRMP],[DG],[Sensitivity],[Simulation],[todo]") //{ // const double absTols[] = { 50.0, 2e-10, 1.0, 5e-7 }; // const double relTols[] = { 2e-4, 9e-6, 5e-7, 1e-7 }; // const double passRatio[] = { 1.0, 0.99, 0.98, 0.99 }; -// cadet::test::column::testFwdSensSolutionForwardBackward("LUMPED_RATE_MODEL_WITH_PORES", "DG", absTols, relTols, passRatio); +// cadet::test::column::testFwdSensSolutionForwardBackward("LUMPED_RATE_MODEL_WITH_PORES", "DG", absTols, relTols, +// passRatio); //} // todo fix consistent initialization for AD with req binding TEST_CASE("LRMP_DG consistent initialization with linear binding", "[LRMP],[DG],[ConsistentInit],[CI]") { - cadet::test::column::testConsistentInitializationLinearBinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", 1e-12, 1e-12, 0, 0); - cadet::test::column::testConsistentInitializationLinearBinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", 1e-12, 1e-12, 1, 0); - cadet::test::column::testConsistentInitializationLinearBinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", 1e-12, 1e-12, 0, 1); - //cadet::test::column::testConsistentInitializationLinearBinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", 1e-12, 1e-12, 1, 1); + cadet::test::column::testConsistentInitializationLinearBinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", 1e-12, 1e-12, + 0, 0); + cadet::test::column::testConsistentInitializationLinearBinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", 1e-12, 1e-12, + 1, 0); + cadet::test::column::testConsistentInitializationLinearBinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", 1e-12, 1e-12, + 0, 1); + // cadet::test::column::testConsistentInitializationLinearBinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", 1e-12, + // 1e-12, 1, 1); } ////// todo fix consistent initialization for SMA (initialization not completely correct; AD gives assertion error) -//TEST_CASE("LRMP_DG consistent initialization with SMA binding", "[LRMP],[DG],[ConsistentInit],[todo]") +// TEST_CASE("LRMP_DG consistent initialization with SMA binding", "[LRMP],[DG],[ConsistentInit],[todo]") //{ // std::vector y(4 + 4 * 16 + 16 * (4 + 4) + 4 * 16, 0.0); // const double bindingCell[] = { 1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, // 1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0 }; -// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 16); -// cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 16, 8); -// cadet::test::util::populate(y.data() + 4 + 4 * 16 + 16 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); +// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 +//* 16); cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 16, 8); cadet::test::util::populate(y.data() +//+ 4 +//+ 4 * 16 + 16 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); // -// //cadet::test::column::testConsistentInitializationSMABinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), 1e-14, 1e-5, 0, 0); -// //cadet::test::column::testConsistentInitializationSMABinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), 1e-14, 1e-5, 1, 0); -// //cadet::test::column::testConsistentInitializationSMABinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), 1e-14, 1e-5, 0, 1); -// //cadet::test::column::testConsistentInitializationSMABinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), 1e-14, 1e-5, 1, 1); -//} +// //cadet::test::column::testConsistentInitializationSMABinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), 1e-14, +// 1e-5, 0, 0); +// //cadet::test::column::testConsistentInitializationSMABinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), 1e-14, +// 1e-5, 1, 0); +// //cadet::test::column::testConsistentInitializationSMABinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), 1e-14, +// 1e-5, 0, 1); +// //cadet::test::column::testConsistentInitializationSMABinding("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), 1e-14, +// 1e-5, 1, 1); +// } // todo fix kinetic binding sensitivity init -TEST_CASE("LRMP_DG consistent sensitivity initialization with linear binding", "[LRMP],[DG],[ConsistentInit],[Sensitivity],[CI]") +TEST_CASE("LRMP_DG consistent sensitivity initialization with linear binding", + "[LRMP],[DG],[ConsistentInit],[Sensitivity],[CI]") { // Fill state vector with given initial values const unsigned int numDofs = 2 + 2 * 10 + 10 * (2 + 2); std::vector y(numDofs, 0.0); std::vector yDot(numDofs, 0.0); - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - - //cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), yDot.data(), true, 1e-14, 0, 0); // doesnt work - cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), yDot.data(), true, 1e-14, 1, 0); // works - //cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), yDot.data(), true, 1e-14, 0, 1); // doesnt work - cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), yDot.data(), true, 1e-14, 1, 1); // works -} - -//// todo fix memory stuff (works for FV) -//TEST_CASE("LRMP_DG consistent sensitivity initialization with SMA binding", "[LRMP],[DG],[ConsistentInit],[Sensitivity],[todo]") + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + + // cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), + // yDot.data(), true, 1e-14, 0, 0); // doesnt work + cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), + yDot.data(), true, 1e-14, 1, 0); // works + // cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), + // yDot.data(), true, 1e-14, 0, 1); // doesnt work + cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), + yDot.data(), true, 1e-14, 1, 1); // works +} + +//// todo fix memory stuff (works for FV) +// TEST_CASE("LRMP_DG consistent sensitivity initialization with SMA binding", +// "[LRMP],[DG],[ConsistentInit],[Sensitivity],[todo]") //{ // // Fill state vector with given initial values -// const unsigned int numDofs = 4 + 4 * 10 + 10 * (4 + 4); +// const unsigned int numDofs = 4 + 4 * 10 + 10 * (4 + 4); // std::vector y(numDofs, 0.0); // std::vector yDot(numDofs, 0.0); // // const double bindingCell[] = { 1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0 }; -// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 10); -// cadet::test::util::repeat(y.data() + 4 + 4 * 10, bindingCell, 8, 10); -// cadet::test::util::populate(y.data() + 4 + 4 * 10 + 10 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 10); +// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 +//* 10); cadet::test::util::repeat(y.data() + 4 + 4 * 10, bindingCell, 8, 10); cadet::test::util::populate(y.data() +//+ 4 +//+ 4 * 10 + 10 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 10); // -// cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); +// cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, +// numDofs); // -// //cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), yDot.data(), false, 1e-10, 0, 0); -// //cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), yDot.data(), false, 1e-10, 1, 0); -// //cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), yDot.data(), false, 1e-10, 0, 1); -// //cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), yDot.data(), false, 1e-10, 1, 1); -//} +// //cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), +// yDot.data(), false, 1e-10, 0, 0); +// //cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), +// yDot.data(), false, 1e-10, 1, 0); +// //cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), +// yDot.data(), false, 1e-10, 0, 1); +// //cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITH_PORES", "DG", y.data(), +// yDot.data(), false, 1e-10, 1, 1); +// } TEST_CASE("LRMP_DG inlet DOF Jacobian", "[LRMP],[DG],[UnitOp],[Jacobian],[Inlet],[CI]") { @@ -203,7 +238,8 @@ TEST_CASE("LRMP_DG LWE separate identical particle types match", "[LRMP],[DG],[S cadet::test::particle::testSeparateIdenticalParticleTypes("LUMPED_RATE_MODEL_WITH_PORES", "DG", 1e-12, 1e-12); } -TEST_CASE("LRMP_DG linear binding single particle matches particle distribution", "[LRMP],[DG],[Simulation],[ParticleType],[CI]") +TEST_CASE("LRMP_DG linear binding single particle matches particle distribution", + "[LRMP],[DG],[Simulation],[ParticleType],[CI]") { cadet::test::particle::testLinearMixedParticleTypes("LUMPED_RATE_MODEL_WITH_PORES", "DG", 5e-8, 5e-5); } @@ -213,136 +249,169 @@ TEST_CASE("LRMP_DG multiple particle types Jacobian analytic vs AD", "[LRMP],[DG cadet::test::particle::testJacobianMixedParticleTypes("LUMPED_RATE_MODEL_WITH_PORES", "DG", 1e-11); } -TEST_CASE("LRMP_DG multiple particle types time derivative Jacobian vs FD", "[LRMP],[DG],[UnitOp],[Residual],[Jacobian],[ParticleType],[CI]") +TEST_CASE("LRMP_DG multiple particle types time derivative Jacobian vs FD", + "[LRMP],[DG],[UnitOp],[Residual],[Jacobian],[ParticleType],[CI]") { - cadet::test::particle::testTimeDerivativeJacobianMixedParticleTypesFD("LUMPED_RATE_MODEL_WITH_PORES", "DG", 1e-6, 0.0, 9e-4); + cadet::test::particle::testTimeDerivativeJacobianMixedParticleTypesFD("LUMPED_RATE_MODEL_WITH_PORES", "DG", 1e-6, + 0.0, 9e-4); } -TEST_CASE("LRMP_DG multiple spatially dependent particle types Jacobian analytic vs AD", "[LRMP],[DG],[Jacobian],[AD],[ParticleType],[CI]") +TEST_CASE("LRMP_DG multiple spatially dependent particle types Jacobian analytic vs AD", + "[LRMP],[DG],[Jacobian],[AD],[ParticleType],[CI]") { - cadet::test::particle::testJacobianSpatiallyMixedParticleTypes("LUMPED_RATE_MODEL_WITH_PORES", "DG", std::numeric_limits::epsilon() * 100.0); + cadet::test::particle::testJacobianSpatiallyMixedParticleTypes("LUMPED_RATE_MODEL_WITH_PORES", "DG", + std::numeric_limits::epsilon() * 100.0); } -TEST_CASE("LRMP_DG linear binding single particle matches spatially dependent particle distribution", "[LRMP],[DG],[Simulation],[ParticleType],[CI]") +TEST_CASE("LRMP_DG linear binding single particle matches spatially dependent particle distribution", + "[LRMP],[DG],[Simulation],[ParticleType],[CI]") { cadet::test::particle::testLinearSpatiallyMixedParticleTypes("LUMPED_RATE_MODEL_WITH_PORES", "DG", 5e-8, 5e-5); } TEST_CASE("LRMP_DG dynamic reactions Jacobian vs AD bulk", "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[CI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITH_PORES", "DG", true, false, false, std::numeric_limits::epsilon() * 100.0); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITH_PORES", "DG", true, false, false, + std::numeric_limits::epsilon() * 100.0); } TEST_CASE("LRMP_DG dynamic reactions Jacobian vs AD particle", "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[CI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITH_PORES", "DG", false, true, false, std::numeric_limits::epsilon() * 100.0); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITH_PORES", "DG", false, true, false, + std::numeric_limits::epsilon() * 100.0); } -TEST_CASE("LRMP_DG dynamic reactions Jacobian vs AD modified particle", "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[CI]") +TEST_CASE("LRMP_DG dynamic reactions Jacobian vs AD modified particle", + "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[CI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITH_PORES", "DG", false, true, true, std::numeric_limits::epsilon() * 100.0); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITH_PORES", "DG", false, true, true, + std::numeric_limits::epsilon() * 100.0); } -TEST_CASE("LRMP_DG dynamic reactions Jacobian vs AD bulk and particle", "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[CI]") +TEST_CASE("LRMP_DG dynamic reactions Jacobian vs AD bulk and particle", + "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[CI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITH_PORES", "DG", true, true, false, std::numeric_limits::epsilon() * 100.0); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITH_PORES", "DG", true, true, false, + std::numeric_limits::epsilon() * 100.0); } -TEST_CASE("LRMP_DG dynamic reactions Jacobian vs AD bulk and modified particle", "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[CI]") +TEST_CASE("LRMP_DG dynamic reactions Jacobian vs AD bulk and modified particle", + "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[CI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITH_PORES", "DG", true, true, true, std::numeric_limits::epsilon() * 100.0); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITH_PORES", "DG", true, true, true, + std::numeric_limits::epsilon() * 100.0); } -TEST_CASE("LRMP_DG dynamic reactions time derivative Jacobian vs FD bulk", "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[CI]") +TEST_CASE("LRMP_DG dynamic reactions time derivative Jacobian vs FD bulk", + "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[CI]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "DG", true, false, false, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "DG", true, + false, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP_DG dynamic reactions time derivative Jacobian vs FD particle", "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[CI]") +TEST_CASE("LRMP_DG dynamic reactions time derivative Jacobian vs FD particle", + "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[CI]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "DG", false, true, false, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "DG", false, + true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP_DG dynamic reactions time derivative Jacobian vs FD modified particle", "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[CI]") +TEST_CASE("LRMP_DG dynamic reactions time derivative Jacobian vs FD modified particle", + "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[CI]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "DG", false, true, true, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "DG", false, + true, true, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP_DG dynamic reactions time derivative Jacobian vs FD bulk and particle", "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[CI]") +TEST_CASE("LRMP_DG dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[CI]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "DG", true, true, false, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "DG", true, + true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP_DG dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[CI]") +TEST_CASE("LRMP_DG dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[CI]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "DG", true, true, true, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITH_PORES", "DG", true, + true, true, 1e-6, 1e-14, 8e-4); } inline cadet::JsonParameterProvider createLRMPColumnWithTwoCompLinearBindingThreeParticleTypes() { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding("LUMPED_RATE_MODEL_WITH_PORES", "DG"); - const double parVolFrac[] = { 0.3, 0.6, 0.1 }; - const double parFactor[] = { 0.9, 0.8 }; + const double parVolFrac[] = {0.3, 0.6, 0.1}; + const double parFactor[] = {0.9, 0.8}; cadet::test::particle::extendModelToManyParticleTypes(jpp, 3, parFactor, parVolFrac); return jpp; } -TEST_CASE("LRMP_DG multi particle types dynamic reactions Jacobian vs AD bulk", "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("LRMP_DG multi particle types dynamic reactions Jacobian vs AD bulk", + "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createLRMPColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, false, false, 1e-11); } -TEST_CASE("LRMP_DG multi particle types dynamic reactions Jacobian vs AD particle", "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("LRMP_DG multi particle types dynamic reactions Jacobian vs AD particle", + "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createLRMPColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, false, true, false, 1e-11); } -TEST_CASE("LRMP_DG multi particle types dynamic reactions Jacobian vs AD modified particle", "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("LRMP_DG multi particle types dynamic reactions Jacobian vs AD modified particle", + "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createLRMPColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, false, true, true, 1e-11); } -TEST_CASE("LRMP_DG multi particle types dynamic reactions Jacobian vs AD bulk and particle", "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("LRMP_DG multi particle types dynamic reactions Jacobian vs AD bulk and particle", + "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createLRMPColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, true, false, 1e-11); } -TEST_CASE("LRMP_DG multi particle types dynamic reactions Jacobian vs AD bulk and modified particle", "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("LRMP_DG multi particle types dynamic reactions Jacobian vs AD bulk and modified particle", + "[LRMP],[DG],[Jacobian],[AD],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createLRMPColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, true, true, 1e-11); } -TEST_CASE("LRMP_DG multi particle types dynamic reactions time derivative Jacobian vs FD bulk", "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("LRMP_DG multi particle types dynamic reactions time derivative Jacobian vs FD bulk", + "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createLRMPColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, false, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP_DG multi particle types dynamic reactions time derivative Jacobian vs FD particle", "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("LRMP_DG multi particle types dynamic reactions time derivative Jacobian vs FD particle", + "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createLRMPColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP_DG multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("LRMP_DG multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", + "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createLRMPColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, true, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP_DG multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("LRMP_DG multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createLRMPColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRMP_DG multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") +TEST_CASE("LRMP_DG multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[LRMP],[DG],[Jacobian],[Residual],[ReactionModel],[ParticleType],[CI]") { cadet::JsonParameterProvider jpp = createLRMPColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, true, 1e-6, 1e-14, 8e-4); diff --git a/test/LumpedRateModelWithoutPores.cpp b/test/LumpedRateModelWithoutPores.cpp index 9ca25aabf..0a0643ea8 100644 --- a/test/LumpedRateModelWithoutPores.cpp +++ b/test/LumpedRateModelWithoutPores.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -33,17 +33,24 @@ TEST_CASE("LRM LWE forward vs backward flow", "[LRM],[FV],[Simulation],[CI]") TEST_CASE("LRM linear pulse vs analytic solution", "[LRM],[FV],[Simulation],[Reference],[Analytic],[CI]") { cadet::test::column::FVparams disc(1024); - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", true, true, disc, 2e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", true, false, disc, 2e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", false, true, disc, 2e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", false, false, disc, 2e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", true, + true, disc, 2e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", true, + false, disc, 2e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", + false, true, disc, 2e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", + false, false, disc, 2e-5, 1e-7); } -TEST_CASE("LRM non-binding linear pulse vs analytic solution", "[LRM],[FV],[Simulation],[Reference],[Analytic],[NonBinding],[CI]") +TEST_CASE("LRM non-binding linear pulse vs analytic solution", + "[LRM],[FV],[Simulation],[Reference],[Analytic],[NonBinding],[CI]") { cadet::test::column::FVparams disc(1024); - cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-nonBinding.data", true, disc, 2e-5, 1e-7); - cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-nonBinding.data", false, disc, 2e-5, 1e-7); + cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-nonBinding.data", + true, disc, 2e-5, 1e-7); + cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-nonBinding.data", + false, disc, 2e-5, 1e-7); } TEST_CASE("LRM Jacobian forward vs backward flow", "[LRM],[FV],[UnitOp],[Residual],[Jacobian],[AD],[CI]") @@ -58,23 +65,28 @@ TEST_CASE("LRM Jacobian forward vs backward flow", "[LRM],[FV],[UnitOp],[Residua } } -TEST_CASE("LRM numerical Benchmark with parameter sensitivities for linear case", "[LRM],[FV],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE("LRM numerical Benchmark with parameter sensitivities for linear case", + "[LRM],[FV],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on + // server { const std::string& modelFilePath = std::string("/data/model_LRM_dynLin_1comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_LRM_dynLin_1comp_sensbenchmark1_FV_Z32.h5"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1e-4, 1e-4, 1e-4, 1e-4 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1e-4, 1e-4, 1e-4, 1e-4}; cadet::test::column::FVparams disc(32); cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, true); } -TEST_CASE("LRM numerical Benchmark with parameter sensitivities for SMA LWE case", "[LRM],[FV],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE("LRM numerical Benchmark with parameter sensitivities for SMA LWE case", + "[LRM],[FV],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on + // server { const std::string& modelFilePath = std::string("/data/model_LRM_reqSMA_4comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_LRM_reqSMA_4comp_sensbenchmark1_FV_Z32.h5"); - const std::vector absTol = { 1e-12, 1e-0, 1e-12, 1e-12 }; // todo: why is such a high tolerance required for first sensitivity parameter - const std::vector relTol = { 1e-4, 1.0, 1e-4, 1e-4 }; + const std::vector absTol = { + 1e-12, 1e-0, 1e-12, 1e-12}; // todo: why is such a high tolerance required for first sensitivity parameter + const std::vector relTol = {1e-4, 1.0, 1e-4, 1e-4}; cadet::test::column::FVparams disc(32); cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "000", absTol, relTol, disc, true); @@ -85,11 +97,12 @@ TEST_CASE("LRM numerical EOC Benchmark with parameter sensitivities for linear c const std::string& modelFilePath = std::string("/data/model_LRM_dynLin_1comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_LRM_dynLin_1comp_sensbenchmark1_FV_Z131072.h5"); const std::string& convFilePath = std::string("/data/convergence_LRM_dynLin_1comp_sensbenchmark1.json"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1e-2, 1e-3, 1e-1, 1e-2 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1e-2, 1e-3, 1e-1, 1e-2}; cadet::test::column::FVparams disc(16); - cadet::test::column::testEOCReferenceBenchmark(modelFilePath, refFilePath, convFilePath, "001", absTol, relTol, 4, disc, true); + cadet::test::column::testEOCReferenceBenchmark(modelFilePath, refFilePath, convFilePath, "001", absTol, relTol, 4, + disc, true); } TEST_CASE("LRM numerical EOC Benchmark with parameter sensitivities for SMA LWE case", "[releaseCI],[EOC],[EOC_LRM_FV]") @@ -97,11 +110,12 @@ TEST_CASE("LRM numerical EOC Benchmark with parameter sensitivities for SMA LWE const std::string& modelFilePath = std::string("/data/model_LRM_reqSMA_4comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_LRM_reqSMA_4comp_sensbenchmark1_FV_Z4096.h5"); const std::string& convFilePath = std::string("/data/convergence_LRM_reqSMA_4comp_sensbenchmark1.json"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1e-4, 1e-4, 1e-4, 1e-4 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1e-4, 1e-4, 1e-4, 1e-4}; cadet::test::column::FVparams disc(8); - cadet::test::column::testEOCReferenceBenchmark(modelFilePath, refFilePath, convFilePath, "000", absTol, relTol, 2, disc, true); + cadet::test::column::testEOCReferenceBenchmark(modelFilePath, refFilePath, convFilePath, "000", absTol, relTol, 2, + disc, true); } TEST_CASE("LRM time derivative Jacobian vs FD", "[LRM],[FV],[UnitOp],[Residual],[Jacobian],[CI],[FD]") @@ -116,7 +130,8 @@ TEST_CASE("LRM sensitivity Jacobians", "[LRM],[FV],[UnitOp],[Sensitivity],[CI]") cadet::test::column::testFwdSensJacobians(jpp, 1e-4, 3e-7, 5e-5); } -//TEST_CASE("LRM forward sensitivity vs FD", "[LRM],[FV],[Sensitivity],[Simulation],[failedFDtestLRM],[FD]") // todo fix tolerances +// TEST_CASE("LRM forward sensitivity vs FD", "[LRM],[FV],[Sensitivity],[Simulation],[failedFDtestLRM],[FD]") // todo +// fix tolerances //{ // // Relative error is checked first, we use high absolute error for letting // // some points that are far off pass the error test, too. This is required @@ -125,49 +140,59 @@ TEST_CASE("LRM sensitivity Jacobians", "[LRM],[FV],[UnitOp],[Sensitivity],[CI]") // const double absTols[] = {2e8, 8e-3, 2e-2, 3e-1}; // const double relTols[] = {1e-1, 5e-1, 5e-2, 1e-2}; // const double passRatio[] = {0.88, 0.84, 0.73, 0.87}; -// cadet::test::column::testFwdSensSolutionFD("LUMPED_RATE_MODEL_WITHOUT_PORES", false, fdStepSize, absTols, relTols, passRatio); -//} +// cadet::test::column::testFwdSensSolutionFD("LUMPED_RATE_MODEL_WITHOUT_PORES", false, fdStepSize, absTols, relTols, +// passRatio); +// } // -//TEST_CASE("LRM forward sensitivity forward vs backward flow", "[LRM],[FV],[Sensitivity],[Simulation],[fixLRM]") // todo fix tolerances? why is there a pass ratio here, shouldnt this be precise? +// TEST_CASE("LRM forward sensitivity forward vs backward flow", "[LRM],[FV],[Sensitivity],[Simulation],[fixLRM]") // +// todo fix tolerances? why is there a pass ratio here, shouldnt this be precise? //{ // const double absTols[] = {500.0, 8e-7, 9e-7, 2e-3}; // const double relTols[] = {7e-3, 5e-5, 5e-5, 9e-4}; // const double passRatio[] = {0.99, 0.97, 0.97, 0.98}; -// cadet::test::column::testFwdSensSolutionForwardBackward("LUMPED_RATE_MODEL_WITHOUT_PORES", absTols, relTols, passRatio); -//} +// cadet::test::column::testFwdSensSolutionForwardBackward("LUMPED_RATE_MODEL_WITHOUT_PORES", absTols, relTols, +// passRatio); +// } TEST_CASE("LRM consistent initialization with linear binding", "[LRM],[FV],[ConsistentInit],[CI]") { - cadet::test::column::testConsistentInitializationLinearBinding("LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", 1e-12, 1e-12); + cadet::test::column::testConsistentInitializationLinearBinding("LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", 1e-12, + 1e-12); } -//TEST_CASE("LRM consistent initialization with SMA binding", "[LRM],[FV],[ConsistentInit],[fixLRM]") // todo fix +// TEST_CASE("LRM consistent initialization with SMA binding", "[LRM],[FV],[ConsistentInit],[fixLRM]") // todo fix //{ // std::vector y(4 + 16 * (4 + 4), 0.0); // // Optimal values: -// // const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, +// // const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, // // 1.0, 1.8, 1.5, 1.6, 856.173, 64.457, 5.73227, 2.85286}; -// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, +// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, // 1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0}; // cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4); // cadet::test::util::repeat(y.data() + 4, bindingCell, 16, 8); // -// cadet::test::column::testConsistentInitializationSMABinding("LUMPED_RATE_MODEL_WITHOUT_PORES", y.data(), 1e-14, 1e-5); -//} +// cadet::test::column::testConsistentInitializationSMABinding("LUMPED_RATE_MODEL_WITHOUT_PORES", y.data(), 1e-14, +// 1e-5); +// } -TEST_CASE("LRM consistent sensitivity initialization with linear binding", "[LRM],[FV],[ConsistentInit],[Sensitivity],[CI]") +TEST_CASE("LRM consistent sensitivity initialization with linear binding", + "[LRM],[FV],[ConsistentInit],[Sensitivity],[CI]") { // Fill state vector with given initial values const unsigned int numDofs = 4 + 16 * (4 + 4); std::vector y(numDofs, 0.0); std::vector yDot(numDofs, 0.0); - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", y.data(), yDot.data(), true, 1e-14); + cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", y.data(), + yDot.data(), true, 1e-14); } -TEST_CASE("LRM consistent sensitivity initialization with SMA binding", "[LRM],[FV],[ConsistentInit],[Sensitivity],[CI]") +TEST_CASE("LRM consistent sensitivity initialization with SMA binding", + "[LRM],[FV],[ConsistentInit],[Sensitivity],[CI]") { // Fill state vector with given initial values const unsigned int numDofs = 4 + 16 * (4 + 4); @@ -178,9 +203,11 @@ TEST_CASE("LRM consistent sensitivity initialization with SMA binding", "[LRM],[ cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4); cadet::test::util::repeat(y.data() + 4, bindingCell, 8, 16); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", y.data(), yDot.data(), false, 1e-9); + cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", y.data(), + yDot.data(), false, 1e-9); } TEST_CASE("LRM inlet DOF Jacobian", "[LRM],[FV],[UnitOp],[Jacobian],[Inlet],[CI]") @@ -190,7 +217,8 @@ TEST_CASE("LRM inlet DOF Jacobian", "[LRM],[FV],[UnitOp],[Jacobian],[Inlet],[CI] TEST_CASE("LRM transport Jacobian", "[LRM],[FV],[UnitOp],[Jacobian],[CI]") { - cadet::JsonParameterProvider jpp = createColumnLinearBenchmark(false, true, "LUMPED_RATE_MODEL_WITHOUT_PORES", "FV"); + cadet::JsonParameterProvider jpp = + createColumnLinearBenchmark(false, true, "LUMPED_RATE_MODEL_WITHOUT_PORES", "FV"); cadet::test::column::testJacobianAD(jpp); } @@ -202,20 +230,26 @@ TEST_CASE("LRM with two component linear binding Jacobian", "[LRM],[FV],[UnitOp] TEST_CASE("LRM dynamic reactions Jacobian vs AD bulk", "[LRM],[FV],[Jacobian],[AD],[ReactionModel],[CI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", true, false, false); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", true, false, + false); } TEST_CASE("LRM dynamic reactions Jacobian vs AD modified bulk", "[LRM],[FV],[Jacobian],[AD],[ReactionModel],[CI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", true, false, true); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", true, false, + true); } -TEST_CASE("LRM dynamic reactions time derivative Jacobian vs FD bulk", "[LRM],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("LRM dynamic reactions time derivative Jacobian vs FD bulk", + "[LRM],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", true, false, false, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", true, + false, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRM dynamic reactions time derivative Jacobian vs FD modified bulk", "[LRM],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("LRM dynamic reactions time derivative Jacobian vs FD modified bulk", + "[LRM],[FV],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", true, false, true, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", true, + false, true, 1e-6, 1e-14, 8e-4); } diff --git a/test/LumpedRateModelWithoutPoresDG.cpp b/test/LumpedRateModelWithoutPoresDG.cpp index b24e8e0e9..2f3eca92b 100644 --- a/test/LumpedRateModelWithoutPoresDG.cpp +++ b/test/LumpedRateModelWithoutPoresDG.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -32,20 +32,27 @@ TEST_CASE("LRM_DG LWE forward vs backward flow", "[LRM],[DG],[Simulation],[CI]") TEST_CASE("LRM_DG linear pulse vs analytic solution", "[LRM],[DG],[Simulation],[Analytic],[CI]") { cadet::test::column::DGparams disc; - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", true, true, disc, 2e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", true, false, disc, 2e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", false, true, disc, 2e-5, 1e-7); - cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", false, false, disc, 2e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", true, + true, disc, 2e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", true, + false, disc, 2e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", + false, true, disc, 2e-5, 1e-7); + cadet::test::column::testAnalyticBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-pulseBenchmark.data", + false, false, disc, 2e-5, 1e-7); } -TEST_CASE("LRM_DG non-binding linear pulse vs analytic solution", "[LRM],[DG],[Simulation],[Analytic],[NonBinding],[CI]") +TEST_CASE("LRM_DG non-binding linear pulse vs analytic solution", + "[LRM],[DG],[Simulation],[Analytic],[NonBinding],[CI]") { cadet::test::column::DGparams disc; - cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-nonBinding.data", true, disc, 2e-5, 1e-7); - cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-nonBinding.data", false, disc, 2e-5, 1e-7); + cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-nonBinding.data", + true, disc, 2e-5, 1e-7); + cadet::test::column::testAnalyticNonBindingBenchmark("LUMPED_RATE_MODEL_WITHOUT_PORES", "/data/lrm-nonBinding.data", + false, disc, 2e-5, 1e-7); } -//TEST_CASE("LRM_DG Jacobian forward vs backward flow", "[LRM],[DG],[UnitOp],[Residual],[Jacobian],[AD],[fix]") +// TEST_CASE("LRM_DG Jacobian forward vs backward flow", "[LRM],[DG],[UnitOp],[Residual],[Jacobian],[AD],[fix]") //{ // cadet::test::column::DGparams disc; // @@ -53,27 +60,31 @@ TEST_CASE("LRM_DG non-binding linear pulse vs analytic solution", "[LRM],[DG],[S // for (int i = 0; i < 2; i++) // { // disc.setIntegrationMode(i); -// cadet::test::column::testJacobianForwardBackward("LUMPED_RATE_MODEL_WITHOUT_PORES", disc, std::numeric_limits::epsilon() * 100.0); +// cadet::test::column::testJacobianForwardBackward("LUMPED_RATE_MODEL_WITHOUT_PORES", disc, +// std::numeric_limits::epsilon() * 100.0); // } -//} +// } -TEST_CASE("LRM_DG numerical Benchmark with parameter sensitivities for linear case", "[LRM],[DG],[Simulation],[Reference],[Sensitivity],[CI]") +TEST_CASE("LRM_DG numerical Benchmark with parameter sensitivities for linear case", + "[LRM],[DG],[Simulation],[Reference],[Sensitivity],[CI]") { const std::string& modelFilePath = std::string("/data/model_LRM_dynLin_1comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_LRM_dynLin_1comp_sensbenchmark1_DG_P3Z8.h5"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1.0, 1.0, 1.0, 1.0 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1.0, 1.0, 1.0, 1.0}; cadet::test::column::DGparams disc(0, 3, 8); cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, true); } -TEST_CASE("LRM_DG numerical Benchmark with parameter sensitivities for SMA LWE case", "[LRM],[DG],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE("LRM_DG numerical Benchmark with parameter sensitivities for SMA LWE case", + "[LRM],[DG],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on + // server { const std::string& modelFilePath = std::string("/data/model_LRM_reqSMA_4comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_LRM_reqSMA_4comp_sensbenchmark1_DG_P3Z8.h5"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1.0, 1.0, 1.0, 1.0 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1.0, 1.0, 1.0, 1.0}; cadet::test::column::DGparams disc(0, 3, 8); cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "000", absTol, relTol, disc, true); @@ -91,7 +102,8 @@ TEST_CASE("LRM_DG sensitivity Jacobians", "[LRM],[DG],[UnitOp],[Sensitivity],[CI cadet::test::column::testFwdSensJacobians(jpp, 1e-4, 3e-7, 5e-4); } -//TEST_CASE("LRM_DG forward sensitivity vs FD", "[LRM],[DG],[Sensitivity],[Simulation]") // todo fix tolerances (also for FV) +// TEST_CASE("LRM_DG forward sensitivity vs FD", "[LRM],[DG],[Sensitivity],[Simulation]") // todo fix tolerances (also +// for FV) //{ // // Relative error is checked first, we use high absolute error for letting // // some points that are far off pass the error test, too. This is required @@ -100,50 +112,62 @@ TEST_CASE("LRM_DG sensitivity Jacobians", "[LRM],[DG],[UnitOp],[Sensitivity],[CI // const double absTols[] = { 2e8, 8e-3, 2e-2, 3e-1 }; // const double relTols[] = { 1e-1, 5e-1, 5e-2, 1e-2 }; // const double passRatio[] = { 0.88, 0.84, 0.73, 0.87 }; -// cadet::test::column::testFwdSensSolutionFD("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", false, fdStepSize, absTols, relTols, passRatio); -//} +// cadet::test::column::testFwdSensSolutionFD("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", false, fdStepSize, absTols, +// relTols, passRatio); +// } -//TEST_CASE("LRM_DG forward sensitivity forward vs backward flow", "[LRM],[DG],[Sensitivity],[Simulation]") // todo fix (also for FV) tolerances? why is there a pass ratio here, shouldnt this be precise? +// TEST_CASE("LRM_DG forward sensitivity forward vs backward flow", "[LRM],[DG],[Sensitivity],[Simulation]") // todo fix +// (also for FV) tolerances? why is there a pass ratio here, shouldnt this be precise? //{ // const double absTols[] = { 500.0, 8e-7, 9e-7, 2e-3 }; // const double relTols[] = { 7e-3, 5e-5, 5e-5, 9e-4 }; // const double passRatio[] = { 0.99, 0.97, 0.97, 0.98 }; -// cadet::test::column::testFwdSensSolutionForwardBackward("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", absTols, relTols, passRatio); -//} +// cadet::test::column::testFwdSensSolutionForwardBackward("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", absTols, relTols, +// passRatio); +// } TEST_CASE("LRM_DG consistent initialization with linear binding", "[LRM],[DG],[ConsistentInit],[CI]") { - cadet::test::column::testConsistentInitializationLinearBinding("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", 1e-12, 1e-12); + cadet::test::column::testConsistentInitializationLinearBinding("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", 1e-12, + 1e-12); } -//TEST_CASE("LRM_DG consistent initialization with SMA binding", "[LRM],[DG],[ConsistentInit]") // todo fix (also for FV) +// TEST_CASE("LRM_DG consistent initialization with SMA binding", "[LRM],[DG],[ConsistentInit]") // todo fix (also for +// FV) //{ // std::vector y(4 + 16 * (4 + 4), 0.0); // // Optimal values: -// // const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, +// // const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, // // 1.0, 1.8, 1.5, 1.6, 856.173, 64.457, 5.73227, 2.85286}; // const double bindingCell[] = { 1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, // 1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0 }; // cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4); // cadet::test::util::repeat(y.data() + 4, bindingCell, 16, 8); // -// cadet::test::column::testConsistentInitializationSMABinding("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", y.data(), 1e-14, 1e-5); -//} +// cadet::test::column::testConsistentInitializationSMABinding("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", y.data(), +// 1e-14, 1e-5); +// } -TEST_CASE("LRM_DG consistent sensitivity initialization with linear binding", "[LRM],[DG],[ConsistentInit],[Sensitivity],[CI]") +TEST_CASE("LRM_DG consistent sensitivity initialization with linear binding", + "[LRM],[DG],[ConsistentInit],[Sensitivity],[CI]") { // Fill state vector with given initial values const unsigned int numDofs = 4 + 10 * (4 + 4); std::vector y(numDofs, 0.0); std::vector yDot(numDofs, 0.0); - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", y.data(), yDot.data(), true, 1e-14); + cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", y.data(), + yDot.data(), true, 1e-14); } -//// todo fix: sigsev read access violation when allocating _tempState = new double[numDofs()] in configure model discretization -//TEST_CASE("LRM_DG consistent sensitivity initialization with SMA binding", "[LRM],[DG],[ConsistentInit],[Sensitivity],[todo]") +//// todo fix: sigsev read access violation when allocating _tempState = new double[numDofs()] in configure model +/// discretization +// TEST_CASE("LRM_DG consistent sensitivity initialization with SMA binding", +// "[LRM],[DG],[ConsistentInit],[Sensitivity],[todo]") //{ // // Fill state vector with given initial values // const unsigned int numDofs = 4 + 10 * (4 + 4); @@ -154,10 +178,12 @@ TEST_CASE("LRM_DG consistent sensitivity initialization with linear binding", "[ // cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4); // cadet::test::util::repeat(y.data() + 4, bindingCell, 8, 16); // -// cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); +// cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, +// numDofs); // -// cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", y.data(), yDot.data(), false, 1e-10); -//} +// cadet::test::column::testConsistentInitializationSensitivity("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", y.data(), +// yDot.data(), false, 1e-10); +// } TEST_CASE("LRM_DG inlet DOF Jacobian", "[LRM],[DG],[UnitOp],[Jacobian],[Inlet],[CI]") { @@ -166,7 +192,8 @@ TEST_CASE("LRM_DG inlet DOF Jacobian", "[LRM],[DG],[UnitOp],[Jacobian],[Inlet],[ TEST_CASE("LRM_DG transport Jacobian", "[LRM],[FV],[UnitOp],[Jacobian],[CI]") { - cadet::JsonParameterProvider jpp = createColumnLinearBenchmark(false, true, "LUMPED_RATE_MODEL_WITHOUT_PORES", "DG"); + cadet::JsonParameterProvider jpp = + createColumnLinearBenchmark(false, true, "LUMPED_RATE_MODEL_WITHOUT_PORES", "DG"); cadet::test::column::testJacobianAD(jpp, std::numeric_limits::epsilon() * 100.0); } @@ -178,20 +205,26 @@ TEST_CASE("LRM_DG with two component linear binding Jacobian", "[LRM],[FV],[Unit TEST_CASE("LRM_DG dynamic reactions Jacobian vs AD bulk", "[LRM],[DG],[Jacobian],[AD],[ReactionModel],[CI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", true, false, false, std::numeric_limits::epsilon() * 100.0); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", true, false, + false, std::numeric_limits::epsilon() * 100.0); } TEST_CASE("LRM_DG dynamic reactions Jacobian vs AD modified bulk", "[LRM],[DG],[Jacobian],[AD],[ReactionModel],[CI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", true, false, true, std::numeric_limits::epsilon() * 100.0); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", true, false, + true, std::numeric_limits::epsilon() * 100.0); } -TEST_CASE("LRM_DG dynamic reactions time derivative Jacobian vs FD bulk", "[LRM],[DG],[Jacobian],[Residual],[ReactionModel],[CI]") +TEST_CASE("LRM_DG dynamic reactions time derivative Jacobian vs FD bulk", + "[LRM],[DG],[Jacobian],[Residual],[ReactionModel],[CI]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", true, false, false, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", true, + false, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("LRM_DG dynamic reactions time derivative Jacobian vs FD modified bulk", "[LRM],[DG],[Jacobian],[Residual],[ReactionModel],[CI]") +TEST_CASE("LRM_DG dynamic reactions time derivative Jacobian vs FD modified bulk", + "[LRM],[DG],[Jacobian],[Residual],[ReactionModel],[CI]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", true, false, true, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("LUMPED_RATE_MODEL_WITHOUT_PORES", "DG", true, + false, true, 1e-6, 1e-14, 8e-4); } diff --git a/test/MatrixHelper.hpp b/test/MatrixHelper.hpp index bee7cafb0..7991b8aea 100644 --- a/test/MatrixHelper.hpp +++ b/test/MatrixHelper.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines helper functions for checking matrices. */ @@ -47,8 +47,7 @@ inline void checkMatrixAgainstLinearArray(double const* mat, const std::vector -Matrix_t createBandMatrix(unsigned int rows, unsigned int lower, unsigned int upper) +template Matrix_t createBandMatrix(unsigned int rows, unsigned int lower, unsigned int upper) { Matrix_t bm; bm.resize(rows, lower, upper); @@ -70,4 +69,4 @@ Matrix_t createBandMatrix(unsigned int rows, unsigned int lower, unsigned int up } // namespace test } // namespace cadet -#endif // CADETTEST_MATRIXHELPER_HPP_ +#endif // CADETTEST_MATRIXHELPER_HPP_ diff --git a/test/ModelSystem.cpp b/test/ModelSystem.cpp index 6780c2e48..9d2528c08 100644 --- a/test/ModelSystem.cpp +++ b/test/ModelSystem.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -34,187 +34,353 @@ namespace { - class DummyUnitOperation : public cadet::IUnitOperation +class DummyUnitOperation : public cadet::IUnitOperation +{ +public: + DummyUnitOperation(cadet::UnitOpIdx unitOpIdx) : _unitOpIdx(unitOpIdx), _inFlow(0), _outFlow(0) + { + } + DummyUnitOperation(cadet::UnitOpIdx unitOpIdx, unsigned int nComp, unsigned int nInletPorts, + unsigned int nOutletPorts, bool canAccumulate) + : _unitOpIdx(unitOpIdx), _nComp(nComp), _nInletPorts(nInletPorts), _nOutletPorts(nOutletPorts), + _canAccumulate(canAccumulate), _inFlow(nInletPorts, 0.0), _outFlow(nOutletPorts, 0.0) { - public: - DummyUnitOperation(cadet::UnitOpIdx unitOpIdx) : _unitOpIdx(unitOpIdx), _inFlow(0), _outFlow(0) { } - DummyUnitOperation(cadet::UnitOpIdx unitOpIdx, unsigned int nComp, unsigned int nInletPorts, unsigned int nOutletPorts, bool canAccumulate) - : _unitOpIdx(unitOpIdx), _nComp(nComp), _nInletPorts(nInletPorts), _nOutletPorts(nOutletPorts), _canAccumulate(canAccumulate), - _inFlow(nInletPorts, 0.0), _outFlow(nOutletPorts, 0.0) - { } + } - // Default copy and move mechanisms - DummyUnitOperation(const DummyUnitOperation& cpy) = default; - DummyUnitOperation(DummyUnitOperation&& cpy) CADET_NOEXCEPT = default; + // Default copy and move mechanisms + DummyUnitOperation(const DummyUnitOperation& cpy) = default; + DummyUnitOperation(DummyUnitOperation&& cpy) CADET_NOEXCEPT = default; - inline DummyUnitOperation& operator=(const DummyUnitOperation& cpy) = default; + inline DummyUnitOperation& operator=(const DummyUnitOperation& cpy) = default; #ifdef COMPILER_SUPPORT_NOEXCEPT_DEFAULTED_MOVE - inline DummyUnitOperation& operator=(DummyUnitOperation&& cpy) CADET_NOEXCEPT = default; + inline DummyUnitOperation& operator=(DummyUnitOperation&& cpy) CADET_NOEXCEPT = default; #else - inline DummyUnitOperation& operator=(DummyUnitOperation&& cpy) = default; + inline DummyUnitOperation& operator=(DummyUnitOperation&& cpy) = default; #endif - inline void setup(cadet::UnitOpIdx unitOpIdx, unsigned int nComp, unsigned int nInletPorts, unsigned int nOutletPorts, bool canAccumulate) - { - _unitOpIdx = unitOpIdx; - _nComp = nComp; - _nInletPorts = nInletPorts; - _nOutletPorts = nOutletPorts; - _canAccumulate = canAccumulate; - - _inFlow.resize(nInletPorts, 0.0); - _outFlow.resize(nOutletPorts, 0.0); - } + inline void setup(cadet::UnitOpIdx unitOpIdx, unsigned int nComp, unsigned int nInletPorts, + unsigned int nOutletPorts, bool canAccumulate) + { + _unitOpIdx = unitOpIdx; + _nComp = nComp; + _nInletPorts = nInletPorts; + _nOutletPorts = nOutletPorts; + _canAccumulate = canAccumulate; + + _inFlow.resize(nInletPorts, 0.0); + _outFlow.resize(nOutletPorts, 0.0); + } - virtual cadet::UnitOpIdx unitOperationId() const CADET_NOEXCEPT { return _unitOpIdx; } - virtual const char* unitOperationName() const CADET_NOEXCEPT { return "DUMMY"; } + virtual cadet::UnitOpIdx unitOperationId() const CADET_NOEXCEPT + { + return _unitOpIdx; + } + virtual const char* unitOperationName() const CADET_NOEXCEPT + { + return "DUMMY"; + } - virtual bool setParameter(const cadet::ParameterId& pId, int value) { return false; } - virtual bool setParameter(const cadet::ParameterId& pId, double value) { return false; } - virtual bool setParameter(const cadet::ParameterId& pId, bool value) { return false; } - virtual bool hasParameter(const cadet::ParameterId& pId) const { return false; } - virtual std::unordered_map getAllParameterValues() const { return std::unordered_map(); } - virtual double getParameterDouble(const cadet::ParameterId& pId) const { return 0.0; } - virtual void useAnalyticJacobian(const bool analyticJac) { } + virtual bool setParameter(const cadet::ParameterId& pId, int value) + { + return false; + } + virtual bool setParameter(const cadet::ParameterId& pId, double value) + { + return false; + } + virtual bool setParameter(const cadet::ParameterId& pId, bool value) + { + return false; + } + virtual bool hasParameter(const cadet::ParameterId& pId) const + { + return false; + } + virtual std::unordered_map getAllParameterValues() const + { + return std::unordered_map(); + } + virtual double getParameterDouble(const cadet::ParameterId& pId) const + { + return 0.0; + } + virtual void useAnalyticJacobian(const bool analyticJac) + { + } #ifdef CADET_BENCHMARK_MODE - virtual std::vector benchmarkTimings() const { return std::vector(); } - virtual char const* const* benchmarkDescriptions() const { return nullptr; } + virtual std::vector benchmarkTimings() const + { + return std::vector(); + } + virtual char const* const* benchmarkDescriptions() const + { + return nullptr; + } #endif - virtual unsigned int numDofs() const CADET_NOEXCEPT { return (_nInletPorts + _nOutletPorts) * _nComp; } - virtual unsigned int numPureDofs() const CADET_NOEXCEPT { return _nOutletPorts * _nComp; } - - virtual bool usesAD() const CADET_NOEXCEPT { return false; } - virtual unsigned int requiredADdirs() const CADET_NOEXCEPT { return 0; } + virtual unsigned int numDofs() const CADET_NOEXCEPT + { + return (_nInletPorts + _nOutletPorts) * _nComp; + } + virtual unsigned int numPureDofs() const CADET_NOEXCEPT + { + return _nOutletPorts * _nComp; + } - virtual bool configureModelDiscretization(cadet::IParameterProvider& paramProvider, const cadet::IConfigHelper& helper) { return true; } - virtual bool configure(cadet::IParameterProvider& paramProvider) { return true; } + virtual bool usesAD() const CADET_NOEXCEPT + { + return false; + } + virtual unsigned int requiredADdirs() const CADET_NOEXCEPT + { + return 0; + } - virtual void reportSolution(cadet::ISolutionRecorder& reporter, double const* const solution) const { } - virtual void reportSolutionStructure(cadet::ISolutionRecorder& reporter) const { } + virtual bool configureModelDiscretization(cadet::IParameterProvider& paramProvider, + const cadet::IConfigHelper& helper) + { + return true; + } + virtual bool configure(cadet::IParameterProvider& paramProvider) + { + return true; + } - virtual bool setSensitiveParameter(const cadet::ParameterId& pId, unsigned int adDirection, double adValue) { return false; } - virtual void setSensitiveParameterValue(const cadet::ParameterId& id, double value) { } - virtual void clearSensParams() { } - virtual unsigned int numSensParams() const { return 0; } + virtual void reportSolution(cadet::ISolutionRecorder& reporter, double const* const solution) const + { + } + virtual void reportSolutionStructure(cadet::ISolutionRecorder& reporter) const + { + } - virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, const cadet::ConstSimulationState& simState, const cadet::AdJacobianParams& adJac) { } - virtual void applyInitialCondition(const cadet::SimulationState& simState) const { } - virtual void readInitialCondition(cadet::IParameterProvider& paramProvider) { } + virtual bool setSensitiveParameter(const cadet::ParameterId& pId, unsigned int adDirection, double adValue) + { + return false; + } + virtual void setSensitiveParameterValue(const cadet::ParameterId& id, double value) + { + } + virtual void clearSensParams() + { + } + virtual unsigned int numSensParams() const + { + return 0; + } - virtual int residual(const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, double* const res, cadet::util::ThreadLocalStorage& tls) - { - std::copy(simState.vecStateY, simState.vecStateY + numDofs(), res); - return 0; - } + virtual void notifyDiscontinuousSectionTransition(double t, unsigned int secIdx, + const cadet::ConstSimulationState& simState, + const cadet::AdJacobianParams& adJac) + { + } + virtual void applyInitialCondition(const cadet::SimulationState& simState) const + { + } + virtual void readInitialCondition(cadet::IParameterProvider& paramProvider) + { + } - virtual int jacobian(const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, double* const res, - const cadet::AdJacobianParams& adJac, cadet::util::ThreadLocalStorage& tls) - { - std::copy(simState.vecStateY, simState.vecStateY + numDofs(), res); - return 0; - } + virtual int residual(const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, + double* const res, cadet::util::ThreadLocalStorage& tls) + { + std::copy(simState.vecStateY, simState.vecStateY + numDofs(), res); + return 0; + } - virtual int residualWithJacobian(const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, double* const res, - const cadet::AdJacobianParams& adJac, cadet::util::ThreadLocalStorage& tls) - { - std::copy(simState.vecStateY, simState.vecStateY + numDofs(), res); - return 0; - } + virtual int jacobian(const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, + double* const res, const cadet::AdJacobianParams& adJac, cadet::util::ThreadLocalStorage& tls) + { + std::copy(simState.vecStateY, simState.vecStateY + numDofs(), res); + return 0; + } - virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, - const cadet::ConstSimulationState& simState) - { - return 0; - } + virtual int residualWithJacobian(const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, + double* const res, const cadet::AdJacobianParams& adJac, + cadet::util::ThreadLocalStorage& tls) + { + std::copy(simState.vecStateY, simState.vecStateY + numDofs(), res); + return 0; + } - virtual void prepareADvectors(const cadet::AdJacobianParams& adJac) const { } - virtual void initializeSensitivityStates(const std::vector& vecSensY) const { } + virtual int linearSolve(double t, double alpha, double tol, double* const rhs, double const* const weight, + const cadet::ConstSimulationState& simState) + { + return 0; + } - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { } - virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut) { } + virtual void prepareADvectors(const cadet::AdJacobianParams& adJac) const + { + } + virtual void initializeSensitivityStates(const std::vector& vecSensY) const + { + } - virtual unsigned int numComponents() const CADET_NOEXCEPT { return _nComp; } - virtual bool hasInlet() const CADET_NOEXCEPT { return _nInletPorts > 0; } - virtual bool hasOutlet() const CADET_NOEXCEPT { return _nOutletPorts > 0; } - virtual unsigned int numInletPorts() const CADET_NOEXCEPT { return _nInletPorts; } - virtual unsigned int numOutletPorts() const CADET_NOEXCEPT { return _nOutletPorts; } + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) + { + } + virtual void expandErrorTol(double const* errorSpec, unsigned int errorSpecSize, double* expandOut) + { + } - virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT { return (_nInletPorts + port) * _nComp; } - virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT { return 1u; } - virtual unsigned int localInletComponentIndex(unsigned int port) const CADET_NOEXCEPT { return port * _nComp; } - virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT { return 1u; } + virtual unsigned int numComponents() const CADET_NOEXCEPT + { + return _nComp; + } + virtual bool hasInlet() const CADET_NOEXCEPT + { + return _nInletPorts > 0; + } + virtual bool hasOutlet() const CADET_NOEXCEPT + { + return _nOutletPorts > 0; + } + virtual unsigned int numInletPorts() const CADET_NOEXCEPT + { + return _nInletPorts; + } + virtual unsigned int numOutletPorts() const CADET_NOEXCEPT + { + return _nOutletPorts; + } - virtual int residualSensFwdAdOnly(const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, cadet::active* const adRes, cadet::util::ThreadLocalStorage& tls) { return 0; } + virtual unsigned int localOutletComponentIndex(unsigned int port) const CADET_NOEXCEPT + { + return (_nInletPorts + port) * _nComp; + } + virtual unsigned int localOutletComponentStride(unsigned int port) const CADET_NOEXCEPT + { + return 1u; + } + virtual unsigned int localInletComponentIndex(unsigned int port) const CADET_NOEXCEPT + { + return port * _nComp; + } + virtual unsigned int localInletComponentStride(unsigned int port) const CADET_NOEXCEPT + { + return 1u; + } - virtual int residualSensFwdCombine(const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, - const std::vector& yS, const std::vector& ySdot, const std::vector& resS, cadet::active const* adRes, - double* const tmp1, double* const tmp2, double* const tmp3) { return 0; } + virtual int residualSensFwdAdOnly(const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, + cadet::active* const adRes, cadet::util::ThreadLocalStorage& tls) + { + return 0; + } - virtual int residualSensFwdWithJacobian(const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, const cadet::AdJacobianParams& adJac, cadet::util::ThreadLocalStorage& tls) { return 0; } + virtual int residualSensFwdCombine(const cadet::SimulationTime& simTime, + const cadet::ConstSimulationState& simState, + const std::vector& yS, const std::vector& ySdot, + const std::vector& resS, cadet::active const* adRes, double* const tmp1, + double* const tmp2, double* const tmp3) + { + return 0; + } - virtual void consistentInitialState(const cadet::SimulationTime& simTime, double* const vecStateY, const cadet::AdJacobianParams& adJac, double errorTol, cadet::util::ThreadLocalStorage& tls) - { - std::fill_n(vecStateY + _nInletPorts * _nComp, _nOutletPorts * _nComp, 0.0); - } + virtual int residualSensFwdWithJacobian(const cadet::SimulationTime& simTime, + const cadet::ConstSimulationState& simState, + const cadet::AdJacobianParams& adJac, cadet::util::ThreadLocalStorage& tls) + { + return 0; + } - virtual void consistentInitialTimeDerivative(const cadet::SimulationTime& simTime, double const* vecStateY, double* const vecStateYdot, cadet::util::ThreadLocalStorage& tls) - { - std::fill_n(vecStateYdot + _nInletPorts * _nComp, _nOutletPorts * _nComp, 0.0); - } + virtual void consistentInitialState(const cadet::SimulationTime& simTime, double* const vecStateY, + const cadet::AdJacobianParams& adJac, double errorTol, + cadet::util::ThreadLocalStorage& tls) + { + std::fill_n(vecStateY + _nInletPorts * _nComp, _nOutletPorts * _nComp, 0.0); + } - virtual void leanConsistentInitialState(const cadet::SimulationTime& simTime, double* const vecStateY, const cadet::AdJacobianParams& adJac, double errorTol, cadet::util::ThreadLocalStorage& tls) { } - virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, double* const vecStateYdot, double* const res, cadet::util::ThreadLocalStorage& tls) { } + virtual void consistentInitialTimeDerivative(const cadet::SimulationTime& simTime, double const* vecStateY, + double* const vecStateYdot, cadet::util::ThreadLocalStorage& tls) + { + std::fill_n(vecStateYdot + _nInletPorts * _nComp, _nOutletPorts * _nComp, 0.0); + } - virtual void consistentInitialSensitivity(const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, std::vector& vecSensY, - std::vector& vecSensYdot, cadet::active const* const adRes, cadet::util::ThreadLocalStorage& tls) { } - virtual void leanConsistentInitialSensitivity(const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, std::vector& vecSensY, - std::vector& vecSensYdot, cadet::active const* const adRes, cadet::util::ThreadLocalStorage& tls) { } + virtual void leanConsistentInitialState(const cadet::SimulationTime& simTime, double* const vecStateY, + const cadet::AdJacobianParams& adJac, double errorTol, + cadet::util::ThreadLocalStorage& tls) + { + } + virtual void leanConsistentInitialTimeDerivative(double t, double const* const vecStateY, + double* const vecStateYdot, double* const res, + cadet::util::ThreadLocalStorage& tls) + { + } - virtual void setExternalFunctions(cadet::IExternalFunction** extFuns, unsigned int size) { } + virtual void consistentInitialSensitivity(const cadet::SimulationTime& simTime, + const cadet::ConstSimulationState& simState, + std::vector& vecSensY, std::vector& vecSensYdot, + cadet::active const* const adRes, cadet::util::ThreadLocalStorage& tls) + { + } + virtual void leanConsistentInitialSensitivity(const cadet::SimulationTime& simTime, + const cadet::ConstSimulationState& simState, + std::vector& vecSensY, std::vector& vecSensYdot, + cadet::active const* const adRes, + cadet::util::ThreadLocalStorage& tls) + { + } - virtual void setFlowRates(cadet::active const* in, cadet::active const* out) CADET_NOEXCEPT - { - std::copy(in, in + _nInletPorts, _inFlow.begin()); - std::copy(out, out + _nOutletPorts, _outFlow.begin()); - } + virtual void setExternalFunctions(cadet::IExternalFunction** extFuns, unsigned int size) + { + } - virtual bool canAccumulate() const CADET_NOEXCEPT { return _canAccumulate; } + virtual void setFlowRates(cadet::active const* in, cadet::active const* out) CADET_NOEXCEPT + { + std::copy(in, in + _nInletPorts, _inFlow.begin()); + std::copy(out, out + _nOutletPorts, _outFlow.begin()); + } - virtual void multiplyWithJacobian(const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, double const* yS, double alpha, double beta, double* ret) - { - // dF / dy = I (identity matrix) - for (unsigned int i = 0; i < numDofs(); ++i) - { - ret[i] = alpha * yS[i] + beta * ret[i]; - } - } + virtual bool canAccumulate() const CADET_NOEXCEPT + { + return _canAccumulate; + } - virtual void multiplyWithDerivativeJacobian(const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, double const* sDot, double* ret) + virtual void multiplyWithJacobian(const cadet::SimulationTime& simTime, const cadet::ConstSimulationState& simState, + double const* yS, double alpha, double beta, double* ret) + { + // dF / dy = I (identity matrix) + for (unsigned int i = 0; i < numDofs(); ++i) { - std::fill_n(ret, numDofs(), 0.0); + ret[i] = alpha * yS[i] + beta * ret[i]; } + } - virtual unsigned int threadLocalMemorySize() const CADET_NOEXCEPT { return 0; } - - inline const std::vector& inFlow() const CADET_NOEXCEPT { return _inFlow; } - inline const std::vector& outFlow() const CADET_NOEXCEPT { return _outFlow; } + virtual void multiplyWithDerivativeJacobian(const cadet::SimulationTime& simTime, + const cadet::ConstSimulationState& simState, double const* sDot, + double* ret) + { + std::fill_n(ret, numDofs(), 0.0); + } - protected: - cadet::UnitOpIdx _unitOpIdx; - unsigned int _nComp; - unsigned int _nInletPorts; - unsigned int _nOutletPorts; - bool _canAccumulate; - std::vector _inFlow; - std::vector _outFlow; - }; + virtual unsigned int threadLocalMemorySize() const CADET_NOEXCEPT + { + return 0; + } - cadet::JsonParameterProvider createSystemConfig(const std::vector& connections) + inline const std::vector& inFlow() const CADET_NOEXCEPT { - cadet::JsonParameterProvider jpp(R"json({ + return _inFlow; + } + inline const std::vector& outFlow() const CADET_NOEXCEPT + { + return _outFlow; + } + +protected: + cadet::UnitOpIdx _unitOpIdx; + unsigned int _nComp; + unsigned int _nInletPorts; + unsigned int _nOutletPorts; + bool _canAccumulate; + std::vector _inFlow; + std::vector _outFlow; +}; + +cadet::JsonParameterProvider createSystemConfig(const std::vector& connections) +{ + cadet::JsonParameterProvider jpp(R"json({ "solver": { "MAX_KRYLOV": 0, @@ -233,353 +399,365 @@ namespace } })json"); - jpp.pushScope("connections"); - jpp.pushScope("switch_000"); + jpp.pushScope("connections"); + jpp.pushScope("switch_000"); - jpp.set("CONNECTIONS", connections); + jpp.set("CONNECTIONS", connections); - jpp.popScope(); - jpp.popScope(); + jpp.popScope(); + jpp.popScope(); - return jpp; - } + return jpp; +} +std::vector calculateDofOffsets(const cadet::model::ModelSystem& sys) +{ + std::vector dofOffsets(sys.numModels() + 1, 0u); + for (unsigned int i = 0; i < sys.numModels(); ++i) + dofOffsets[i + 1] = dofOffsets[i] + sys.getUnitOperationModel(i)->numDofs(); - std::vector calculateDofOffsets(const cadet::model::ModelSystem& sys) - { - std::vector dofOffsets(sys.numModels() + 1, 0u); - for (unsigned int i = 0; i < sys.numModels(); ++i) - dofOffsets[i+1] = dofOffsets[i] + sys.getUnitOperationModel(i)->numDofs(); + return dofOffsets; +} - return dofOffsets; - } +std::vector calculateJacobian(cadet::model::ModelSystem& sys) +{ + const unsigned int nDof = sys.numDofs(); + std::vector jac(nDof * nDof, 0.0); + std::vector y(nDof, 0.0); - std::vector calculateJacobian(cadet::model::ModelSystem& sys) + const cadet::SimulationTime simTime{0.0, 0u}; + const cadet::ConstSimulationState simState{y.data(), y.data()}; + for (unsigned int i = 0; i < nDof; ++i) { - const unsigned int nDof = sys.numDofs(); - std::vector jac(nDof * nDof, 0.0); - std::vector y(nDof, 0.0); + y[i] = 1.0; + sys.multiplyWithJacobian(simTime, simState, y.data(), 1.0, 0.0, jac.data() + i * nDof); + y[i] = 0.0; + } - const cadet::SimulationTime simTime{0.0, 0u}; - const cadet::ConstSimulationState simState{y.data(), y.data()}; - for (unsigned int i = 0; i < nDof; ++i) - { - y[i] = 1.0; - sys.multiplyWithJacobian(simTime, simState, y.data(), 1.0, 0.0, jac.data() + i * nDof); - y[i] = 0.0; - } + return jac; +} - return jac; - } +void checkDedicatedInletDofConnections(cadet::model::ModelSystem& sys, const std::vector& dofOffsets, + const std::vector& jac) +{ + const unsigned int nDof = sys.numDofs(); - void checkDedicatedInletDofConnections(cadet::model::ModelSystem& sys, const std::vector& dofOffsets, const std::vector& jac) + // Check that the inlet DOFs are taken in with a 1.0 in each unit operation + // That is, the Jacobian of the unit operation should contain the identity matrix at the inlet DOFs + for (unsigned int i = 0; i < sys.numModels(); ++i) { - const unsigned int nDof = sys.numDofs(); + cadet::IUnitOperation const* const m = sys.getUnitOperationModel(i); - // Check that the inlet DOFs are taken in with a 1.0 in each unit operation - // That is, the Jacobian of the unit operation should contain the identity matrix at the inlet DOFs - for (unsigned int i = 0; i < sys.numModels(); ++i) - { - cadet::IUnitOperation const* const m = sys.getUnitOperationModel(i); + // Jacobian is in column-major format (columns of Jacobian are concatenated to a big array) + // Move to first column of unit operation and inside this column to the first DOF of the unit operation + double const* local = jac.data() + dofOffsets[i] * nDof + dofOffsets[i]; - // Jacobian is in column-major format (columns of Jacobian are concatenated to a big array) - // Move to first column of unit operation and inside this column to the first DOF of the unit operation - double const* local = jac.data() + dofOffsets[i] * nDof + dofOffsets[i]; + for (unsigned int port = 0; port < m->numInletPorts(); ++port) + { + const unsigned int inletOffset = m->localInletComponentIndex(port); + const unsigned int inletStride = m->localInletComponentStride(port); - for (unsigned int port = 0; port < m->numInletPorts(); ++port) + for (unsigned int comp = 0; comp < m->numComponents(); ++comp, local += nDof) { - const unsigned int inletOffset = m->localInletComponentIndex(port); - const unsigned int inletStride = m->localInletComponentStride(port); + CAPTURE(i); + CAPTURE(port); + CAPTURE(comp); + CAPTURE(inletOffset + comp * inletStride); + CAPTURE(dofOffsets[i]); + CHECK(local[inletOffset + comp * inletStride] == 1.0); - for (unsigned int comp = 0; comp < m->numComponents(); ++comp, local += nDof) - { - CAPTURE(i); - CAPTURE(port); - CAPTURE(comp); - CAPTURE(inletOffset + comp * inletStride); - CAPTURE(dofOffsets[i]); - CHECK(local[inletOffset + comp * inletStride] == 1.0); - - // This loop moves the Jacobian pointer to the next column - } + // This loop moves the Jacobian pointer to the next column } } + } - // Check that the inlet DOFs are fed with a -1.0 by the model system - // That is, in the same row of the identity matrix (see above), - // the system Jacobian has a negative identity matrix in the right macro column - unsigned int nInletDofsUpToNow = 0; - for (unsigned int i = 0; i < sys.numModels(); ++i) - { - cadet::IUnitOperation const* const m = sys.getUnitOperationModel(i); + // Check that the inlet DOFs are fed with a -1.0 by the model system + // That is, in the same row of the identity matrix (see above), + // the system Jacobian has a negative identity matrix in the right macro column + unsigned int nInletDofsUpToNow = 0; + for (unsigned int i = 0; i < sys.numModels(); ++i) + { + cadet::IUnitOperation const* const m = sys.getUnitOperationModel(i); - // Jacobian is in column-major format (columns of Jacobian are concatenated to a big array) - // Move to first column behind all unit operations, from there to the column of the current - // unit operation and inside this column to the first DOF of the current unit operation - double const* local = jac.data() + (dofOffsets.back() + nInletDofsUpToNow) * nDof + dofOffsets[i]; + // Jacobian is in column-major format (columns of Jacobian are concatenated to a big array) + // Move to first column behind all unit operations, from there to the column of the current + // unit operation and inside this column to the first DOF of the current unit operation + double const* local = jac.data() + (dofOffsets.back() + nInletDofsUpToNow) * nDof + dofOffsets[i]; - for (unsigned int port = 0; port < m->numInletPorts(); ++port) + for (unsigned int port = 0; port < m->numInletPorts(); ++port) + { + const unsigned int inletOffset = m->localInletComponentIndex(port); + const unsigned int inletStride = m->localInletComponentStride(port); + + for (unsigned int comp = 0; comp < m->numComponents(); ++comp, local += nDof, ++nInletDofsUpToNow) { - const unsigned int inletOffset = m->localInletComponentIndex(port); - const unsigned int inletStride = m->localInletComponentStride(port); + CAPTURE(i); + CAPTURE(port); + CAPTURE(comp); + CAPTURE(inletOffset + comp * inletStride); + CAPTURE(dofOffsets[i]); + CHECK(local[inletOffset + comp * inletStride] == -1.0); - for (unsigned int comp = 0; comp < m->numComponents(); ++comp, local += nDof, ++nInletDofsUpToNow) - { - CAPTURE(i); - CAPTURE(port); - CAPTURE(comp); - CAPTURE(inletOffset + comp * inletStride); - CAPTURE(dofOffsets[i]); - CHECK(local[inletOffset + comp * inletStride] == -1.0); - - // This loop moves the Jacobian pointer to the next column - } + // This loop moves the Jacobian pointer to the next column } } } +} - void checkRightMacroColumn(cadet::model::ModelSystem& sys, const std::vector& dofOffsets, const std::vector& jac) - { - // The right macro column should only have two entries per column (-1 in the unit operation row, +1 in the coupling DOF row) +void checkRightMacroColumn(cadet::model::ModelSystem& sys, const std::vector& dofOffsets, + const std::vector& jac) +{ + // The right macro column should only have two entries per column (-1 in the unit operation row, +1 in the coupling + // DOF row) - const unsigned int nDof = sys.numDofs(); - double const* local = jac.data() + nDof * dofOffsets.back(); - for (unsigned int c = dofOffsets.back(); c < nDof; ++c, local += nDof) + const unsigned int nDof = sys.numDofs(); + double const* local = jac.data() + nDof * dofOffsets.back(); + for (unsigned int c = dofOffsets.back(); c < nDof; ++c, local += nDof) + { + unsigned int count = 0; + for (unsigned int r = 0; r < nDof; ++r) { - unsigned int count = 0; - for (unsigned int r = 0; r < nDof; ++r) - { - if (local[r] != 0.0) - ++count; - } - - CAPTURE(c); - CHECK(count == 2); + if (local[r] != 0.0) + ++count; } + + CAPTURE(c); + CHECK(count == 2); } +} - bool connectedPreviously(const std::vector& connections, unsigned int curPos, unsigned int uoSource, int portSource, unsigned int uoDest, unsigned int portDest) +bool connectedPreviously(const std::vector& connections, unsigned int curPos, unsigned int uoSource, + int portSource, unsigned int uoDest, unsigned int portDest) +{ + double const* conRow = connections.data(); + for (unsigned int i = 0; i < curPos; ++i, conRow += 7) { - double const* conRow = connections.data(); - for (unsigned int i = 0; i < curPos; ++i, conRow += 7) - { - if (static_cast(conRow[0]) != uoSource) - continue; + if (static_cast(conRow[0]) != uoSource) + continue; - if (static_cast(conRow[1]) != uoDest) - continue; + if (static_cast(conRow[1]) != uoDest) + continue; - if (((conRow[2] < 0.0) || (static_cast(conRow[2]) == portSource)) && ((conRow[3] < 0.0) || (static_cast(conRow[3]) == portDest))) - return true; - } - - return false; + if (((conRow[2] < 0.0) || (static_cast(conRow[2]) == portSource)) && + ((conRow[3] < 0.0) || (static_cast(conRow[3]) == portDest))) + return true; } - void checkCouplingRow(cadet::model::ModelSystem& sys, const std::vector& dofOffsets, const std::vector& jac, - const std::vector& connections, unsigned int row, unsigned int uoDest, unsigned int portDest, unsigned int compDest, - std::set>& nonZeros) + return false; +} + +void checkCouplingRow(cadet::model::ModelSystem& sys, const std::vector& dofOffsets, + const std::vector& jac, const std::vector& connections, unsigned int row, + unsigned int uoDest, unsigned int portDest, unsigned int compDest, + std::set>& nonZeros) +{ + // Find total influx + double totalInFlow = 0.0; + + double const* conRow = connections.data(); + for (std::size_t i = 0; i < connections.size() / 7; ++i, conRow += 7) { - // Find total influx - double totalInFlow = 0.0; + if (static_cast(conRow[1]) != uoDest) + continue; - double const* conRow = connections.data(); - for (std::size_t i = 0; i < connections.size() / 7; ++i, conRow += 7) + if ((conRow[3] < 0.0) || (static_cast(conRow[3]) == portDest)) { - if (static_cast(conRow[1]) != uoDest) - continue; + if (!connectedPreviously(connections, i, static_cast(conRow[0]), static_cast(conRow[2]), + uoDest, portDest)) + totalInFlow += conRow[6]; + } + } - if ((conRow[3] < 0.0) || (static_cast(conRow[3]) == portDest)) + // Check connection in Jacobian + const unsigned int nDof = sys.numDofs(); + conRow = connections.data(); + for (std::size_t i = 0; i < connections.size() / 7; ++i, conRow += 7) + { + if (static_cast(conRow[1]) != uoDest) + continue; + + const unsigned int uoSource = static_cast(conRow[0]); + cadet::IUnitOperation const* const m = sys.getUnitOperationModel(uoSource); + const double jacVal = -conRow[6] / totalInFlow; + + if (conRow[3] < 0.0) + { + if (conRow[5] < 0.0) { - if (!connectedPreviously(connections, i, static_cast(conRow[0]), static_cast(conRow[2]), uoDest, portDest)) - totalInFlow += conRow[6]; + // Connect all components of all ports + const unsigned int pos = + m->localOutletComponentIndex(portDest) + m->localOutletComponentStride(portDest) * compDest; + CAPTURE(uoSource); + CAPTURE(uoDest); + CAPTURE(portDest); + CAPTURE(compDest); + CHECK(jac[nDof * (dofOffsets[uoSource] + pos) + dofOffsets.back() + row] == jacVal); + + nonZeros.insert(std::make_pair(row, dofOffsets[uoSource] + pos)); + } + else if (static_cast(conRow[5]) == compDest) + { + // Connect specific component of all ports + const unsigned int compSource = static_cast(conRow[4]); + const unsigned int pos = + m->localOutletComponentIndex(portDest) + m->localOutletComponentStride(portDest) * compSource; + CAPTURE(uoSource); + CAPTURE(uoDest); + CAPTURE(portDest); + CAPTURE(compSource); + CAPTURE(compDest); + CHECK(jac[nDof * (dofOffsets[uoSource] + pos) + dofOffsets.back() + row] == jacVal); + + nonZeros.insert(std::make_pair(row, dofOffsets[uoSource] + pos)); } } - - // Check connection in Jacobian - const unsigned int nDof = sys.numDofs(); - conRow = connections.data(); - for (std::size_t i = 0; i < connections.size() / 7; ++i, conRow += 7) + else if (static_cast(conRow[3]) == portDest) { - if (static_cast(conRow[1]) != uoDest) - continue; - - const unsigned int uoSource = static_cast(conRow[0]); - cadet::IUnitOperation const* const m = sys.getUnitOperationModel(uoSource); - const double jacVal = -conRow[6] / totalInFlow; - - if (conRow[3] < 0.0) + const unsigned int portSource = static_cast(conRow[2]); + if (conRow[5] < 0.0) { - if (conRow[5] < 0.0) - { - // Connect all components of all ports - const unsigned int pos = m->localOutletComponentIndex(portDest) + m->localOutletComponentStride(portDest) * compDest; - CAPTURE(uoSource); - CAPTURE(uoDest); - CAPTURE(portDest); - CAPTURE(compDest); - CHECK(jac[nDof * (dofOffsets[uoSource] + pos) + dofOffsets.back() + row] == jacVal); - - nonZeros.insert(std::make_pair(row, dofOffsets[uoSource] + pos)); - } - else if (static_cast(conRow[5]) == compDest) - { - // Connect specific component of all ports - const unsigned int compSource = static_cast(conRow[4]); - const unsigned int pos = m->localOutletComponentIndex(portDest) + m->localOutletComponentStride(portDest) * compSource; - CAPTURE(uoSource); - CAPTURE(uoDest); - CAPTURE(portDest); - CAPTURE(compSource); - CAPTURE(compDest); - CHECK(jac[nDof * (dofOffsets[uoSource] + pos) + dofOffsets.back() + row] == jacVal); - - nonZeros.insert(std::make_pair(row, dofOffsets[uoSource] + pos)); - } + // Connect all components of specific port + const unsigned int pos = + m->localOutletComponentIndex(portSource) + m->localOutletComponentStride(portSource) * compDest; + CAPTURE(uoSource); + CAPTURE(uoDest); + CAPTURE(portSource); + CAPTURE(portDest); + CAPTURE(compDest); + CHECK(jac[nDof * (dofOffsets[uoSource] + pos) + dofOffsets.back() + row] == jacVal); + + nonZeros.insert(std::make_pair(row, dofOffsets[uoSource] + pos)); } - else if (static_cast(conRow[3]) == portDest) + else if (static_cast(conRow[5]) == compDest) { - const unsigned int portSource = static_cast(conRow[2]); - if (conRow[5] < 0.0) - { - // Connect all components of specific port - const unsigned int pos = m->localOutletComponentIndex(portSource) + m->localOutletComponentStride(portSource) * compDest; - CAPTURE(uoSource); - CAPTURE(uoDest); - CAPTURE(portSource); - CAPTURE(portDest); - CAPTURE(compDest); - CHECK(jac[nDof * (dofOffsets[uoSource] + pos) + dofOffsets.back() + row] == jacVal); - - nonZeros.insert(std::make_pair(row, dofOffsets[uoSource] + pos)); - } - else if (static_cast(conRow[5]) == compDest) - { - // Connect specific component of specific port - const unsigned int compSource = static_cast(conRow[4]); - const unsigned int pos = m->localOutletComponentIndex(portSource) + m->localOutletComponentStride(portSource) * compSource; - CAPTURE(uoSource); - CAPTURE(uoDest); - CAPTURE(portSource); - CAPTURE(portDest); - CAPTURE(compSource); - CAPTURE(compDest); - CHECK(jac[nDof * (dofOffsets[uoSource] + pos) + dofOffsets.back() + row] == jacVal); - - nonZeros.insert(std::make_pair(row, dofOffsets[uoSource] + pos)); - } + // Connect specific component of specific port + const unsigned int compSource = static_cast(conRow[4]); + const unsigned int pos = + m->localOutletComponentIndex(portSource) + m->localOutletComponentStride(portSource) * compSource; + CAPTURE(uoSource); + CAPTURE(uoDest); + CAPTURE(portSource); + CAPTURE(portDest); + CAPTURE(compSource); + CAPTURE(compDest); + CHECK(jac[nDof * (dofOffsets[uoSource] + pos) + dofOffsets.back() + row] == jacVal); + + nonZeros.insert(std::make_pair(row, dofOffsets[uoSource] + pos)); } } } +} - void checkCouplingDofs(cadet::model::ModelSystem& sys, const std::vector& dofOffsets, const std::vector& jac, const std::vector& connections) - { - const unsigned int nDof = sys.numDofs(); +void checkCouplingDofs(cadet::model::ModelSystem& sys, const std::vector& dofOffsets, + const std::vector& jac, const std::vector& connections) +{ + const unsigned int nDof = sys.numDofs(); - // Check identity matrix of coupling DOFs in bottom macro row + // Check identity matrix of coupling DOFs in bottom macro row - // Move to beginning of identitiy matrix - double const* local = jac.data() + dofOffsets.back() * nDof + dofOffsets.back(); - for (unsigned int i = 0; i < nDof - dofOffsets.back(); ++i, local += nDof + 1) - { - CHECK(local[0] == 1.0); + // Move to beginning of identitiy matrix + double const* local = jac.data() + dofOffsets.back() * nDof + dofOffsets.back(); + for (unsigned int i = 0; i < nDof - dofOffsets.back(); ++i, local += nDof + 1) + { + CHECK(local[0] == 1.0); - // This loop advances the Jacobian pointer to the next column and by one additional row - } + // This loop advances the Jacobian pointer to the next column and by one additional row + } - // Check remaining entries in bottom macro row (corresponding to outlet DOFs) - std::set> nonZeros; + // Check remaining entries in bottom macro row (corresponding to outlet DOFs) + std::set> nonZeros; - // Iterate over coupling dofs (rows in bottom macro row) - unsigned int idxCoupling = 0; - for (unsigned int uoDest = 0; uoDest < sys.numModels(); ++uoDest) - { - cadet::IUnitOperation const* const m = sys.getUnitOperationModel(uoDest); + // Iterate over coupling dofs (rows in bottom macro row) + unsigned int idxCoupling = 0; + for (unsigned int uoDest = 0; uoDest < sys.numModels(); ++uoDest) + { + cadet::IUnitOperation const* const m = sys.getUnitOperationModel(uoDest); - if (!m->hasInlet()) - continue; + if (!m->hasInlet()) + continue; - for (unsigned int portDest = 0; portDest < m->numInletPorts(); ++portDest) + for (unsigned int portDest = 0; portDest < m->numInletPorts(); ++portDest) + { + for (unsigned int compDest = 0; compDest < m->numComponents(); ++compDest, ++idxCoupling) { - for (unsigned int compDest = 0; compDest < m->numComponents(); ++compDest, ++idxCoupling) - { - checkCouplingRow(sys, dofOffsets, jac, connections, idxCoupling, uoDest, portDest, compDest, nonZeros); - } + checkCouplingRow(sys, dofOffsets, jac, connections, idxCoupling, uoDest, portDest, compDest, nonZeros); } } + } - // Make sure that the remaining entries are all zero - for (unsigned int col = 0; col < dofOffsets.back(); ++col) + // Make sure that the remaining entries are all zero + for (unsigned int col = 0; col < dofOffsets.back(); ++col) + { + for (unsigned int row = 0; row < nDof - dofOffsets.back(); ++row) { - for (unsigned int row = 0; row < nDof - dofOffsets.back(); ++row) - { - if (nonZeros.find(std::make_pair(row, col)) != nonZeros.end()) - continue; + if (nonZeros.find(std::make_pair(row, col)) != nonZeros.end()) + continue; - CHECK(jac[nDof * col + dofOffsets.back() + row] == 0.0); - } + CHECK(jac[nDof * col + dofOffsets.back() + row] == 0.0); } } +} - void checkCouplingJacobian(const std::vector sysDescription, const std::vector& connections, const std::vector& inFlow, const std::vector& outFlow) - { - cadet::IModelBuilder* const mb = cadet::createModelBuilder(); - REQUIRE(nullptr != mb); - - cadet::IModelSystem* const cadSys = mb->createSystem(); - REQUIRE(cadSys); - cadet::model::ModelSystem* const sys = reinterpret_cast(cadSys); +void checkCouplingJacobian(const std::vector sysDescription, const std::vector& connections, + const std::vector& inFlow, const std::vector& outFlow) +{ + cadet::IModelBuilder* const mb = cadet::createModelBuilder(); + REQUIRE(nullptr != mb); - const std::size_t numUnits = sysDescription.size() / 4; - unsigned int const* cd = sysDescription.data(); - for (std::size_t i = 0; i < numUnits; ++i, cd += 4) - sys->addModel(new DummyUnitOperation(i, cd[0], cd[1], cd[2], cd[3])); + cadet::IModelSystem* const cadSys = mb->createSystem(); + REQUIRE(cadSys); + cadet::model::ModelSystem* const sys = reinterpret_cast(cadSys); - DummyConfigHelper dch; - cadet::JsonParameterProvider jpp = createSystemConfig(connections); - REQUIRE(sys->configureModelDiscretization(jpp, dch)); - REQUIRE(sys->configure(jpp)); + const std::size_t numUnits = sysDescription.size() / 4; + unsigned int const* cd = sysDescription.data(); + for (std::size_t i = 0; i < numUnits; ++i, cd += 4) + sys->addModel(new DummyUnitOperation(i, cd[0], cd[1], cd[2], cd[3])); - // Setup matrices - const cadet::AdJacobianParams noParams{nullptr, nullptr, 0u}; - sys->notifyDiscontinuousSectionTransition(0.0, 0u, {nullptr, nullptr}, noParams); + DummyConfigHelper dch; + cadet::JsonParameterProvider jpp = createSystemConfig(connections); + REQUIRE(sys->configureModelDiscretization(jpp, dch)); + REQUIRE(sys->configure(jpp)); - const std::vector dofOffsets = calculateDofOffsets(*sys); - const std::vector jac = calculateJacobian(*sys); + // Setup matrices + const cadet::AdJacobianParams noParams{nullptr, nullptr, 0u}; + sys->notifyDiscontinuousSectionTransition(0.0, 0u, {nullptr, nullptr}, noParams); - checkDedicatedInletDofConnections(*sys, dofOffsets, jac); - checkRightMacroColumn(*sys, dofOffsets, jac); - checkCouplingDofs(*sys, dofOffsets, jac, connections); + const std::vector dofOffsets = calculateDofOffsets(*sys); + const std::vector jac = calculateJacobian(*sys); - // Check values of setFlowRate() received by unit operation models - double const* refInFlow = inFlow.data(); - double const* refOutFlow = outFlow.data(); - for (unsigned int model = 0; model < sys->numModels(); ++model) - { - DummyUnitOperation const* const m = static_cast(sys->getUnitOperationModel(model)); + checkDedicatedInletDofConnections(*sys, dofOffsets, jac); + checkRightMacroColumn(*sys, dofOffsets, jac); + checkCouplingDofs(*sys, dofOffsets, jac, connections); - const std::vector& unitInFlow = m->inFlow(); - for (unsigned int port = 0; port < m->numInletPorts(); ++port, ++refInFlow) - { - CAPTURE(model); - CAPTURE(port); - CHECK(*refInFlow == static_cast(unitInFlow[port])); - } + // Check values of setFlowRate() received by unit operation models + double const* refInFlow = inFlow.data(); + double const* refOutFlow = outFlow.data(); + for (unsigned int model = 0; model < sys->numModels(); ++model) + { + DummyUnitOperation const* const m = static_cast(sys->getUnitOperationModel(model)); - const std::vector& unitOutFlow = m->outFlow(); - for (unsigned int port = 0; port < m->numOutletPorts(); ++port, ++refOutFlow) - { - CAPTURE(model); - CAPTURE(port); - CHECK(*refOutFlow == static_cast(unitOutFlow[port])); - } + const std::vector& unitInFlow = m->inFlow(); + for (unsigned int port = 0; port < m->numInletPorts(); ++port, ++refInFlow) + { + CAPTURE(model); + CAPTURE(port); + CHECK(*refInFlow == static_cast(unitInFlow[port])); } - destroyModelBuilder(mb); + const std::vector& unitOutFlow = m->outFlow(); + for (unsigned int port = 0; port < m->numOutletPorts(); ++port, ++refOutFlow) + { + CAPTURE(model); + CAPTURE(port); + CHECK(*refOutFlow == static_cast(unitOutFlow[port])); + } } + destroyModelBuilder(mb); } +} // namespace + TEST_CASE("ModelSystem Jacobian AD vs analytic", "[ModelSystem],[Jacobian],[AD]") { cadet::IModelBuilder* const mb = cadet::createModelBuilder(); @@ -599,12 +777,12 @@ TEST_CASE("ModelSystem Jacobian AD vs analytic", "[ModelSystem],[Jacobian],[AD]" const std::vector secTimes = jpp.getDoubleArray("SECTION_TIMES"); std::vector secCont(secTimes.size() - 2, false); - if (jpp.exists("SECTION_CONTINUITY")) + if (jpp.exists("SECTION_CONTINUITY")) secCont = jpp.getBoolArray("SECTION_CONTINUITY"); jpp.popScope(); jpp.popScope(); - + // Create and configure ModelSystem jpp.pushScope("model"); cadet::test::column::setNumAxialCells(jpp, 10); @@ -646,8 +824,10 @@ TEST_CASE("ModelSystem Jacobian AD vs analytic", "[ModelSystem],[Jacobian],[AD]" std::vector jacCol2(nDof, 0.0); // Fill state vector with some values - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); - cadet::test::util::populate(yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); // Setup matrices sysAna->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, noParams); @@ -655,24 +835,48 @@ TEST_CASE("ModelSystem Jacobian AD vs analytic", "[ModelSystem],[Jacobian],[AD]" // Compute state Jacobian const cadet::SimulationTime simTime{0.0, 0u}; - sysAna->residualWithJacobian(simTime, cadet::ConstSimulationState{y.data(), yDot.data()}, jacDir.data(), noParams); - sysAD->residualWithJacobian(simTime, cadet::ConstSimulationState{y.data(), yDot.data()}, jacDir.data(), adParams); + sysAna->residualWithJacobian(simTime, cadet::ConstSimulationState{y.data(), yDot.data()}, jacDir.data(), + noParams); + sysAD->residualWithJacobian(simTime, cadet::ConstSimulationState{y.data(), yDot.data()}, jacDir.data(), + adParams); std::fill(jacDir.begin(), jacDir.end(), 0.0); // Compare Jacobians cadet::test::checkJacobianPatternFD( - [sysAna, &yDot](double const* lDir, double* res) -> void { sysAna->residual(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{lDir, yDot.data()}, res); }, - [sysAD, &y, &yDot](double const* lDir, double* res) -> void { sysAD->multiplyWithJacobian(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), yDot.data()}, lDir, 1.0, 0.0, res); }, + [sysAna, &yDot](double const* lDir, double* res) -> void { + sysAna->residual(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{lDir, yDot.data()}, + res); + }, + [sysAD, &y, &yDot](double const* lDir, double* res) -> void { + sysAD->multiplyWithJacobian(cadet::SimulationTime{0.0, 0u}, + cadet::ConstSimulationState{y.data(), yDot.data()}, lDir, 1.0, 0.0, + res); + }, y.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), nDof); cadet::test::checkJacobianPatternFD( - [sysAna, &yDot](double const* lDir, double* res) -> void { sysAna->residual(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{lDir, yDot.data()}, res); }, - [sysAna, &y, &yDot](double const* lDir, double* res) -> void { sysAna->multiplyWithJacobian(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), yDot.data()}, lDir, 1.0, 0.0, res); }, + [sysAna, &yDot](double const* lDir, double* res) -> void { + sysAna->residual(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{lDir, yDot.data()}, + res); + }, + [sysAna, &y, &yDot](double const* lDir, double* res) -> void { + sysAna->multiplyWithJacobian(cadet::SimulationTime{0.0, 0u}, + cadet::ConstSimulationState{y.data(), yDot.data()}, lDir, 1.0, 0.0, + res); + }, y.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), nDof); cadet::test::compareJacobian( - [sysAna, &y, &yDot](double const* lDir, double* res) -> void { sysAna->multiplyWithJacobian(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), yDot.data()}, lDir, 1.0, 0.0, res); }, - [sysAD, &y, &yDot](double const* lDir, double* res) -> void { sysAD->multiplyWithJacobian(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), yDot.data()}, lDir, 1.0, 0.0, res); }, + [sysAna, &y, &yDot](double const* lDir, double* res) -> void { + sysAna->multiplyWithJacobian(cadet::SimulationTime{0.0, 0u}, + cadet::ConstSimulationState{y.data(), yDot.data()}, lDir, 1.0, 0.0, + res); + }, + [sysAD, &y, &yDot](double const* lDir, double* res) -> void { + sysAD->multiplyWithJacobian(cadet::SimulationTime{0.0, 0u}, + cadet::ConstSimulationState{y.data(), yDot.data()}, lDir, 1.0, 0.0, + res); + }, jacDir.data(), jacCol1.data(), jacCol2.data(), nDof); delete[] adRes; @@ -705,12 +909,12 @@ TEST_CASE("ModelSystem time derivative Jacobian FD vs analytic", "[ModelSystem], const std::vector secTimes = jpp.getDoubleArray("SECTION_TIMES"); std::vector secCont(secTimes.size() - 2, false); - if (jpp.exists("SECTION_CONTINUITY")) + if (jpp.exists("SECTION_CONTINUITY")) secCont = jpp.getBoolArray("SECTION_CONTINUITY"); jpp.popScope(); jpp.popScope(); - + // Create and configure ModelSystem jpp.pushScope("model"); cadet::test::column::setNumAxialCells(jpp, 10); @@ -733,20 +937,30 @@ TEST_CASE("ModelSystem time derivative Jacobian FD vs analytic", "[ModelSystem], std::vector jacCol2(nDof, 0.0); // Fill state vector with some values - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); - cadet::test::util::populate(yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); // Setup matrices - sys->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, cadet::AdJacobianParams{nullptr, nullptr, 0u}); + sys->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, + cadet::AdJacobianParams{nullptr, nullptr, 0u}); // Compute state Jacobian - sys->residualWithJacobian(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), yDot.data()}, jacDir.data(), cadet::AdJacobianParams{nullptr, nullptr, 0u}); + sys->residualWithJacobian(cadet::SimulationTime{0.0, 0u}, + cadet::ConstSimulationState{y.data(), yDot.data()}, jacDir.data(), + cadet::AdJacobianParams{nullptr, nullptr, 0u}); std::fill(jacDir.begin(), jacDir.end(), 0.0); // Compare Jacobians cadet::test::compareJacobianFD( - [sys, &y](double const* lDir, double* res) -> void { sys->residual(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), lDir}, res); }, - [sys, &y, &yDot](double const* lDir, double* res) -> void { sys->multiplyWithDerivativeJacobian(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), yDot.data()}, lDir, res); }, + [sys, &y](double const* lDir, double* res) -> void { + sys->residual(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), lDir}, res); + }, + [sys, &y, &yDot](double const* lDir, double* res) -> void { + sys->multiplyWithDerivativeJacobian(cadet::SimulationTime{0.0, 0u}, + cadet::ConstSimulationState{y.data(), yDot.data()}, lDir, res); + }, yDot.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), nDof, h, absTol, relTol); } } @@ -776,12 +990,12 @@ TEST_CASE("ModelSystem sensitivity Jacobians", "[ModelSystem],[Sensitivity]") const std::vector secTimes = jpp.getDoubleArray("SECTION_TIMES"); std::vector secCont(secTimes.size() - 2, false); - if (jpp.exists("SECTION_CONTINUITY")) + if (jpp.exists("SECTION_CONTINUITY")) secCont = jpp.getBoolArray("SECTION_CONTINUITY"); jpp.popScope(); jpp.popScope(); - + // Create and configure ModelSystem jpp.pushScope("model"); cadet::test::column::setNumAxialCells(jpp, 10); @@ -801,7 +1015,10 @@ TEST_CASE("ModelSystem sensitivity Jacobians", "[ModelSystem],[Sensitivity]") sys->prepareADvectors(cadet::AdJacobianParams{adRes, nullptr, 0}); // Add dispersion parameter sensitivity - REQUIRE(sys->setSensitiveParameter(cadet::makeParamId(cadet::hashString("COL_DISPERSION"), 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), 0, 1.0)); + REQUIRE(sys->setSensitiveParameter( + cadet::makeParamId(cadet::hashString("COL_DISPERSION"), 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), + 0, 1.0)); // Obtain memory for state, Jacobian multiply direction, Jacobian column const unsigned int nDof = sys->numDofs(); @@ -821,23 +1038,33 @@ TEST_CASE("ModelSystem sensitivity Jacobians", "[ModelSystem],[Sensitivity]") std::vector resS(1, nullptr); // Fill state vector with some values - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); - cadet::test::util::populate(yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); + cadet::test::util::populate( + yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); // Setup matrices - sys->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, cadet::AdJacobianParams{adRes, nullptr, 0u}); + sys->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, + cadet::AdJacobianParams{adRes, nullptr, 0u}); // Calculate Jacobian - sys->residualWithJacobian(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), yDot.data()}, jacDir.data(), cadet::AdJacobianParams{adRes, nullptr, 0u}); + sys->residualWithJacobian(cadet::SimulationTime{0.0, 0u}, + cadet::ConstSimulationState{y.data(), yDot.data()}, jacDir.data(), + cadet::AdJacobianParams{adRes, nullptr, 0u}); // Check state Jacobian cadet::test::compareJacobianFD( [&](double const* lDir, double* res) -> void { yS[0] = lDir; resS[0] = res; - sys->residualSensFwd(1, cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), yDot.data()}, nullptr, yS, ySdot, resS, adRes, temp1.data(), temp2.data(), temp3.data()); - }, - [&](double const* lDir, double* res) -> void { sys->multiplyWithJacobian(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), yDot.data()}, lDir, 1.0, 0.0, res); }, + sys->residualSensFwd(1, cadet::SimulationTime{0.0, 0u}, + cadet::ConstSimulationState{y.data(), yDot.data()}, nullptr, yS, ySdot, resS, + adRes, temp1.data(), temp2.data(), temp3.data()); + }, + [&](double const* lDir, double* res) -> void { + sys->multiplyWithJacobian(cadet::SimulationTime{0.0, 0u}, + cadet::ConstSimulationState{y.data(), yDot.data()}, lDir, 1.0, 0.0, res); + }, zeros.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), nDof, h, absTol, relTol); // Reset evaluation point @@ -849,9 +1076,14 @@ TEST_CASE("ModelSystem sensitivity Jacobians", "[ModelSystem],[Sensitivity]") [&](double const* lDir, double* res) -> void { ySdot[0] = lDir; resS[0] = res; - sys->residualSensFwd(1, cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), yDot.data()}, nullptr, yS, ySdot, resS, adRes, temp1.data(), temp2.data(), temp3.data()); - }, - [&](double const* lDir, double* res) -> void { sys->multiplyWithDerivativeJacobian(cadet::SimulationTime{0.0, 0u}, cadet::ConstSimulationState{y.data(), yDot.data()}, lDir, res); }, + sys->residualSensFwd(1, cadet::SimulationTime{0.0, 0u}, + cadet::ConstSimulationState{y.data(), yDot.data()}, nullptr, yS, ySdot, resS, + adRes, temp1.data(), temp2.data(), temp3.data()); + }, + [&](double const* lDir, double* res) -> void { + sys->multiplyWithDerivativeJacobian(cadet::SimulationTime{0.0, 0u}, + cadet::ConstSimulationState{y.data(), yDot.data()}, lDir, res); + }, zeros.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), nDof, h, absTol, relTol); delete[] adRes; @@ -863,226 +1095,110 @@ TEST_CASE("ModelSystem sensitivity Jacobians", "[ModelSystem],[Sensitivity]") TEST_CASE("ModelSystem coupling Jacobian linear chain single port (all) comp all", "[ModelSystem],[Jacobian],[Inlet]") { - const std::vector sysDescription = { - 3, 0, 1, 0, - 3, 1, 1, 0, - 3, 1, 1, 0, - 3, 1, 0, 0 - }; + const std::vector sysDescription = {3, 0, 1, 0, 3, 1, 1, 0, 3, 1, 1, 0, 3, 1, 0, 0}; - const std::vector connections = { - 0, 1, -1, -1, -1, -1, 1.0, - 1, 2, -1, -1, -1, -1, 1.0, - 2, 3, -1, -1, -1, -1, 1.0 - }; - - const std::vector inFlow = { - 1.0, - 1.0, - 1.0 - }; - - const std::vector outFlow = { - 1.0, - 1.0, - 1.0 - }; + const std::vector connections = {0, 1, -1, -1, -1, -1, 1.0, 1, 2, -1, -1, + -1, -1, 1.0, 2, 3, -1, -1, -1, -1, 1.0}; + + const std::vector inFlow = {1.0, 1.0, 1.0}; + + const std::vector outFlow = {1.0, 1.0, 1.0}; checkCouplingJacobian(sysDescription, connections, inFlow, outFlow); } -TEST_CASE("ModelSystem coupling Jacobian linear chain single port (specific) comp all", "[ModelSystem],[Jacobian],[Inlet]") +TEST_CASE("ModelSystem coupling Jacobian linear chain single port (specific) comp all", + "[ModelSystem],[Jacobian],[Inlet]") { - const std::vector sysDescription = { - 3, 0, 1, 0, - 3, 1, 1, 0, - 3, 1, 1, 0, - 3, 1, 0, 0 - }; + const std::vector sysDescription = {3, 0, 1, 0, 3, 1, 1, 0, 3, 1, 1, 0, 3, 1, 0, 0}; - const std::vector connections = { - 0, 1, 0, 0, -1, -1, 1.0, - 1, 2, 0, 0, -1, -1, 1.0, - 2, 3, 0, 0, -1, -1, 1.0 - }; - - const std::vector inFlow = { - 1.0, - 1.0, - 1.0 - }; - - const std::vector outFlow = { - 1.0, - 1.0, - 1.0 - }; + const std::vector connections = {0, 1, 0, 0, -1, -1, 1.0, 1, 2, 0, 0, -1, -1, 1.0, 2, 3, 0, 0, -1, -1, 1.0}; + + const std::vector inFlow = {1.0, 1.0, 1.0}; + + const std::vector outFlow = {1.0, 1.0, 1.0}; checkCouplingJacobian(sysDescription, connections, inFlow, outFlow); } -TEST_CASE("ModelSystem coupling Jacobian linear chain single port (all) comp specific", "[ModelSystem],[Jacobian],[Inlet]") +TEST_CASE("ModelSystem coupling Jacobian linear chain single port (all) comp specific", + "[ModelSystem],[Jacobian],[Inlet]") { - const std::vector sysDescription = { - 3, 0, 1, 0, - 3, 1, 1, 0, - 3, 1, 1, 0, - 3, 1, 0, 0 - }; + const std::vector sysDescription = {3, 0, 1, 0, 3, 1, 1, 0, 3, 1, 1, 0, 3, 1, 0, 0}; - const std::vector connections = { - 0, 1, -1, -1, 0, 0, 1.0, - 0, 1, -1, -1, 1, 1, 1.0, - 0, 1, -1, -1, 2, 2, 1.0, - 1, 2, -1, -1, 0, 0, 1.0, - 1, 2, -1, -1, 1, 1, 1.0, - 1, 2, -1, -1, 2, 2, 1.0, - 2, 3, -1, -1, 0, 0, 1.0, - 2, 3, -1, -1, 1, 1, 1.0, - 2, 3, -1, -1, 2, 2, 1.0 - }; - - const std::vector inFlow = { - 1.0, - 1.0, - 1.0 - }; - - const std::vector outFlow = { - 1.0, - 1.0, - 1.0 - }; + const std::vector connections = {0, 1, -1, -1, 0, 0, 1.0, 0, 1, -1, -1, 1, 1, 1.0, 0, 1, -1, -1, 2, 2, 1.0, + 1, 2, -1, -1, 0, 0, 1.0, 1, 2, -1, -1, 1, 1, 1.0, 1, 2, -1, -1, 2, 2, 1.0, + 2, 3, -1, -1, 0, 0, 1.0, 2, 3, -1, -1, 1, 1, 1.0, 2, 3, -1, -1, 2, 2, 1.0}; + + const std::vector inFlow = {1.0, 1.0, 1.0}; + + const std::vector outFlow = {1.0, 1.0, 1.0}; checkCouplingJacobian(sysDescription, connections, inFlow, outFlow); } -TEST_CASE("ModelSystem coupling Jacobian linear chain single port (specific) comp specific", "[ModelSystem],[Jacobian],[Inlet]") +TEST_CASE("ModelSystem coupling Jacobian linear chain single port (specific) comp specific", + "[ModelSystem],[Jacobian],[Inlet]") { - const std::vector sysDescription = { - 3, 0, 1, 0, - 3, 1, 1, 0, - 3, 1, 1, 0, - 3, 1, 0, 0 - }; + const std::vector sysDescription = {3, 0, 1, 0, 3, 1, 1, 0, 3, 1, 1, 0, 3, 1, 0, 0}; - const std::vector connections = { - 0, 1, 0, 0, 0, 0, 1.0, - 0, 1, 0, 0, 1, 1, 1.0, - 0, 1, 0, 0, 2, 2, 1.0, - 1, 2, 0, 0, 0, 0, 1.0, - 1, 2, 0, 0, 1, 1, 1.0, - 1, 2, 0, 0, 2, 2, 1.0, - 2, 3, 0, 0, 0, 0, 1.0, - 2, 3, 0, 0, 1, 1, 1.0, - 2, 3, 0, 0, 2, 2, 1.0 - }; - - const std::vector inFlow = { - 1.0, - 1.0, - 1.0 - }; - - const std::vector outFlow = { - 1.0, - 1.0, - 1.0 - }; + const std::vector connections = {0, 1, 0, 0, 0, 0, 1.0, 0, 1, 0, 0, 1, 1, 1.0, 0, 1, 0, 0, 2, 2, 1.0, + 1, 2, 0, 0, 0, 0, 1.0, 1, 2, 0, 0, 1, 1, 1.0, 1, 2, 0, 0, 2, 2, 1.0, + 2, 3, 0, 0, 0, 0, 1.0, 2, 3, 0, 0, 1, 1, 1.0, 2, 3, 0, 0, 2, 2, 1.0}; + + const std::vector inFlow = {1.0, 1.0, 1.0}; + + const std::vector outFlow = {1.0, 1.0, 1.0}; checkCouplingJacobian(sysDescription, connections, inFlow, outFlow); } -TEST_CASE("ModelSystem coupling Jacobian linear chain multi port (specific) comp all", "[ModelSystem],[Jacobian],[Inlet]") +TEST_CASE("ModelSystem coupling Jacobian linear chain multi port (specific) comp all", + "[ModelSystem],[Jacobian],[Inlet]") { /* - 1 1 1 - /-->--O-->--O-->--\ + 1 1 1 + /-->--O-->--O-->--\ 2 -->O O--> 2 - \-->--O-->--O-->--/ - 1 1 1 + \-->--O-->--O-->--/ + 1 1 1 */ - const std::vector sysDescription = { - 3, 0, 1, 0, - 3, 2, 2, 0, - 3, 2, 2, 0, - 3, 1, 0, 0 - }; + const std::vector sysDescription = {3, 0, 1, 0, 3, 2, 2, 0, 3, 2, 2, 0, 3, 1, 0, 0}; - const std::vector connections = { - 0, 1, 0, 0, -1, -1, 1.0, - 0, 1, 0, 1, -1, -1, 1.0, - 1, 2, 0, 1, -1, -1, 1.0, - 1, 2, 1, 0, -1, -1, 1.0, - 2, 3, 0, 0, -1, -1, 1.0, - 2, 3, 1, 0, -1, -1, 1.0 - }; - - const std::vector inFlow = { - 1.0, 1.0, - 1.0, 1.0, - 2.0 - }; - - const std::vector outFlow = { - 2.0, - 1.0, 1.0, - 1.0, 1.0 - }; + const std::vector connections = {0, 1, 0, 0, -1, -1, 1.0, 0, 1, 0, 1, -1, -1, 1.0, 1, 2, 0, 1, -1, -1, 1.0, + 1, 2, 1, 0, -1, -1, 1.0, 2, 3, 0, 0, -1, -1, 1.0, 2, 3, 1, 0, -1, -1, 1.0}; + + const std::vector inFlow = {1.0, 1.0, 1.0, 1.0, 2.0}; + + const std::vector outFlow = {2.0, 1.0, 1.0, 1.0, 1.0}; checkCouplingJacobian(sysDescription, connections, inFlow, outFlow); } -TEST_CASE("ModelSystem coupling Jacobian linear chain multi port (specific) comp specific", "[ModelSystem],[Jacobian],[Inlet]") +TEST_CASE("ModelSystem coupling Jacobian linear chain multi port (specific) comp specific", + "[ModelSystem],[Jacobian],[Inlet]") { /* - 1 1 1 - /-->--O-->--O-->--\ + 1 1 1 + /-->--O-->--O-->--\ 2 -->O O--> 2 - \-->--O-->--O-->--/ - 1 1 1 + \-->--O-->--O-->--/ + 1 1 1 */ - const std::vector sysDescription = { - 3, 0, 1, 0, - 3, 2, 2, 0, - 3, 2, 2, 0, - 3, 1, 0, 0 - }; + const std::vector sysDescription = {3, 0, 1, 0, 3, 2, 2, 0, 3, 2, 2, 0, 3, 1, 0, 0}; - const std::vector connections = { - 0, 1, 0, 0, 0, 0, 1.0, - 0, 1, 0, 0, 1, 1, 1.0, - 0, 1, 0, 0, 2, 2, 1.0, - 0, 1, 0, 1, 0, 0, 1.0, - 0, 1, 0, 1, 1, 1, 1.0, - 0, 1, 0, 1, 2, 2, 1.0, - 1, 2, 0, 1, 0, 0, 1.0, - 1, 2, 0, 1, 1, 1, 1.0, - 1, 2, 0, 1, 2, 2, 1.0, - 1, 2, 1, 0, 0, 0, 1.0, - 1, 2, 1, 0, 1, 1, 1.0, - 1, 2, 1, 0, 2, 2, 1.0, - 2, 3, 0, 0, 0, 0, 1.0, - 2, 3, 0, 0, 1, 1, 1.0, - 2, 3, 0, 0, 2, 2, 1.0, - 2, 3, 1, 0, 0, 0, 1.0, - 2, 3, 1, 0, 1, 1, 1.0, - 2, 3, 1, 0, 2, 2, 1.0 - }; - - const std::vector inFlow = { - 1.0, 1.0, - 1.0, 1.0, - 2.0 - }; - - const std::vector outFlow = { - 2.0, - 1.0, 1.0, - 1.0, 1.0 - }; + const std::vector connections = {0, 1, 0, 0, 0, 0, 1.0, 0, 1, 0, 0, 1, 1, 1.0, 0, 1, 0, 0, 2, 2, 1.0, + 0, 1, 0, 1, 0, 0, 1.0, 0, 1, 0, 1, 1, 1, 1.0, 0, 1, 0, 1, 2, 2, 1.0, + 1, 2, 0, 1, 0, 0, 1.0, 1, 2, 0, 1, 1, 1, 1.0, 1, 2, 0, 1, 2, 2, 1.0, + 1, 2, 1, 0, 0, 0, 1.0, 1, 2, 1, 0, 1, 1, 1.0, 1, 2, 1, 0, 2, 2, 1.0, + 2, 3, 0, 0, 0, 0, 1.0, 2, 3, 0, 0, 1, 1, 1.0, 2, 3, 0, 0, 2, 2, 1.0, + 2, 3, 1, 0, 0, 0, 1.0, 2, 3, 1, 0, 1, 1, 1.0, 2, 3, 1, 0, 2, 2, 1.0}; + + const std::vector inFlow = {1.0, 1.0, 1.0, 1.0, 2.0}; + + const std::vector outFlow = {2.0, 1.0, 1.0, 1.0, 1.0}; checkCouplingJacobian(sysDescription, connections, inFlow, outFlow); } @@ -1090,37 +1206,19 @@ TEST_CASE("ModelSystem coupling Jacobian linear chain multi port (specific) comp TEST_CASE("ModelSystem coupling Jacobian X single port (all) comp all", "[ModelSystem],[Jacobian],[Inlet]") { /* - O--\ /--O - --O-- - O--/ \--O + O--\ /--O + --O-- + O--/ \--O */ - const std::vector sysDescription = { - 2, 0, 1, 0, - 2, 0, 1, 0, - 2, 1, 1, 0, - 2, 1, 0, 0, - 2, 1, 0, 0 - }; + const std::vector sysDescription = {2, 0, 1, 0, 2, 0, 1, 0, 2, 1, 1, 0, 2, 1, 0, 0, 2, 1, 0, 0}; - const std::vector connections = { - 0, 2, 0, 0, -1, -1, 1.0, - 1, 2, 0, 0, -1, -1, 1.0, - 2, 3, 0, 0, -1, -1, 1.0, - 2, 4, 0, 0, -1, -1, 1.0 - }; - - const std::vector inFlow = { - 2.0, - 1.0, - 1.0 - }; - - const std::vector outFlow = { - 1.0, - 1.0, - 2.0 - }; + const std::vector connections = {0, 2, 0, 0, -1, -1, 1.0, 1, 2, 0, 0, -1, -1, 1.0, + 2, 3, 0, 0, -1, -1, 1.0, 2, 4, 0, 0, -1, -1, 1.0}; + + const std::vector inFlow = {2.0, 1.0, 1.0}; + + const std::vector outFlow = {1.0, 1.0, 2.0}; checkCouplingJacobian(sysDescription, connections, inFlow, outFlow); } @@ -1128,127 +1226,60 @@ TEST_CASE("ModelSystem coupling Jacobian X single port (all) comp all", "[ModelS TEST_CASE("ModelSystem coupling Jacobian X multi port comp all", "[ModelSystem],[Jacobian],[Inlet]") { /* - O--\ /--O - --O-- - O--/ \--O + O--\ /--O + --O-- + O--/ \--O */ - const std::vector sysDescription = { - 2, 0, 1, 0, - 2, 0, 1, 0, - 2, 2, 2, 0, - 2, 1, 0, 0, - 2, 1, 0, 0 - }; + const std::vector sysDescription = {2, 0, 1, 0, 2, 0, 1, 0, 2, 2, 2, 0, 2, 1, 0, 0, 2, 1, 0, 0}; - const std::vector connections = { - 0, 2, 0, 0, -1, -1, 1.0, - 1, 2, 0, 1, -1, -1, 1.0, - 2, 3, 1, 0, -1, -1, 1.0, - 2, 4, 0, 0, -1, -1, 1.0 - }; - - const std::vector inFlow = { - 1.0, 1.0, - 1.0, - 1.0 - }; - - const std::vector outFlow = { - 1.0, - 1.0, - 1.0, 1.0 - }; + const std::vector connections = {0, 2, 0, 0, -1, -1, 1.0, 1, 2, 0, 1, -1, -1, 1.0, + 2, 3, 1, 0, -1, -1, 1.0, 2, 4, 0, 0, -1, -1, 1.0}; + + const std::vector inFlow = {1.0, 1.0, 1.0, 1.0}; + + const std::vector outFlow = {1.0, 1.0, 1.0, 1.0}; checkCouplingJacobian(sysDescription, connections, inFlow, outFlow); } -TEST_CASE("ModelSystem coupling Jacobian linear chain single port (all) comp twisted", "[ModelSystem],[Jacobian],[Inlet]") +TEST_CASE("ModelSystem coupling Jacobian linear chain single port (all) comp twisted", + "[ModelSystem],[Jacobian],[Inlet]") { - const std::vector sysDescription = { - 3, 0, 1, 0, - 3, 1, 1, 0, - 3, 1, 1, 0, - 3, 1, 0, 0 - }; + const std::vector sysDescription = {3, 0, 1, 0, 3, 1, 1, 0, 3, 1, 1, 0, 3, 1, 0, 0}; - const std::vector connections = { - 0, 1, 0, 0, 0, 0, 1.0, - 0, 1, 0, 0, 1, 1, 1.0, - 0, 1, 0, 0, 2, 2, 1.0, - 1, 2, 0, 0, 0, 2, 1.0, - 1, 2, 0, 0, 1, 1, 1.0, - 1, 2, 0, 0, 2, 0, 1.0, - 2, 3, 0, 0, 0, 0, 1.0, - 2, 3, 0, 0, 1, 1, 1.0, - 2, 3, 0, 0, 2, 2, 1.0 - }; - - const std::vector inFlow = { - 1.0, - 1.0, - 1.0 - }; - - const std::vector outFlow = { - 1.0, - 1.0, - 1.0 - }; + const std::vector connections = {0, 1, 0, 0, 0, 0, 1.0, 0, 1, 0, 0, 1, 1, 1.0, 0, 1, 0, 0, 2, 2, 1.0, + 1, 2, 0, 0, 0, 2, 1.0, 1, 2, 0, 0, 1, 1, 1.0, 1, 2, 0, 0, 2, 0, 1.0, + 2, 3, 0, 0, 0, 0, 1.0, 2, 3, 0, 0, 1, 1, 1.0, 2, 3, 0, 0, 2, 2, 1.0}; + + const std::vector inFlow = {1.0, 1.0, 1.0}; + + const std::vector outFlow = {1.0, 1.0, 1.0}; checkCouplingJacobian(sysDescription, connections, inFlow, outFlow); } - TEST_CASE("ModelSystem coupling Jacobian circular", "[ModelSystem],[Jacobian],[Inlet]") { /* - ___________________ - | | + ___________________ + | | 0---2--\ /--5--- | - --4-- | | + --4-- | | 1---3--/ \--6----- - |______________| + |______________| */ - const std::vector sysDescription = { - 2, 0, 1, 0, - 2, 0, 1, 0, - 2, 2, 1, 0, - 2, 2, 1, 0, - 2, 1, 1, 1, - 2, 1, 1, 0, - 2, 1, 1, 0 - }; + const std::vector sysDescription = {2, 0, 1, 0, 2, 0, 1, 0, 2, 2, 1, 0, 2, 2, + 1, 0, 2, 1, 1, 1, 2, 1, 1, 0, 2, 1, 1, 0}; const std::vector connections = { - 0, 2, 0, 0, -1, -1, 1.0, - 1, 3, 0, 0, -1, -1, 1.0, - 2, 4, -1, -1, -1, -1, 2.0, - 3, 4, -1, -1, -1, -1, 2.0, - 4, 5, 0, 0, -1, -1, 1.0, - 4, 6, -1, -1, -1, -1, 1.0, - 5, 3, 0, 1, -1, -1, 1.0, - 6, 2, 0, 1, -1, -1, 1.0 - }; - - const std::vector inFlow = { - 1.0, 1.0, - 1.0, 1.0, - 4.0, - 1.0, - 1.0 - }; - - const std::vector outFlow = { - 1.0, - 1.0, - 2.0, - 2.0, - 2.0, - 1.0, - 1.0 - }; + 0, 2, 0, 0, -1, -1, 1.0, 1, 3, 0, 0, -1, -1, 1.0, 2, 4, -1, -1, -1, -1, 2.0, 3, 4, -1, -1, -1, -1, 2.0, + 4, 5, 0, 0, -1, -1, 1.0, 4, 6, -1, -1, -1, -1, 1.0, 5, 3, 0, 1, -1, -1, 1.0, 6, 2, 0, 1, -1, -1, 1.0}; + + const std::vector inFlow = {1.0, 1.0, 1.0, 1.0, 4.0, 1.0, 1.0}; + + const std::vector outFlow = {1.0, 1.0, 2.0, 2.0, 2.0, 1.0, 1.0}; checkCouplingJacobian(sysDescription, connections, inFlow, outFlow); } @@ -1256,36 +1287,19 @@ TEST_CASE("ModelSystem coupling Jacobian circular", "[ModelSystem],[Jacobian],[I TEST_CASE("ModelSystem coupling Jacobian component Y", "[ModelSystem],[Jacobian],[Inlet]") { /* - O--\ - --O--O - O--/ + O--\ + --O--O + O--/ */ - const std::vector sysDescription = { - 2, 0, 1, 0, - 2, 0, 1, 0, - 4, 1, 1, 0, - 4, 1, 0, 0 - }; + const std::vector sysDescription = {2, 0, 1, 0, 2, 0, 1, 0, 4, 1, 1, 0, 4, 1, 0, 0}; - const std::vector connections = { - 0, 2, 0, 0, 0, 0, 1.0, - 0, 2, 0, 0, 1, 1, 1.0, - 1, 2, 0, 0, 0, 2, 1.0, - 1, 2, 0, 0, 0, 3, 1.0, - 2, 3, 0, 0, -1, -1, 2.0 - }; - - const std::vector inFlow = { - 2.0, - 2.0 - }; - - const std::vector outFlow = { - 1.0, - 1.0, - 2.0 - }; + const std::vector connections = {0, 2, 0, 0, 0, 0, 1.0, 0, 2, 0, 0, 1, 1, 1.0, 1, 2, 0, 0, + 0, 2, 1.0, 1, 2, 0, 0, 0, 3, 1.0, 2, 3, 0, 0, -1, -1, 2.0}; + + const std::vector inFlow = {2.0, 2.0}; + + const std::vector outFlow = {1.0, 1.0, 2.0}; checkCouplingJacobian(sysDescription, connections, inFlow, outFlow); } diff --git a/test/MultiChannelTransportModel.cpp b/test/MultiChannelTransportModel.cpp index 308fa9b63..c67e203b3 100644 --- a/test/MultiChannelTransportModel.cpp +++ b/test/MultiChannelTransportModel.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -36,252 +36,261 @@ const char* getTestDirectory(); namespace { - using json = nlohmann::json; +using json = nlohmann::json; - json createMCTModelJson(const std::vector& crossSectionAreas, const std::vector& velocity, const std::vector& exchangeMatrix, const double colDisp = 5.75e-8, const double colLength = 200, const int nCol = 16) +json createMCTModelJson(const std::vector& crossSectionAreas, const std::vector& velocity, + const std::vector& exchangeMatrix, const double colDisp = 5.75e-8, + const double colLength = 200, const int nCol = 16) +{ + json config; + config["UNIT_TYPE"] = "MULTI_CHANNEL_TRANSPORT"; + config["NCOMP"] = 1; + config["COL_DISPERSION"] = colDisp; + config["EXCHANGE_MATRIX"] = exchangeMatrix; + config["NCHANNEL"] = crossSectionAreas.size(); + + // Geometry + config["COL_LENGTH"] = colLength; + config["CHANNEL_CROSS_SECTION_AREAS"] = crossSectionAreas; + config["VELOCITY"] = velocity; + + // Initial conditions + config["INIT_C"] = {0.0}; + + // Discretization { - json config; - config["UNIT_TYPE"] = "MULTI_CHANNEL_TRANSPORT"; - config["NCOMP"] = 1; - config["COL_DISPERSION"] = colDisp; - config["EXCHANGE_MATRIX"] = exchangeMatrix; - config["NCHANNEL"] = crossSectionAreas.size(); - - // Geometry - config["COL_LENGTH"] = colLength; - config["CHANNEL_CROSS_SECTION_AREAS"] = crossSectionAreas; - config["VELOCITY"] = velocity; - - // Initial conditions - config["INIT_C"] = {0.0}; - - // Discretization - { - json disc; + json disc; - disc["NCOL"] = nCol; + disc["NCOL"] = nCol; - disc["USE_ANALYTIC_JACOBIAN"] = true; + disc["USE_ANALYTIC_JACOBIAN"] = true; - // WENO - { - json weno; + // WENO + { + json weno; - weno["WENO_ORDER"] = 3; - weno["BOUNDARY_MODEL"] = 0; - weno["WENO_EPS"] = 1e-10; - disc["weno"] = weno; - } - config["discretization"] = disc; + weno["WENO_ORDER"] = 3; + weno["BOUNDARY_MODEL"] = 0; + weno["WENO_EPS"] = 1e-10; + disc["weno"] = weno; } - - return config; + config["discretization"] = disc; } - json createMCTJson(const std::vector& volFlowRate, const std::vector& velocity, const std::vector& concentrationIn, const std::vector& crossSectionAreas, const std::vector& exchangeMatrix, const double colDisp = 5.75e-8) + return config; +} + +json createMCTJson(const std::vector& volFlowRate, const std::vector& velocity, + const std::vector& concentrationIn, const std::vector& crossSectionAreas, + const std::vector& exchangeMatrix, const double colDisp = 5.75e-8) +{ + json config; + // Model { - json config; - // Model + json model; + model["NUNITS"] = 2; + model["unit_000"] = createMCTModelJson(crossSectionAreas, velocity, exchangeMatrix, colDisp); + + // Inlet - unit 001 ... unitXXX + for (unsigned int i = 0; i < volFlowRate.size(); ++i) { - json model; - model["NUNITS"] = 2; - model["unit_000"] = createMCTModelJson(crossSectionAreas, velocity, exchangeMatrix, colDisp); + json inlet; - // Inlet - unit 001 ... unitXXX - for (unsigned int i = 0; i < volFlowRate.size(); ++i) - { - json inlet; + inlet["UNIT_TYPE"] = std::string("INLET"); + inlet["INLET_TYPE"] = std::string("PIECEWISE_CUBIC_POLY"); + inlet["NCOMP"] = 1; - inlet["UNIT_TYPE"] = std::string("INLET"); - inlet["INLET_TYPE"] = std::string("PIECEWISE_CUBIC_POLY"); - inlet["NCOMP"] = 1; + { + json sec; - { - json sec; + sec["CONST_COEFF"] = {concentrationIn[i]}; + sec["LIN_COEFF"] = {0.0}; + sec["QUAD_COEFF"] = {0.0}; + sec["CUBE_COEFF"] = {0.0}; - sec["CONST_COEFF"] = {concentrationIn[i]}; - sec["LIN_COEFF"] = {0.0}; - sec["QUAD_COEFF"] = {0.0}; - sec["CUBE_COEFF"] = {0.0}; + inlet["sec_000"] = sec; + } - inlet["sec_000"] = sec; - } + { + json sec; - { - json sec; + sec["CONST_COEFF"] = {0.0}; + sec["LIN_COEFF"] = {0.0}; + sec["QUAD_COEFF"] = {0.0}; + sec["CUBE_COEFF"] = {0.0}; - sec["CONST_COEFF"] = {0.0}; - sec["LIN_COEFF"] = {0.0}; - sec["QUAD_COEFF"] = {0.0}; - sec["CUBE_COEFF"] = {0.0}; + inlet["sec_001"] = sec; + } - inlet["sec_001"] = sec; - } + model[std::string("unit_00") + std::to_string(i + 1)] = inlet; + } - model[std::string("unit_00") + std::to_string(i+1)] = inlet; - } + // Valve switches + { + json con; + con["NSWITCHES"] = 1; + con["CONNECTIONS_INCLUDE_PORTS"] = true; - // Valve switches { - json con; - con["NSWITCHES"] = 1; - con["CONNECTIONS_INCLUDE_PORTS"] = true; + json sw; + // This switch occurs at beginning of section 0 (initial configuration) + sw["SECTION"] = 0; + + // Connection list is 3x7 since we have 1 connection between + // the two unit operations with 3 ports (and we need to have 7 columns) + std::vector conn(volFlowRate.size() * 7, 0.0); + for (unsigned int i = 0; i < volFlowRate.size(); ++i) { - json sw; - - // This switch occurs at beginning of section 0 (initial configuration) - sw["SECTION"] = 0; - - // Connection list is 3x7 since we have 1 connection between - // the two unit operations with 3 ports (and we need to have 7 columns) - std::vector conn(volFlowRate.size() * 7, 0.0); - for (unsigned int i = 0; i < volFlowRate.size(); ++i) - { - conn[i * 7 + 0] = i + 1; - conn[i * 7 + 1] = 0.0; - conn[i * 7 + 2] = 0.0; - conn[i * 7 + 3] = i; - conn[i * 7 + 4] = -1.0; - conn[i * 7 + 5] = -1.0; - conn[i * 7 + 6] = volFlowRate[i]; - } - - sw["CONNECTIONS"] = conn; - // Connections: From unit operation, - // to unit operation, - // from port, - // to port, - // connect all components -1 (i.e., all components), - // to all components -1 (i.e., all components), - // volumetric flow rate - - con["switch_000"] = sw; + conn[i * 7 + 0] = i + 1; + conn[i * 7 + 1] = 0.0; + conn[i * 7 + 2] = 0.0; + conn[i * 7 + 3] = i; + conn[i * 7 + 4] = -1.0; + conn[i * 7 + 5] = -1.0; + conn[i * 7 + 6] = volFlowRate[i]; } - model["connections"] = con; - } - // Solver settings - { - json solver; + sw["CONNECTIONS"] = conn; + // Connections: From unit operation, + // to unit operation, + // from port, + // to port, + // connect all components -1 (i.e., all components), + // to all components -1 (i.e., all components), + // volumetric flow rate - solver["MAX_KRYLOV"] = 0; - solver["GS_TYPE"] = 1; - solver["MAX_RESTARTS"] = 10; - solver["SCHUR_SAFETY"] = 1e-8; - model["solver"] = solver; + con["switch_000"] = sw; } - - config["model"] = model; + model["connections"] = con; } - // Return + // Solver settings { - json ret; - ret["WRITE_SOLUTION_TIMES"] = true; - - json mct; - mct["WRITE_SOLUTION_BULK"] = true; - mct["WRITE_SOLUTION_PARTICLE"] = false; - mct["WRITE_SOLUTION_FLUX"] = false; - mct["WRITE_SOLUTION_INLET"] = true; - mct["WRITE_SOLUTION_OUTLET"] = true; - - ret["unit_000"] = mct; - config["return"] = ret; + json solver; + + solver["MAX_KRYLOV"] = 0; + solver["GS_TYPE"] = 1; + solver["MAX_RESTARTS"] = 10; + solver["SCHUR_SAFETY"] = 1e-8; + model["solver"] = solver; } - // Solver - { - json solver; + config["model"] = model; + } - { - std::vector solTimes; + // Return + { + json ret; + ret["WRITE_SOLUTION_TIMES"] = true; + + json mct; + mct["WRITE_SOLUTION_BULK"] = true; + mct["WRITE_SOLUTION_PARTICLE"] = false; + mct["WRITE_SOLUTION_FLUX"] = false; + mct["WRITE_SOLUTION_INLET"] = true; + mct["WRITE_SOLUTION_OUTLET"] = true; + + ret["unit_000"] = mct; + config["return"] = ret; + } - solTimes.reserve(1501); - for (double t = 0.0; t <= 1500.0; t += 1.0) - solTimes.push_back(t); + // Solver + { + json solver; - solver["USER_SOLUTION_TIMES"] = solTimes; - } + { + std::vector solTimes; - solver["NTHREADS"] = 1; + solTimes.reserve(1501); + for (double t = 0.0; t <= 1500.0; t += 1.0) + solTimes.push_back(t); - // Sections - { - json sec; + solver["USER_SOLUTION_TIMES"] = solTimes; + } - sec["NSEC"] = 2; - sec["SECTION_TIMES"] = {0.0, 10.0, 1500.0}; - sec["SECTION_CONTINUITY"] = std::vector{false}; + solver["NTHREADS"] = 1; - solver["sections"] = sec; - } + // Sections + { + json sec; - // Time integrator - { - json ti; - - ti["ABSTOL"] = 1e-8; - ti["RELTOL"] = 1e-6; - ti["ALGTOL"] = 1e-12; - ti["INIT_STEP_SIZE"] = 1e-6; - ti["MAX_STEPS"] = 10000; - ti["MAX_STEP_SIZE"] = 0.0; - ti["RELTOL_SENS"] = 1e-6; - ti["ERRORTEST_SENS"] = true; - ti["MAX_NEWTON_ITER"] = 3; - ti["MAX_ERRTEST_FAIL"] = 7; - ti["MAX_CONVTEST_FAIL"] = 10; - ti["MAX_NEWTON_ITER_SENS"] = 3; - ti["CONSISTENT_INIT_MODE"] = 1; - ti["CONSISTENT_INIT_MODE_SENS"] = 1; - - solver["time_integrator"] = ti; - } + sec["NSEC"] = 2; + sec["SECTION_TIMES"] = {0.0, 10.0, 1500.0}; + sec["SECTION_CONTINUITY"] = std::vector{false}; - config["solver"] = solver; + solver["sections"] = sec; } - return config; - } - cadet::JsonParameterProvider createMCT(const std::vector& volFlowRate, const std::vector& velocity, const std::vector& concentrationIn, const std::vector& crossSectionAreas, const std::vector& exchangeMatrix, const double colDisp = 5.75e-8) - { - return cadet::JsonParameterProvider(createMCTJson(volFlowRate, velocity, concentrationIn, crossSectionAreas, exchangeMatrix, colDisp)); + // Time integrator + { + json ti; + + ti["ABSTOL"] = 1e-8; + ti["RELTOL"] = 1e-6; + ti["ALGTOL"] = 1e-12; + ti["INIT_STEP_SIZE"] = 1e-6; + ti["MAX_STEPS"] = 10000; + ti["MAX_STEP_SIZE"] = 0.0; + ti["RELTOL_SENS"] = 1e-6; + ti["ERRORTEST_SENS"] = true; + ti["MAX_NEWTON_ITER"] = 3; + ti["MAX_ERRTEST_FAIL"] = 7; + ti["MAX_CONVTEST_FAIL"] = 10; + ti["MAX_NEWTON_ITER_SENS"] = 3; + ti["CONSISTENT_INIT_MODE"] = 1; + ti["CONSISTENT_INIT_MODE_SENS"] = 1; + + solver["time_integrator"] = ti; + } + + config["solver"] = solver; } + return config; +} - /** - * @brief Creates a runnable column model with given WENO order - * @details Creates a column model and configures it using the given IParameterProvider @p jpp. - * @param [in] uoType Unit operation type - * @param [in] mb ModelBuilder - * @param [in] jpp Configuration of the model - * @param [in] wenoOrder WENO order - * @return Runnable column model - */ - inline cadet::IUnitOperation* createAndConfigureUnit(cadet::IModelBuilder& mb, cadet::JsonParameterProvider& jpp, int wenoOrder) - { - // Create a unit - cadet::IModel* const iUnit = mb.createUnitOperation(jpp, 0); - REQUIRE(nullptr != iUnit); +cadet::JsonParameterProvider createMCT(const std::vector& volFlowRate, const std::vector& velocity, + const std::vector& concentrationIn, + const std::vector& crossSectionAreas, + const std::vector& exchangeMatrix, const double colDisp = 5.75e-8) +{ + return cadet::JsonParameterProvider( + createMCTJson(volFlowRate, velocity, concentrationIn, crossSectionAreas, exchangeMatrix, colDisp)); +} - cadet::IUnitOperation* const unit = reinterpret_cast(iUnit); +/** + * @brief Creates a runnable column model with given WENO order + * @details Creates a column model and configures it using the given IParameterProvider @p jpp. + * @param [in] uoType Unit operation type + * @param [in] mb ModelBuilder + * @param [in] jpp Configuration of the model + * @param [in] wenoOrder WENO order + * @return Runnable column model + */ +inline cadet::IUnitOperation* createAndConfigureUnit(cadet::IModelBuilder& mb, cadet::JsonParameterProvider& jpp, + int wenoOrder) +{ + // Create a unit + cadet::IModel* const iUnit = mb.createUnitOperation(jpp, 0); + REQUIRE(nullptr != iUnit); - // Set WENO order - cadet::test::column::setWenoOrder(jpp, wenoOrder); + cadet::IUnitOperation* const unit = reinterpret_cast(iUnit); - // Configure - cadet::ModelBuilder& temp = *reinterpret_cast(&mb); - REQUIRE(unit->configureModelDiscretization(jpp, temp)); - REQUIRE(unit->configure(jpp)); + // Set WENO order + cadet::test::column::setWenoOrder(jpp, wenoOrder); - // Do some checks - const unsigned int nComp = jpp.getInt("NCOMP"); - REQUIRE(unit->numComponents() == nComp); + // Configure + cadet::ModelBuilder& temp = *reinterpret_cast(&mb); + REQUIRE(unit->configureModelDiscretization(jpp, temp)); + REQUIRE(unit->configure(jpp)); - return unit; - } + // Do some checks + const unsigned int nComp = jpp.getInt("NCOMP"); + REQUIRE(unit->numComponents() == nComp); + + return unit; } +} // namespace TEST_CASE("MCT with two channels and without exchange yields same result on both ports", "[MCT],[Simulation],[CI]") { @@ -289,7 +298,7 @@ TEST_CASE("MCT with two channels and without exchange yields same result on both const double absTol = 1e-14; // Setup simulation - cadet::JsonParameterProvider jpp = createMCT({ 1.0, 1.0 }, {1.0, 1.0}, {1.0, 1.0}, {1.0, 1.0}, {0.0, 0.0, 0.0, 0.0}); + cadet::JsonParameterProvider jpp = createMCT({1.0, 1.0}, {1.0, 1.0}, {1.0, 1.0}, {1.0, 1.0}, {0.0, 0.0, 0.0, 0.0}); // Run simulation cadet::Driver drv; @@ -315,13 +324,15 @@ TEST_CASE("MCT with two channels and without exchange yields same result on both } } -TEST_CASE("Two MCTs with two channels and forward/backward exchange yield same output in opposite ports", "[MCT],[Simulation],[CI]") +TEST_CASE("Two MCTs with two channels and forward/backward exchange yield same output in opposite ports", + "[MCT],[Simulation],[CI]") { const double relTol = 1e-6; const double absTol = 1e-10; // Setup forward exchange simulation - cadet::JsonParameterProvider jppFwdEx = createMCT({ 1.0, 1.0 }, { 1.0, 1.0 }, { 1.0, 0.2 }, { 1.0, 1.0 }, { 0.0, 0.01, 0.0, 0.0 }); + cadet::JsonParameterProvider jppFwdEx = + createMCT({1.0, 1.0}, {1.0, 1.0}, {1.0, 0.2}, {1.0, 1.0}, {0.0, 0.01, 0.0, 0.0}); // Run simulation cadet::Driver drvFwdEx; @@ -330,9 +341,10 @@ TEST_CASE("Two MCTs with two channels and forward/backward exchange yield same o // Get data from simulation cadet::InternalStorageUnitOpRecorder const* const FwdExData = drvFwdEx.solution()->unitOperation(0); - + // Setup backward exchange simulation - cadet::JsonParameterProvider jppBwdEx = createMCT({ 1.0, 1.0 }, { 1.0, 1.0 }, { 0.2, 1.0 }, { 1.0, 1.0 }, { 0.0, 0.0, 0.01, 0.0 }); + cadet::JsonParameterProvider jppBwdEx = + createMCT({1.0, 1.0}, {1.0, 1.0}, {0.2, 1.0}, {1.0, 1.0}, {0.0, 0.0, 0.01, 0.0}); // Run simulation cadet::Driver drvBwdEx; @@ -367,7 +379,8 @@ TEST_CASE("MCT with two channels and forward/backward flow yields same result at const double relTol = 6e-4; // Setup forward exchange simulation - cadet::JsonParameterProvider jppMixFlow = createMCT({ 1.0, 1.0 }, { 1.0, -1.0 }, { 1.0, 1.0 }, { 1.0, 1.0 }, { 0.0, 0.0, 0.0, 0.0 }); + cadet::JsonParameterProvider jppMixFlow = + createMCT({1.0, 1.0}, {1.0, -1.0}, {1.0, 1.0}, {1.0, 1.0}, {0.0, 0.0, 0.0, 0.0}); // Run simulation cadet::Driver drvMixFlow; @@ -400,7 +413,7 @@ TEST_CASE("Two MCT's with forward/backward flow yield same result", "[MCT],[Simu const double absTol = 1e-10; // Setup forward exchange simulation - cadet::JsonParameterProvider jppFwdFlow = createMCT({ 1.0 }, { 1.0 }, { 1.0 }, { 1.0 }, { 0.0 }); + cadet::JsonParameterProvider jppFwdFlow = createMCT({1.0}, {1.0}, {1.0}, {1.0}, {0.0}); // Run simulation cadet::Driver drvFwdFlow; @@ -411,7 +424,7 @@ TEST_CASE("Two MCT's with forward/backward flow yield same result", "[MCT],[Simu cadet::InternalStorageUnitOpRecorder const* const FwdFlowData = drvFwdFlow.solution()->unitOperation(0); // Setup backward exchange simulation - cadet::JsonParameterProvider jppBwdFlow = createMCT({ 1.0 }, { -1.0 }, { 1.0 }, { 1.0 }, { 0.0 }); + cadet::JsonParameterProvider jppBwdFlow = createMCT({1.0}, {-1.0}, {1.0}, {1.0}, {0.0}); // Run simulation cadet::Driver drvBwdFlow; @@ -447,68 +460,79 @@ TEST_CASE("MCT inlet DOF Jacobian", "[MCT],[UnitOp],[Jacobian],[Inlet],[CI]") cadet::test::column::testInletDofJacobian("MULTI_CHANNEL_TRANSPORT", "FV"); } -TEST_CASE("MCT numerical Benchmark for 1 channel no exchange, no reaction case", "[MCT],[Simulation],[Reference],[mctReference]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE( + "MCT numerical Benchmark for 1 channel no exchange, no reaction case", + "[MCT],[Simulation],[Reference],[mctReference]") // todo CI flag: currently only runs locally but fails on server { const std::string& modelFilePath = std::string("/data/model_MCT1ch_noEx_noReac_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_MCT1ch_noEx_noReac_benchmark1_FV_Z256.h5"); - const std::vector absTol = { RelApprox::defaultEpsilon() }; - const std::vector relTol = { RelApprox::defaultMargin() }; + const std::vector absTol = {RelApprox::defaultEpsilon()}; + const std::vector relTol = {RelApprox::defaultMargin()}; cadet::test::column::FVparams disc(256); disc.setNRad(1); // will be used as NCHANNEL cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, false); } -TEST_CASE("MCT numerical Benchmark comparison with LRM (1 channel no exchange, no reaction case)", "[MCT],[Simulation],[Reference],[mctReference]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE( + "MCT numerical Benchmark comparison with LRM (1 channel no exchange, no reaction case)", + "[MCT],[Simulation],[Reference],[mctReference]") // todo CI flag: currently only runs locally but fails on server { const std::string& modelFilePath = std::string("/data/model_LRM_noBnd_1comp_MCTbenchmark.json"); const std::string& refFilePath = std::string("/data/ref_MCT1ch_noEx_noReac_benchmark1_FV_Z256.h5"); - const std::vector absTol = { RelApprox::defaultEpsilon() }; - const std::vector relTol = { RelApprox::defaultMargin() }; + const std::vector absTol = {RelApprox::defaultEpsilon()}; + const std::vector relTol = {RelApprox::defaultMargin()}; cadet::test::column::FVparams disc(256); disc.setNRad(1); // will be used as NCHANNEL cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, false); } -TEST_CASE("MCT numerical Benchmark comparison with linear binding LRM (2 channel with exchange, no reaction case)", "[MCT],[Simulation],[Reference],[mctReference],[CI]") +TEST_CASE("MCT numerical Benchmark comparison with linear binding LRM (2 channel with exchange, no reaction case)", + "[MCT],[Simulation],[Reference],[mctReference],[CI]") { const std::string& modelFilePath = std::string("/data/model_MCT2ch_1comp_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_LRM_dynLin_1comp_benchmark2_FV_Z357.h5"); - const std::vector absTol = { RelApprox::defaultEpsilon() }; - const std::vector relTol = { RelApprox::defaultMargin() }; + const std::vector absTol = {RelApprox::defaultEpsilon()}; + const std::vector relTol = {RelApprox::defaultMargin()}; cadet::test::column::FVparams disc(357); disc.setNRad(2); // will be used as NCHANNEL cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, false, 2); } -TEST_CASE("MCT numerical Benchmark for 1 channel no exchange, with reaction case", "[MCT],[Simulation],[Reference],[mctReference]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE( + "MCT numerical Benchmark for 1 channel no exchange, with reaction case", + "[MCT],[Simulation],[Reference],[mctReference]") // todo CI flag: currently only runs locally but fails on server { const std::string& modelFilePath = std::string("/data/model_MCT1ch_noEx_reac_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_MCT1ch_noEx_reac_benchmark1_FV_Z256.h5"); - const std::vector absTol = { RelApprox::defaultEpsilon() }; - const std::vector relTol = { RelApprox::defaultMargin() }; + const std::vector absTol = {RelApprox::defaultEpsilon()}; + const std::vector relTol = {RelApprox::defaultMargin()}; cadet::test::column::FVparams disc(256); disc.setNRad(1); // will be used as NCHANNEL cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, false); } -TEST_CASE("MCT numerical Benchmark for 2 channels with one-way-exchange and reaction case", "[MCT],[Simulation],[Reference],[mctReference]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE( + "MCT numerical Benchmark for 2 channels with one-way-exchange and reaction case", + "[MCT],[Simulation],[Reference],[mctReference]") // todo CI flag: currently only runs locally but fails on server { const std::string& modelFilePath = std::string("/data/model_MCT2ch_oneWayEx_reac_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_MCT2ch_oneWayEx_reac_benchmark1_FV_Z256.h5"); - const std::vector absTol = { RelApprox::defaultEpsilon() }; - const std::vector relTol = { RelApprox::defaultMargin() }; + const std::vector absTol = {RelApprox::defaultEpsilon()}; + const std::vector relTol = {RelApprox::defaultMargin()}; cadet::test::column::FVparams disc(256); disc.setNRad(2); // will be used as NCHANNEL cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, false); } -TEST_CASE("MCT numerical Benchmark for 3 channels with two-way-exchange and reaction case", "[MCT],[Simulation],[Reference],[mctReference]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE( + "MCT numerical Benchmark for 3 channels with two-way-exchange and reaction case", + "[MCT],[Simulation],[Reference],[mctReference]") // todo CI flag: currently only runs locally but fails on server { const std::string& modelFilePath = std::string("/data/model_MCT3ch_twoWayExc_reac_benchmark1.json"); const std::string& refFilePath = std::string("/data/ref_MCT3ch_twoWayExc_reac_benchmark1_FV_Z256.h5"); - const std::vector absTol = { RelApprox::defaultEpsilon() }; - const std::vector relTol = { RelApprox::defaultMargin() }; + const std::vector absTol = {RelApprox::defaultEpsilon()}; + const std::vector relTol = {RelApprox::defaultMargin()}; cadet::test::column::FVparams disc(256); disc.setNRad(3); // will be used as NCHANNEL cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, false); @@ -516,7 +540,9 @@ TEST_CASE("MCT numerical Benchmark for 3 channels with two-way-exchange and reac TEST_CASE("MCT compare AD with analytical Jacobian for 1 channel without exchange", "[MCT],[UnitOp],[Jacobian],[CI]") { - cadet::JsonParameterProvider jpp = createMCT({ 1.0 }, { 1.0 }, { 1.0 }, { 1.0 }, { 0.0 }, 1e-4); // increased col dispersion so that jacobian entries are above tolerances + cadet::JsonParameterProvider jpp = + createMCT({1.0}, {1.0}, {1.0}, {1.0}, {0.0}, + 1e-4); // increased col dispersion so that jacobian entries are above tolerances jpp.pushScope("model"); jpp.pushScope("unit_000"); cadet::test::column::testJacobianAD(jpp, std::numeric_limits::epsilon()); @@ -524,44 +550,54 @@ TEST_CASE("MCT compare AD with analytical Jacobian for 1 channel without exchang TEST_CASE("MCT compare AD with analytical Jacobian for 2 channels and exchange", "[MCT],[UnitOp],[Jacobian],[CI]") { - cadet::JsonParameterProvider jpp = createMCT({ 1.0, 1.0 }, { 1.0, 1.0 }, { 1.0, 0.2 }, { 1.0, 1.0 }, { 0.0, 0.01, 0.0, 0.0 }, 1e-4); // increased col dispersion so that jacobian entries are above tolerances + cadet::JsonParameterProvider jpp = + createMCT({1.0, 1.0}, {1.0, 1.0}, {1.0, 0.2}, {1.0, 1.0}, {0.0, 0.01, 0.0, 0.0}, + 1e-4); // increased col dispersion so that jacobian entries are above tolerances jpp.pushScope("model"); jpp.pushScope("unit_000"); - const double FDtolerance = 0.02; // large tolerance to effectively disable FD pattern check, which fails with 0.0 != -0.01 + const double FDtolerance = + 0.02; // large tolerance to effectively disable FD pattern check, which fails with 0.0 != -0.01 cadet::test::column::testJacobianAD(jpp, FDtolerance); } -TEST_CASE("MCT compare AD with analytical Jacobian for 2 channels with opposing flow directions and exchange", "[MCT],[UnitOp],[Jacobian],[CI]") +TEST_CASE("MCT compare AD with analytical Jacobian for 2 channels with opposing flow directions and exchange", + "[MCT],[UnitOp],[Jacobian],[CI]") { - cadet::JsonParameterProvider jpp = createMCT({ 1.0, 1.0 }, { 1.0, -1.0 }, { 1.0, 0.2 }, { 1.0, 1.0 }, { 0.0, 0.01, 0.0, 0.0 }, 1e-4); // increased col dispersion so that jacobian entries are above tolerances + cadet::JsonParameterProvider jpp = + createMCT({1.0, 1.0}, {1.0, -1.0}, {1.0, 0.2}, {1.0, 1.0}, {0.0, 0.01, 0.0, 0.0}, + 1e-4); // increased col dispersion so that jacobian entries are above tolerances jpp.pushScope("model"); jpp.pushScope("unit_000"); - const double FDtolerance = 0.02; // large tolerance to effectively disable FD pattern check, which fails with 0.0 != -0.01 + const double FDtolerance = + 0.02; // large tolerance to effectively disable FD pattern check, which fails with 0.0 != -0.01 cadet::test::column::testJacobianAD(jpp, FDtolerance); } TEST_CASE("MCT time derivative Jacobian vs FD", "[MCT],[UnitOp],[Residual],[Jacobian],[CI],[FD]") { // Use some test case parameters - cadet::JsonParameterProvider jpp = createMCTModelJson({ 1.0, 1.0 }, { 1.0, 1.0 }, { 0.0, 0.01, 0.0, 0.0 }); + cadet::JsonParameterProvider jpp = createMCTModelJson({1.0, 1.0}, {1.0, 1.0}, {0.0, 0.01, 0.0, 0.0}); cadet::test::unitoperation::testTimeDerivativeJacobianFD(jpp, 1e-7, 0.0, 1e-3); } TEST_CASE("MCT sensitivity Jacobians", "[MCT],[UnitOp],[Sensitivity],[CI]") { // Use some test case parameters - cadet::JsonParameterProvider jpp = createMCTModelJson({ 1.0, 1.0 }, { 1.0, 1.0 }, { 0.0, 0.01, 0.0, 0.0 }); + cadet::JsonParameterProvider jpp = createMCTModelJson({1.0, 1.0}, {1.0, 1.0}, {0.0, 0.01, 0.0, 0.0}); cadet::test::column::testFwdSensJacobians(jpp, 1e-4, 6e-7, std::numeric_limits::epsilon() * 100.0, false); } TEST_CASE("MCT consistent sensitivity initialization", "[MCT],[ConsistentInit],[Sensitivity],[CI]") { // Fill state vector with given initial values - const unsigned int numDofs = 1 * 2 + 1 * 16 * 2; // nComp * nChannel + nComp * nCol * nChannel = (inletDof + unitDof) + const unsigned int numDofs = + 1 * 2 + 1 * 16 * 2; // nComp * nChannel + nComp * nCol * nChannel = (inletDof + unitDof) std::vector y(numDofs, 0.0); std::vector yDot(numDofs, 0.0); - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); cadet::IModelBuilder* const mb = cadet::createModelBuilder(); REQUIRE(nullptr != mb); @@ -572,16 +608,23 @@ TEST_CASE("MCT consistent sensitivity initialization", "[MCT],[ConsistentInit],[ SECTION("AD " + adEnabled ? "enabled" : "disabled") { // Use some test case parameters - cadet::JsonParameterProvider jpp = createMCT({ 1.0, 1.0 }, { 1.0, 1.0 }, { 1.0, 1.0 }, { 1.0, 1.0 }, { 0.0, 0.0, 0.0, 0.0 }); + cadet::JsonParameterProvider jpp = + createMCT({1.0, 1.0}, {1.0, 1.0}, {1.0, 1.0}, {1.0, 1.0}, {0.0, 0.0, 0.0, 0.0}); jpp.pushScope("model"); jpp.pushScope("unit_000"); cadet::IUnitOperation* const unit = createAndConfigureUnit(*mb, jpp, cadet::Weno::maxOrder()); - unit->setSensitiveParameter(cadet::makeParamId("INIT_C", 0, 0, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), 0, 1.0); - unit->setSensitiveParameter(cadet::makeParamId("COL_LENGTH", 0, cadet::CompIndep, cadet::ParTypeIndep, cadet::BoundStateIndep, cadet::ReactionIndep, cadet::SectionIndep), 2, 1.0); + unit->setSensitiveParameter(cadet::makeParamId("INIT_C", 0, 0, cadet::ParTypeIndep, cadet::BoundStateIndep, + cadet::ReactionIndep, cadet::SectionIndep), + 0, 1.0); + unit->setSensitiveParameter(cadet::makeParamId("COL_LENGTH", 0, cadet::CompIndep, cadet::ParTypeIndep, + cadet::BoundStateIndep, cadet::ReactionIndep, + cadet::SectionIndep), + 2, 1.0); REQUIRE(unit->numSensParams() == 2); - cadet::test::unitoperation::testConsistentInitializationSensitivity(unit, adEnabled, y.data(), yDot.data(), 1e-14); + cadet::test::unitoperation::testConsistentInitializationSensitivity(unit, adEnabled, y.data(), yDot.data(), + 1e-14); mb->destroyUnitOperation(unit); } @@ -589,19 +632,22 @@ TEST_CASE("MCT consistent sensitivity initialization", "[MCT],[ConsistentInit],[ destroyModelBuilder(mb); } -TEST_CASE("MCT dynamic reactions time derivative Jacobian vs FD bulk", "[MCT],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("MCT dynamic reactions time derivative Jacobian vs FD bulk", + "[MCT],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::JsonParameterProvider jpp = createMCTModelJson({ 1.0, 1.0 }, { 1.0, 1.0 }, { 0.0, 0.01, 0.0, 0.0 }); + cadet::JsonParameterProvider jpp = createMCTModelJson({1.0, 1.0}, {1.0, 1.0}, {0.0, 0.01, 0.0, 0.0}); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, false, false, 1e-7, 1e-14, 8e-4); } -TEST_CASE("MCT dynamic reactions time derivative Jacobian vs FD modified bulk", "[MCT],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") +TEST_CASE("MCT dynamic reactions time derivative Jacobian vs FD modified bulk", + "[MCT],[Jacobian],[Residual],[ReactionModel],[CI],[FD]") { - cadet::JsonParameterProvider jpp = createMCTModelJson({ 1.0, 1.0 }, { 1.0, 1.0 }, { 0.0, 0.01, 0.0, 0.0 }); + cadet::JsonParameterProvider jpp = createMCTModelJson({1.0, 1.0}, {1.0, 1.0}, {0.0, 0.01, 0.0, 0.0}); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, false, true, 1e-6, 1e-14, 8e-4); } TEST_CASE("MCT dynamic reactions Jacobian vs AD bulk", "[MCT],[Jacobian],[AD],[ReactionModel],[CI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("MULTI_CHANNEL_TRANSPORT", "FV", true, false, false, std::numeric_limits::epsilon() * 100.0); -} \ No newline at end of file + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("MULTI_CHANNEL_TRANSPORT", "FV", true, false, false, + std::numeric_limits::epsilon() * 100.0); +} diff --git a/test/ParamDepTests.cpp b/test/ParamDepTests.cpp index 85f6d6f0f..ff54f459b 100644 --- a/test/ParamDepTests.cpp +++ b/test/ParamDepTests.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -33,15 +33,15 @@ namespace { - inline cadet::model::IParameterStateDependence* createParameterStateDependence(const char* name) - { - cadet::ParameterDependenceFactory pdf; - cadet::model::IParameterStateDependence* const pd = pdf.createStateDependence(name); - - REQUIRE(nullptr != pd); - return pd; - } +inline cadet::model::IParameterStateDependence* createParameterStateDependence(const char* name) +{ + cadet::ParameterDependenceFactory pdf; + cadet::model::IParameterStateDependence* const pd = pdf.createStateDependence(name); + + REQUIRE(nullptr != pd); + return pd; } +} // namespace namespace cadet { @@ -58,7 +58,8 @@ ConfiguredParameterDependence::~ConfiguredParameterDependence() delete _paramDep; } -ConfiguredParameterDependence ConfiguredParameterDependence::create(const char* name, unsigned int nComp, unsigned int const* nBound, const char* config) +ConfiguredParameterDependence ConfiguredParameterDependence::create(const char* name, unsigned int nComp, + unsigned int const* nBound, const char* config) { cadet::model::IParameterStateDependence* const pd = createParameterStateDependence(name); @@ -67,7 +68,7 @@ ConfiguredParameterDependence ConfiguredParameterDependence::create(const char* boundOffset[0] = 0; for (unsigned int i = 1; i < nComp; ++i) { - boundOffset[i] = boundOffset[i-1] + nBound[i-1]; + boundOffset[i] = boundOffset[i - 1] + nBound[i - 1]; } const unsigned int totalBoundStates = boundOffset[nComp - 1] + nBound[nComp - 1]; @@ -82,7 +83,8 @@ ConfiguredParameterDependence ConfiguredParameterDependence::create(const char* return ConfiguredParameterDependence(pd, nComp, nBound, boundOffset); } -void testLiquidJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, double const* point, double absTol, double relTol) +void testLiquidJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, + double const* point, double absTol, double relTol) { ConfiguredParameterDependence cpd = ConfiguredParameterDependence::create(modelName, nComp, nBound, config); const unsigned int numDofs = cpd.nComp(); @@ -93,7 +95,8 @@ void testLiquidJacobianAD(const char* modelName, unsigned int nComp, unsigned in jacAna.resize(numDofs, numDofs); for (unsigned int comp = 0; comp < nComp; ++comp) - cpd.model().analyticJacobianLiquidAdd(ColumnPosition{0.0, 0.0, 0.0}, 2.1, point, comp, factor, 0, jacAna.row(comp)); + cpd.model().analyticJacobianLiquidAdd(ColumnPosition{0.0, 0.0, 0.0}, 2.1, point, comp, factor, 0, + jacAna.row(comp)); // Enable AD cadet::ad::setDirections(cadet::ad::getMaxDirections()); @@ -139,7 +142,8 @@ void testLiquidJacobianAD(const char* modelName, unsigned int nComp, unsigned in } } -void testCombinedJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, double const* point, double absTol, double relTol) +void testCombinedJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, + double const* point, double absTol, double relTol) { ConfiguredParameterDependence cpd = ConfiguredParameterDependence::create(modelName, nComp, nBound, config); const unsigned int numDofs = cpd.nComp() + cpd.numBoundStates(); @@ -150,9 +154,11 @@ void testCombinedJacobianAD(const char* modelName, unsigned int nComp, unsigned jacAna.resize(numDofs, numDofs); for (unsigned int comp = 0; comp < nComp; ++comp) - cpd.model().analyticJacobianCombinedAddLiquid(ColumnPosition{0.0, 0.0, 0.0}, 2.1, point, point + nComp, comp, factor, 0, jacAna.row(comp)); + cpd.model().analyticJacobianCombinedAddLiquid(ColumnPosition{0.0, 0.0, 0.0}, 2.1, point, point + nComp, comp, + factor, 0, jacAna.row(comp)); for (unsigned int bnd = 0; bnd < cpd.numBoundStates(); ++bnd) - cpd.model().analyticJacobianCombinedAddSolid(ColumnPosition{0.0, 0.0, 0.0}, 2.1, point, point + nComp, bnd, factor, 0, jacAna.row(nComp + bnd)); + cpd.model().analyticJacobianCombinedAddSolid(ColumnPosition{0.0, 0.0, 0.0}, 2.1, point, point + nComp, bnd, + factor, 0, jacAna.row(nComp + bnd)); // Enable AD cadet::ad::setDirections(cadet::ad::getMaxDirections()); @@ -164,9 +170,11 @@ void testCombinedJacobianAD(const char* modelName, unsigned int nComp, unsigned ad::copyToAd(point, adY, numDofs); for (unsigned int comp = 0; comp < nComp; ++comp) - adRes[comp] = factor * cpd.model().combinedParameterLiquid(ColumnPosition{0.0, 0.0, 0.0}, 2.1, adY, adY + nComp, comp); + adRes[comp] = + factor * cpd.model().combinedParameterLiquid(ColumnPosition{0.0, 0.0, 0.0}, 2.1, adY, adY + nComp, comp); for (unsigned int bnd = 0; bnd < cpd.numBoundStates(); ++bnd) - adRes[nComp + bnd] = factor * cpd.model().combinedParameterSolid(ColumnPosition{0.0, 0.0, 0.0}, 2.1, adY, adY + nComp, bnd); + adRes[nComp + bnd] = + factor * cpd.model().combinedParameterSolid(ColumnPosition{0.0, 0.0, 0.0}, 2.1, adY, adY + nComp, bnd); // Extract Jacobian cadet::linalg::DenseMatrix jacAD; diff --git a/test/ParamDepTests.hpp b/test/ParamDepTests.hpp index 737cb0b10..88344cb6a 100644 --- a/test/ParamDepTests.hpp +++ b/test/ParamDepTests.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines tests for parameter dependencies. */ @@ -27,7 +27,7 @@ namespace cadet namespace model { - class IParameterStateDependence; +class IParameterStateDependence; } namespace test @@ -36,84 +36,108 @@ namespace test namespace paramdep { - class ConfiguredParameterDependence +class ConfiguredParameterDependence +{ +public: + ConfiguredParameterDependence(ConfiguredParameterDependence&& cpy) CADET_NOEXCEPT : _paramDep(cpy._paramDep), + _nComp(cpy._nComp), + _nBound(cpy._nBound), + _boundOffset(cpy._boundOffset) + { + cpy._paramDep = nullptr; + cpy._nBound = nullptr; + cpy._boundOffset = nullptr; + } + + ~ConfiguredParameterDependence(); + + inline ConfiguredParameterDependence& operator=(ConfiguredParameterDependence&& cpy) CADET_NOEXCEPT + { + _paramDep = cpy._paramDep; + _nComp = cpy._nComp; + _nBound = cpy._nBound; + _boundOffset = cpy._boundOffset; + + cpy._paramDep = nullptr; + cpy._nBound = nullptr; + cpy._boundOffset = nullptr; + + return *this; + } + + static ConfiguredParameterDependence create(const char* name, unsigned int nComp, unsigned int const* nBound, + const char* config); + + inline cadet::model::IParameterStateDependence& model() + { + return *_paramDep; + } + inline const cadet::model::IParameterStateDependence& model() const + { + return *_paramDep; + } + + inline unsigned int nComp() const + { + return _nComp; + } + inline unsigned int const* nBound() const { - public: - - ConfiguredParameterDependence(ConfiguredParameterDependence&& cpy) CADET_NOEXCEPT - : _paramDep(cpy._paramDep), _nComp(cpy._nComp), _nBound(cpy._nBound), _boundOffset(cpy._boundOffset) - { - cpy._paramDep = nullptr; - cpy._nBound = nullptr; - cpy._boundOffset = nullptr; - } - - ~ConfiguredParameterDependence(); - - inline ConfiguredParameterDependence& operator=(ConfiguredParameterDependence&& cpy) CADET_NOEXCEPT - { - _paramDep = cpy._paramDep; - _nComp = cpy._nComp; - _nBound = cpy._nBound; - _boundOffset = cpy._boundOffset; - - cpy._paramDep = nullptr; - cpy._nBound = nullptr; - cpy._boundOffset = nullptr; - - return *this; - } - - static ConfiguredParameterDependence create(const char* name, unsigned int nComp, unsigned int const* nBound, const char* config); - - inline cadet::model::IParameterStateDependence& model() { return *_paramDep; } - inline const cadet::model::IParameterStateDependence& model() const { return *_paramDep; } - - inline unsigned int nComp() const { return _nComp; } - inline unsigned int const* nBound() const { return _nBound; } - inline unsigned int const* boundOffset() const { return _boundOffset; } - - inline unsigned int numBoundStates() const { return _boundOffset[_nComp - 1] + _nBound[_nComp - 1]; } - - private: - - ConfiguredParameterDependence(cadet::model::IParameterStateDependence* paramDep, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset) - : _paramDep(paramDep), _nComp(nComp), _nBound(nBound), _boundOffset(boundOffset) - { - } - - cadet::model::IParameterStateDependence* _paramDep; - unsigned int _nComp; - unsigned int const* _nBound; - unsigned int const* _boundOffset; - }; - - /** - * @brief Checks the analytic Jacobian of the binding model against AD - * @param [in] modelName Name of the binding model - * @param [in] nComp Number of components - * @param [in] nBound Array with number of bound states for each component - * @param [in] config JSON string with binding model parameters - * @param [in] point Liquid phase values to check Jacobian at - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testLiquidJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, double const* point, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0); - - /** - * @brief Checks the analytic Jacobian of the binding model against AD - * @param [in] modelName Name of the binding model - * @param [in] nComp Number of components - * @param [in] nBound Array with number of bound states for each component - * @param [in] config JSON string with binding model parameters - * @param [in] point Liquid phase and solid phase values to check Jacobian at - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testCombinedJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, double const* point, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0); - -} // namespace binding + return _nBound; + } + inline unsigned int const* boundOffset() const + { + return _boundOffset; + } + + inline unsigned int numBoundStates() const + { + return _boundOffset[_nComp - 1] + _nBound[_nComp - 1]; + } + +private: + ConfiguredParameterDependence(cadet::model::IParameterStateDependence* paramDep, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset) + : _paramDep(paramDep), _nComp(nComp), _nBound(nBound), _boundOffset(boundOffset) + { + } + + cadet::model::IParameterStateDependence* _paramDep; + unsigned int _nComp; + unsigned int const* _nBound; + unsigned int const* _boundOffset; +}; + +/** + * @brief Checks the analytic Jacobian of the binding model against AD + * @param [in] modelName Name of the binding model + * @param [in] nComp Number of components + * @param [in] nBound Array with number of bound states for each component + * @param [in] config JSON string with binding model parameters + * @param [in] point Liquid phase values to check Jacobian at + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testLiquidJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, + double const* point, double absTol = 0.0, + double relTol = std::numeric_limits::epsilon() * 100.0); + +/** + * @brief Checks the analytic Jacobian of the binding model against AD + * @param [in] modelName Name of the binding model + * @param [in] nComp Number of components + * @param [in] nBound Array with number of bound states for each component + * @param [in] config JSON string with binding model parameters + * @param [in] point Liquid phase and solid phase values to check Jacobian at + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testCombinedJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, + double const* point, double absTol = 0.0, + double relTol = std::numeric_limits::epsilon() * 100.0); + +} // namespace paramdep } // namespace test } // namespace cadet -#endif // CADETTEST_PARAMDEPTEST_HPP_ +#endif // CADETTEST_PARAMDEPTEST_HPP_ diff --git a/test/ParameterDependencies.cpp b/test/ParameterDependencies.cpp index 68e4cee2d..e27249e7f 100644 --- a/test/ParameterDependencies.cpp +++ b/test/ParameterDependencies.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -15,18 +15,18 @@ #include "ParamDepTests.hpp" #include "ParameterDependencies.hpp" -CADET_PARAMDEPTEST("LIQUID_SALT_EXPONENTIAL", (1, 0, 2, 1), (1.1, 1.8, 0.9, 1.4, 1.7, 0.8, 2.1, 1.5), \ - R"json( "PD_EXPFACTOR": [1.0, 2.0, 1.5, 1.3], +CADET_PARAMDEPTEST("LIQUID_SALT_EXPONENTIAL", (1, 0, 2, 1), (1.1, 1.8, 0.9, 1.4, 1.7, 0.8, 2.1, 1.5), + R"json( "PD_EXPFACTOR": [1.0, 2.0, 1.5, 1.3], "PD_EXPARGMULT": [0.1, 0.2, 0.3, 0.4] )json") -CADET_PARAMDEPTEST("LIQUID_SALT_POWER", (1, 0, 2, 1), (1.1, 1.8, 0.9, 1.4, 1.7, 0.8, 2.1, 1.5), \ - R"json( "PD_POWFACTOR": [1.0, 2.0, 1.5, 1.3], +CADET_PARAMDEPTEST("LIQUID_SALT_POWER", (1, 0, 2, 1), (1.1, 1.8, 0.9, 1.4, 1.7, 0.8, 2.1, 1.5), + R"json( "PD_POWFACTOR": [1.0, 2.0, 1.5, 1.3], "PD_POWEXP": [0.1, 0.2, 0.3, 0.4] )json") -CADET_PARAMDEPTEST("LIQUID_SALT_COLLOIDAL_AFFINITY", (1, 0, 2, 1), (1.1, 1.8, 0.9, 1.4, 1.7, 0.8, 2.1, 1.5), \ - R"json( "PD_LOGKEQEXP": [1.0, 2.0, 1.5, 1.3], +CADET_PARAMDEPTEST("LIQUID_SALT_COLLOIDAL_AFFINITY", (1, 0, 2, 1), (1.1, 1.8, 0.9, 1.4, 1.7, 0.8, 2.1, 1.5), + R"json( "PD_LOGKEQEXP": [1.0, 2.0, 1.5, 1.3], "PD_LOGKEQFACTOR": [0.1, 0.2, 0.3, 0.4], "PD_LOGKEQCONST": [0.8, 1.1, 1.8, 1.6], "PD_POWFACTOR": [0.9, 1.3, 1.7, 2.2], diff --git a/test/ParameterDependencies.hpp b/test/ParameterDependencies.hpp index dbd2d645e..acc98a8f7 100644 --- a/test/ParameterDependencies.hpp +++ b/test/ParameterDependencies.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines tests for parameter dependencies. */ @@ -33,21 +33,24 @@ * @param state Array with full state vector (liquid and solid phase) in parentheses * @param config Interior of a JSON object block with parameters (prefix "PD") */ -#define CADET_PARAMDEPTEST_IMPL(modelName, tagName, postFix, nBound, state, config) \ - TEST_CASE(modelName " param dep liquid cell analytic Jacobian vs AD" postFix, "[Jacobian],[AD],[ParameterDependence]," tagName) \ - { \ - const unsigned int nBound2[] = BRACED_INIT_LIST nBound; \ - const double state2[] = BRACED_INIT_LIST state; \ - cadet::test::paramdep::testLiquidJacobianAD(modelName, sizeof(nBound2) / sizeof(unsigned int), nBound2, "{" config "}", state2); \ - } \ - TEST_CASE(modelName " param dep combined cell analytic Jacobian vs AD" postFix, "[Jacobian],[AD],[ParameterDependence]," tagName) \ - { \ - const unsigned int nBound2[] = BRACED_INIT_LIST nBound; \ - const double state2[] = BRACED_INIT_LIST state; \ - cadet::test::paramdep::testCombinedJacobianAD(modelName, sizeof(nBound2) / sizeof(unsigned int), nBound2, "{" config "}", state2); \ +#define CADET_PARAMDEPTEST_IMPL(modelName, tagName, postFix, nBound, state, config) \ + TEST_CASE(modelName " param dep liquid cell analytic Jacobian vs AD" postFix, \ + "[Jacobian],[AD],[ParameterDependence]," tagName) \ + { \ + const unsigned int nBound2[] = BRACED_INIT_LIST nBound; \ + const double state2[] = BRACED_INIT_LIST state; \ + cadet::test::paramdep::testLiquidJacobianAD(modelName, sizeof(nBound2) / sizeof(unsigned int), nBound2, \ + "{" config "}", state2); \ + } \ + TEST_CASE(modelName " param dep combined cell analytic Jacobian vs AD" postFix, \ + "[Jacobian],[AD],[ParameterDependence]," tagName) \ + { \ + const unsigned int nBound2[] = BRACED_INIT_LIST nBound; \ + const double state2[] = BRACED_INIT_LIST state; \ + cadet::test::paramdep::testCombinedJacobianAD(modelName, sizeof(nBound2) / sizeof(unsigned int), nBound2, \ + "{" config "}", state2); \ } - /** * @brief Emits tests for a parameter dependence * @param modelName Identifier of the model as string (e.g. "LINEAR") @@ -55,8 +58,7 @@ * @param state Array with full state vector (liquid and solid phase) in parentheses * @param config Interior of a JSON object block with parameters (prefix "PD") */ -#define CADET_PARAMDEPTEST(modelName, nBound, state, config) \ +#define CADET_PARAMDEPTEST(modelName, nBound, state, config) \ CADET_PARAMDEPTEST_IMPL(modelName, "[" modelName "]", "", nBound, state, config) - -#endif // CADETTEST_PARAMDEPENDENCIES_HPP_ +#endif // CADETTEST_PARAMDEPENDENCIES_HPP_ diff --git a/test/ParticleHelper.cpp b/test/ParticleHelper.cpp index 4dc2c69dc..d94d5d856 100644 --- a/test/ParticleHelper.cpp +++ b/test/ParticleHelper.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -32,121 +32,122 @@ namespace { - template - void replicateData(std::vector& data, unsigned int nTimes) +template void replicateData(std::vector& data, unsigned int nTimes) +{ + data.reserve(data.size() * nTimes); + const typename std::vector::iterator itEnd = data.end(); + for (unsigned int i = 0; i < nTimes - 1; ++i) { - data.reserve(data.size() * nTimes); - const typename std::vector::iterator itEnd = data.end(); - for (unsigned int i = 0; i < nTimes - 1; ++i) - { - data.insert(data.end(), data.begin(), itEnd); - } + data.insert(data.end(), data.begin(), itEnd); } +} - void replicateFieldDataDouble(cadet::JsonParameterProvider& jpp, const std::string& field, const std::vector& factors) - { - if (!jpp.exists(field)) - return; - - std::vector data = jpp.getDoubleArray(field); - data.reserve(data.size() * (factors.size() + 1)); - const unsigned int numElements = data.size(); +void replicateFieldDataDouble(cadet::JsonParameterProvider& jpp, const std::string& field, + const std::vector& factors) +{ + if (!jpp.exists(field)) + return; - for (std::size_t i = 0; i < factors.size(); ++i) - { - for (unsigned int j = 0; j < numElements; ++j) - data.push_back(data[j] * factors[i]); - } + std::vector data = jpp.getDoubleArray(field); + data.reserve(data.size() * (factors.size() + 1)); + const unsigned int numElements = data.size(); - jpp.set(field, data); + for (std::size_t i = 0; i < factors.size(); ++i) + { + for (unsigned int j = 0; j < numElements; ++j) + data.push_back(data[j] * factors[i]); } - void replicateFieldDataDouble(cadet::JsonParameterProvider& jpp, const std::string& field, unsigned int nTimes) - { - if (!jpp.exists(field)) - return; + jpp.set(field, data); +} - std::vector data = jpp.getDoubleArray(field); - replicateData(data, nTimes); - jpp.set(field, data); - } +void replicateFieldDataDouble(cadet::JsonParameterProvider& jpp, const std::string& field, unsigned int nTimes) +{ + if (!jpp.exists(field)) + return; - void replicateFieldDataInt(cadet::JsonParameterProvider& jpp, const std::string& field, unsigned int nTimes) - { - if (!jpp.exists(field)) - return; + std::vector data = jpp.getDoubleArray(field); + replicateData(data, nTimes); + jpp.set(field, data); +} - std::vector data = jpp.getIntArray(field); - replicateData(data, nTimes); - jpp.set(field, data); - } +void replicateFieldDataInt(cadet::JsonParameterProvider& jpp, const std::string& field, unsigned int nTimes) +{ + if (!jpp.exists(field)) + return; - void replicateFieldDataString(cadet::JsonParameterProvider& jpp, const std::string& field, unsigned int nTimes) - { - if (!jpp.exists(field)) - return; + std::vector data = jpp.getIntArray(field); + replicateData(data, nTimes); + jpp.set(field, data); +} - std::vector data = jpp.getStringArray(field); - replicateData(data, nTimes); - jpp.set(field, data); - } +void replicateFieldDataString(cadet::JsonParameterProvider& jpp, const std::string& field, unsigned int nTimes) +{ + if (!jpp.exists(field)) + return; + + std::vector data = jpp.getStringArray(field); + replicateData(data, nTimes); + jpp.set(field, data); +} - template - void extendModelToManyParticleTypesImpl(cadet::JsonParameterProvider& jpp, const factor_t& factors, unsigned int nTypes, double const* const volFrac) +template +void extendModelToManyParticleTypesImpl(cadet::JsonParameterProvider& jpp, const factor_t& factors, unsigned int nTypes, + double const* const volFrac) +{ { - { - auto ds = cadet::test::util::makeOptionalGroupScope(jpp, "discretization"); + auto ds = cadet::test::util::makeOptionalGroupScope(jpp, "discretization"); - if(jpp.exists("SPATIAL_METHOD")) - if (jpp.getString("SPATIAL_METHOD") == "DG") - { - replicateFieldDataInt(jpp, "PAR_POLYDEG", nTypes); - replicateFieldDataInt(jpp, "PAR_NELEM", nTypes); - } - else - replicateFieldDataInt(jpp, "NPAR", nTypes); + if (jpp.exists("SPATIAL_METHOD")) + if (jpp.getString("SPATIAL_METHOD") == "DG") + { + replicateFieldDataInt(jpp, "PAR_POLYDEG", nTypes); + replicateFieldDataInt(jpp, "PAR_NELEM", nTypes); + } else replicateFieldDataInt(jpp, "NPAR", nTypes); + else + replicateFieldDataInt(jpp, "NPAR", nTypes); - replicateFieldDataString(jpp, "PAR_DISC_TYPE", nTypes); - replicateFieldDataDouble(jpp, "PAR_DISC_VECTOR", nTypes); - } + replicateFieldDataString(jpp, "PAR_DISC_TYPE", nTypes); + replicateFieldDataDouble(jpp, "PAR_DISC_VECTOR", nTypes); + } - replicateFieldDataDouble(jpp, "FILM_DIFFUSION", factors); - replicateFieldDataDouble(jpp, "PAR_DIFFUSION", factors); - replicateFieldDataDouble(jpp, "PAR_SURFDIFFUSION", factors); - replicateFieldDataDouble(jpp, "PAR_GEOM", factors); - replicateFieldDataDouble(jpp, "PAR_RADIUS", factors); - replicateFieldDataDouble(jpp, "PAR_CORERADIUS", factors); - replicateFieldDataDouble(jpp, "PAR_POROSITY", factors); - replicateFieldDataDouble(jpp, "PORE_ACCESSIBILITY", factors); + replicateFieldDataDouble(jpp, "FILM_DIFFUSION", factors); + replicateFieldDataDouble(jpp, "PAR_DIFFUSION", factors); + replicateFieldDataDouble(jpp, "PAR_SURFDIFFUSION", factors); + replicateFieldDataDouble(jpp, "PAR_GEOM", factors); + replicateFieldDataDouble(jpp, "PAR_RADIUS", factors); + replicateFieldDataDouble(jpp, "PAR_CORERADIUS", factors); + replicateFieldDataDouble(jpp, "PAR_POROSITY", factors); + replicateFieldDataDouble(jpp, "PORE_ACCESSIBILITY", factors); - replicateFieldDataDouble(jpp, "INIT_CP", nTypes); - replicateFieldDataDouble(jpp, "INIT_Q", nTypes); + replicateFieldDataDouble(jpp, "INIT_CP", nTypes); + replicateFieldDataDouble(jpp, "INIT_Q", nTypes); - replicateFieldDataString(jpp, "ADSORPTION_MODEL", nTypes); - replicateFieldDataInt(jpp, "NBOUND", nTypes); + replicateFieldDataString(jpp, "ADSORPTION_MODEL", nTypes); + replicateFieldDataInt(jpp, "NBOUND", nTypes); - // Move group "adsorption" to "adsorption_000" - if (jpp.exists("adsorption")) - { - jpp.copy("adsorption", "adsorption_000"); - jpp.remove("adsorption"); - } - - // Replicate "adsorption_000" - std::ostringstream ss; - for (unsigned int i = 1; i < nTypes; ++i) - { - ss.str(""); - ss << "adsorption_" << std::setfill('0') << std::setw(3) << i; - jpp.copy("adsorption_000", ss.str()); - } + // Move group "adsorption" to "adsorption_000" + if (jpp.exists("adsorption")) + { + jpp.copy("adsorption", "adsorption_000"); + jpp.remove("adsorption"); + } - if (volFrac) - jpp.set("PAR_TYPE_VOLFRAC", std::vector(volFrac, volFrac + nTypes)); + // Replicate "adsorption_000" + std::ostringstream ss; + for (unsigned int i = 1; i < nTypes; ++i) + { + ss.str(""); + ss << "adsorption_" << std::setfill('0') << std::setw(3) << i; + jpp.copy("adsorption_000", ss.str()); } + + if (volFrac) + jpp.set("PAR_TYPE_VOLFRAC", std::vector(volFrac, volFrac + nTypes)); } +} // namespace namespace cadet { @@ -156,286 +157,299 @@ namespace test namespace particle { - void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, unsigned int nTypes, double const* const volFrac) - { - extendModelToManyParticleTypesImpl(jpp, nTypes, nTypes, volFrac); - } - - void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, unsigned int nTypes, double const* const volFrac) - { - auto ms = util::makeModelGroupScope(jpp, unit); - extendModelToManyParticleTypes(jpp, nTypes, volFrac); - } +void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, unsigned int nTypes, double const* const volFrac) +{ + extendModelToManyParticleTypesImpl(jpp, nTypes, nTypes, volFrac); +} - void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, const std::vector& factors, double const* const volFrac) - { - extendModelToManyParticleTypesImpl(jpp, factors, factors.size() + 1, volFrac); - } +void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, unsigned int nTypes, + double const* const volFrac) +{ + auto ms = util::makeModelGroupScope(jpp, unit); + extendModelToManyParticleTypes(jpp, nTypes, volFrac); +} - void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, unsigned int nTypes, double const* const paramFactors, double const* const volFrac) - { - extendModelToManyParticleTypes(jpp, std::vector(paramFactors, paramFactors + nTypes - 1), volFrac); - } +void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, const std::vector& factors, + double const* const volFrac) +{ + extendModelToManyParticleTypesImpl(jpp, factors, factors.size() + 1, volFrac); +} - void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, unsigned int nTypes, double const* const paramFactors, double const* const volFrac) - { - auto ms = util::makeModelGroupScope(jpp, unit); - extendModelToManyParticleTypes(jpp, std::vector(paramFactors, paramFactors + nTypes - 1), volFrac); - } +void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, unsigned int nTypes, + double const* const paramFactors, double const* const volFrac) +{ + extendModelToManyParticleTypes(jpp, std::vector(paramFactors, paramFactors + nTypes - 1), volFrac); +} - void setParticleTypeVolumeFractions(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, const std::vector& volFrac) - { - auto ms = util::makeModelGroupScope(jpp, unit); - setParticleTypeVolumeFractions(jpp, volFrac); - } +void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, unsigned int nTypes, + double const* const paramFactors, double const* const volFrac) +{ + auto ms = util::makeModelGroupScope(jpp, unit); + extendModelToManyParticleTypes(jpp, std::vector(paramFactors, paramFactors + nTypes - 1), volFrac); +} - void setParticleTypeVolumeFractions(cadet::JsonParameterProvider& jpp, const std::vector& volFrac) - { - jpp.set("PAR_TYPE_VOLFRAC", volFrac); - } +void setParticleTypeVolumeFractions(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, + const std::vector& volFrac) +{ + auto ms = util::makeModelGroupScope(jpp, unit); + setParticleTypeVolumeFractions(jpp, volFrac); +} - void scrambleParticleTypeFractionsSpatially(cadet::JsonParameterProvider& jpp, unsigned int nParType) - { - auto ms = cadet::test::util::makeModelGroupScope(jpp); +void setParticleTypeVolumeFractions(cadet::JsonParameterProvider& jpp, const std::vector& volFrac) +{ + jpp.set("PAR_TYPE_VOLFRAC", volFrac); +} - unsigned int nCol = 0; - unsigned int nRad = 1; - { - auto ds = cadet::test::util::makeOptionalGroupScope(jpp, "discretization"); - if (jpp.exists("SPATIAL_METHOD")) - nCol = jpp.getString("SPATIAL_METHOD") == "DG" ? (jpp.getInt("POLYDEG") + 1) * jpp.getInt("NELEM") : jpp.getInt("NCOL"); - else - nCol = jpp.getInt("NCOL"); +void scrambleParticleTypeFractionsSpatially(cadet::JsonParameterProvider& jpp, unsigned int nParType) +{ + auto ms = cadet::test::util::makeModelGroupScope(jpp); - if (jpp.exists("NRAD")) - nRad = jpp.getInt("NRAD"); - } - - const double baseFrac[] = {0.2, 0.45, 0.35}; - std::vector volFrac(nCol * nRad * nParType, 0.0); - for (unsigned int i = 0; i < nCol * nRad; ++i) - { - volFrac[i * nParType + 0] = baseFrac[(i+0) % nParType]; - volFrac[i * nParType + 1] = baseFrac[(i+1) % nParType]; - volFrac[i * nParType + 2] = baseFrac[(i+2) % nParType]; - } - cadet::test::particle::setParticleTypeVolumeFractions(jpp, volFrac); + unsigned int nCol = 0; + unsigned int nRad = 1; + { + auto ds = cadet::test::util::makeOptionalGroupScope(jpp, "discretization"); + if (jpp.exists("SPATIAL_METHOD")) + nCol = jpp.getString("SPATIAL_METHOD") == "DG" ? (jpp.getInt("POLYDEG") + 1) * jpp.getInt("NELEM") + : jpp.getInt("NCOL"); + else + nCol = jpp.getInt("NCOL"); + + if (jpp.exists("NRAD")) + nRad = jpp.getInt("NRAD"); } - void testOneVsTwoIdenticalParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol) + const double baseFrac[] = {0.2, 0.45, 0.35}; + std::vector volFrac(nCol * nRad * nParType, 0.0); + for (unsigned int i = 0; i < nCol * nRad; ++i) { - // Use Load-Wash-Elution test case - cadet::JsonParameterProvider jpp = createLWE(uoType, spatialMethod); - testOneVsTwoIdenticalParticleTypes(jpp, absTol, relTol); + volFrac[i * nParType + 0] = baseFrac[(i + 0) % nParType]; + volFrac[i * nParType + 1] = baseFrac[(i + 1) % nParType]; + volFrac[i * nParType + 2] = baseFrac[(i + 2) % nParType]; } + cadet::test::particle::setParticleTypeVolumeFractions(jpp, volFrac); +} - void testOneVsTwoIdenticalParticleTypes(cadet::JsonParameterProvider& jpp, double absTol, double relTol) - { - // Simulate without additional particle types - cadet::Driver drv; - drv.configure(jpp); - drv.run(); +void testOneVsTwoIdenticalParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol) +{ + // Use Load-Wash-Elution test case + cadet::JsonParameterProvider jpp = createLWE(uoType, spatialMethod); + testOneVsTwoIdenticalParticleTypes(jpp, absTol, relTol); +} - // Extend to two particle types - const double volFrac[] = {1.0, 0.0}; - extendModelToManyParticleTypes(jpp, 0, 2, volFrac); +void testOneVsTwoIdenticalParticleTypes(cadet::JsonParameterProvider& jpp, double absTol, double relTol) +{ + // Simulate without additional particle types + cadet::Driver drv; + drv.configure(jpp); + drv.run(); - // Simulate with first particle type only - cadet::Driver drvP1; - drvP1.configure(jpp); - drvP1.run(); + // Extend to two particle types + const double volFrac[] = {1.0, 0.0}; + extendModelToManyParticleTypes(jpp, 0, 2, volFrac); - cadet::InternalStorageUnitOpRecorder const* const data = drv.solution()->unitOperation(0); - cadet::InternalStorageUnitOpRecorder const* const p1Data = drvP1.solution()->unitOperation(0); + // Simulate with first particle type only + cadet::Driver drvP1; + drvP1.configure(jpp); + drvP1.run(); - double const* outlet = data->outlet(); - double const* p1Outlet = p1Data->outlet(); + cadet::InternalStorageUnitOpRecorder const* const data = drv.solution()->unitOperation(0); + cadet::InternalStorageUnitOpRecorder const* const p1Data = drvP1.solution()->unitOperation(0); - const unsigned int nComp = p1Data->numComponents(); - for (unsigned int i = 0; i < p1Data->numDataPoints() * p1Data->numInletPorts() * nComp; ++i, ++outlet, ++p1Outlet) - { - CAPTURE(i); - CHECK((*outlet) == makeApprox(*p1Outlet, relTol, absTol)); - } + double const* outlet = data->outlet(); + double const* p1Outlet = p1Data->outlet(); + + const unsigned int nComp = p1Data->numComponents(); + for (unsigned int i = 0; i < p1Data->numDataPoints() * p1Data->numInletPorts() * nComp; ++i, ++outlet, ++p1Outlet) + { + CAPTURE(i); + CHECK((*outlet) == makeApprox(*p1Outlet, relTol, absTol)); } +} - void testSeparateIdenticalParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol) +void testSeparateIdenticalParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol) +{ + // Use Load-Wash-Elution test case + cadet::JsonParameterProvider jpp = createLWE(uoType, spatialMethod); + testSeparateIdenticalParticleTypes(jpp, absTol, relTol); +} + +void testSeparateIdenticalParticleTypes(cadet::JsonParameterProvider& jpp, double absTol, double relTol) +{ + // Extend to two particle types + const double volFrac[] = {1.0, 0.0}; + extendModelToManyParticleTypes(jpp, 0, 2, volFrac); + + // Simulate with first particle type only + cadet::Driver drvP1; + drvP1.configure(jpp); + drvP1.run(); + + // Simulate with second particle type only + setParticleTypeVolumeFractions(jpp, 0, {0.0, 1.0}); + cadet::Driver drvP2; + drvP2.configure(jpp); + drvP2.run(); + + cadet::InternalStorageUnitOpRecorder const* const p1Data = drvP1.solution()->unitOperation(0); + cadet::InternalStorageUnitOpRecorder const* const p2Data = drvP2.solution()->unitOperation(0); + + double const* p1Outlet = p1Data->outlet(); + double const* p2Outlet = p2Data->outlet(); + + // Check outlet + const unsigned int nComp = p1Data->numComponents(); + for (unsigned int i = 0; i < p1Data->numDataPoints() * p1Data->numInletPorts() * nComp; ++i, ++p1Outlet, ++p2Outlet) { - // Use Load-Wash-Elution test case - cadet::JsonParameterProvider jpp = createLWE(uoType, spatialMethod); - testSeparateIdenticalParticleTypes(jpp, absTol, relTol); + CAPTURE(i); + CHECK((*p1Outlet) == makeApprox(*p2Outlet, relTol, absTol)); } - void testSeparateIdenticalParticleTypes(cadet::JsonParameterProvider& jpp, double absTol, double relTol) + // Check volume if available + if (p1Data->solutionConfig().storeVolume) { - // Extend to two particle types - const double volFrac[] = {1.0, 0.0}; - extendModelToManyParticleTypes(jpp, 0, 2, volFrac); - - // Simulate with first particle type only - cadet::Driver drvP1; - drvP1.configure(jpp); - drvP1.run(); - - // Simulate with second particle type only - setParticleTypeVolumeFractions(jpp, 0, {0.0, 1.0}); - cadet::Driver drvP2; - drvP2.configure(jpp); - drvP2.run(); - - cadet::InternalStorageUnitOpRecorder const* const p1Data = drvP1.solution()->unitOperation(0); - cadet::InternalStorageUnitOpRecorder const* const p2Data = drvP2.solution()->unitOperation(0); - - double const* p1Outlet = p1Data->outlet(); - double const* p2Outlet = p2Data->outlet(); - - // Check outlet - const unsigned int nComp = p1Data->numComponents(); - for (unsigned int i = 0; i < p1Data->numDataPoints() * p1Data->numInletPorts() * nComp; ++i, ++p1Outlet, ++p2Outlet) + double const* p1Vol = p1Data->volume(); + double const* p2Vol = p2Data->volume(); + for (unsigned int i = 0; i < p1Data->numDataPoints(); ++i, ++p1Vol, ++p2Vol) { CAPTURE(i); - CHECK((*p1Outlet) == makeApprox(*p2Outlet, relTol, absTol)); - } - - // Check volume if available - if (p1Data->solutionConfig().storeVolume) - { - double const* p1Vol = p1Data->volume(); - double const* p2Vol = p2Data->volume(); - for (unsigned int i = 0; i < p1Data->numDataPoints(); ++i, ++p1Vol, ++p2Vol) - { - CAPTURE(i); - CHECK((*p1Vol) == makeApprox(*p2Vol, relTol, absTol)); - } + CHECK((*p1Vol) == makeApprox(*p2Vol, relTol, absTol)); } } +} - template - void testLinearMixedParticleTypesImpl(cadet::JsonParameterProvider& jpp, double absTol, double relTol, modifier_t modify) +template +void testLinearMixedParticleTypesImpl(cadet::JsonParameterProvider& jpp, double absTol, double relTol, + modifier_t modify) +{ + for (int i = 0; i < 2; ++i) { - for (int i = 0; i < 2; ++i) + const bool dynamicBinding = (i == 0); + SECTION(std::string(dynamicBinding ? " Kinetic binding" : " Quasi-stationary binding")) { - const bool dynamicBinding = (i == 0); - SECTION(std::string(dynamicBinding ? " Kinetic binding" : " Quasi-stationary binding")) - { - setBindingMode(jpp, dynamicBinding); + setBindingMode(jpp, dynamicBinding); - // Simulate without additional particle types - cadet::Driver drv; - drv.configure(jpp); - drv.run(); + // Simulate without additional particle types + cadet::Driver drv; + drv.configure(jpp); + drv.run(); - // Extend to multiple particle types (such that we have a total of 3 types) - const double volFrac[] = {0.3, 0.6, 0.1}; - extendModelToManyParticleTypes(jpp, 0, 3, volFrac); + // Extend to multiple particle types (such that we have a total of 3 types) + const double volFrac[] = {0.3, 0.6, 0.1}; + extendModelToManyParticleTypes(jpp, 0, 3, volFrac); - modify(jpp); + modify(jpp); - // Simulate with mixed particle types - cadet::Driver drvMP; - drvMP.configure(jpp); - drvMP.run(); + // Simulate with mixed particle types + cadet::Driver drvMP; + drvMP.configure(jpp); + drvMP.run(); - cadet::InternalStorageUnitOpRecorder const* const data = drv.solution()->unitOperation(0); - cadet::InternalStorageUnitOpRecorder const* const mpData = drvMP.solution()->unitOperation(0); + cadet::InternalStorageUnitOpRecorder const* const data = drv.solution()->unitOperation(0); + cadet::InternalStorageUnitOpRecorder const* const mpData = drvMP.solution()->unitOperation(0); - double const* outlet = data->outlet(); - double const* mpOutlet = mpData->outlet(); + double const* outlet = data->outlet(); + double const* mpOutlet = mpData->outlet(); - // Check outlet - const unsigned int nComp = mpData->numComponents(); - for (unsigned int i = 0; i < mpData->numDataPoints() * mpData->numInletPorts() * nComp; ++i, ++outlet, ++mpOutlet) - { - CAPTURE(i); - CHECK((*outlet) == makeApprox(*mpOutlet, relTol, absTol)); - } + // Check outlet + const unsigned int nComp = mpData->numComponents(); + for (unsigned int i = 0; i < mpData->numDataPoints() * mpData->numInletPorts() * nComp; + ++i, ++outlet, ++mpOutlet) + { + CAPTURE(i); + CHECK((*outlet) == makeApprox(*mpOutlet, relTol, absTol)); + } - // Check volume if available - if (data->solutionConfig().storeVolume) + // Check volume if available + if (data->solutionConfig().storeVolume) + { + double const* vol = data->volume(); + double const* mpVol = mpData->volume(); + for (unsigned int i = 0; i < data->numDataPoints(); ++i, ++vol, ++mpVol) { - double const* vol = data->volume(); - double const* mpVol = mpData->volume(); - for (unsigned int i = 0; i < data->numDataPoints(); ++i, ++vol, ++mpVol) - { - CAPTURE(i); - CHECK((*vol) == makeApprox(*mpVol, relTol, absTol)); - } + CAPTURE(i); + CHECK((*vol) == makeApprox(*mpVol, relTol, absTol)); } } } } +} - void testLinearMixedParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol) - { - cadet::JsonParameterProvider jpp = createPulseInjectionColumn(uoType, spatialMethod, true); - testLinearMixedParticleTypesImpl(jpp, absTol, relTol, [](cadet::JsonParameterProvider& jpp) { }); - } +void testLinearMixedParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol) +{ + cadet::JsonParameterProvider jpp = createPulseInjectionColumn(uoType, spatialMethod, true); + testLinearMixedParticleTypesImpl(jpp, absTol, relTol, [](cadet::JsonParameterProvider& jpp) {}); +} - void testLinearMixedParticleTypes(cadet::JsonParameterProvider& jpp, double absTol, double relTol) - { - testLinearMixedParticleTypesImpl(jpp, absTol, relTol, [](cadet::JsonParameterProvider& jpp) { }); - } +void testLinearMixedParticleTypes(cadet::JsonParameterProvider& jpp, double absTol, double relTol) +{ + testLinearMixedParticleTypesImpl(jpp, absTol, relTol, [](cadet::JsonParameterProvider& jpp) {}); +} - void testLinearSpatiallyMixedParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol) - { - cadet::JsonParameterProvider jpp = createPulseInjectionColumn(uoType, spatialMethod, true); - testLinearMixedParticleTypesImpl(jpp, absTol, relTol, [](cadet::JsonParameterProvider& jpp) { scrambleParticleTypeFractionsSpatially(jpp, 3); }); - } +void testLinearSpatiallyMixedParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol) +{ + cadet::JsonParameterProvider jpp = createPulseInjectionColumn(uoType, spatialMethod, true); + testLinearMixedParticleTypesImpl( + jpp, absTol, relTol, [](cadet::JsonParameterProvider& jpp) { scrambleParticleTypeFractionsSpatially(jpp, 3); }); +} - void testJacobianMixedParticleTypes(const std::string& uoType, const std::string& spatialMethod, const double absTolFDpattern) - { - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); - testJacobianMixedParticleTypes(jpp, absTolFDpattern); - } +void testJacobianMixedParticleTypes(const std::string& uoType, const std::string& spatialMethod, + const double absTolFDpattern) +{ + cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); + testJacobianMixedParticleTypes(jpp, absTolFDpattern); +} - void testJacobianMixedParticleTypes(cadet::JsonParameterProvider& jpp, const double absTolFDpattern) - { - // Add more particle types (such that we have a total of 3 types) - const double volFrac[] = {0.3, 0.6, 0.1}; - extendModelToManyParticleTypes(jpp, {0.9, 0.8}, volFrac); +void testJacobianMixedParticleTypes(cadet::JsonParameterProvider& jpp, const double absTolFDpattern) +{ + // Add more particle types (such that we have a total of 3 types) + const double volFrac[] = {0.3, 0.6, 0.1}; + extendModelToManyParticleTypes(jpp, {0.9, 0.8}, volFrac); - unitoperation::testJacobianAD(jpp, absTolFDpattern); - } + unitoperation::testJacobianAD(jpp, absTolFDpattern); +} - void testJacobianSpatiallyMixedParticleTypes(const std::string& uoType, const std::string& spatialMethod, const double absTolFDpattern) - { - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); +void testJacobianSpatiallyMixedParticleTypes(const std::string& uoType, const std::string& spatialMethod, + const double absTolFDpattern) +{ + cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); - // Add more particle types (such that we have a total of 3 types) - extendModelToManyParticleTypes(jpp, {0.9, 0.8}, nullptr); + // Add more particle types (such that we have a total of 3 types) + extendModelToManyParticleTypes(jpp, {0.9, 0.8}, nullptr); - // Spatially inhomogeneous - scrambleParticleTypeFractionsSpatially(jpp, 3); + // Spatially inhomogeneous + scrambleParticleTypeFractionsSpatially(jpp, 3); - unitoperation::testJacobianAD(jpp, absTolFDpattern); - } + unitoperation::testJacobianAD(jpp, absTolFDpattern); +} - void testTimeDerivativeJacobianMixedParticleTypesFD(const std::string& uoType, const std::string& spatialMethod, double h, double absTol, double relTol) - { - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); - testTimeDerivativeJacobianMixedParticleTypesFD(jpp, h, absTol, relTol); - } +void testTimeDerivativeJacobianMixedParticleTypesFD(const std::string& uoType, const std::string& spatialMethod, + double h, double absTol, double relTol) +{ + cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); + testTimeDerivativeJacobianMixedParticleTypesFD(jpp, h, absTol, relTol); +} - void testTimeDerivativeJacobianMixedParticleTypesFD(cadet::JsonParameterProvider& jpp, double h, double absTol, double relTol) - { - // Add more particle types (such that we have a total of 3 types) - const double volFrac[] = {0.3, 0.6, 0.1}; - extendModelToManyParticleTypes(jpp, {0.9, 0.8}, volFrac); - unitoperation::testTimeDerivativeJacobianFD(jpp, h, absTol, relTol); - } +void testTimeDerivativeJacobianMixedParticleTypesFD(cadet::JsonParameterProvider& jpp, double h, double absTol, + double relTol) +{ + // Add more particle types (such that we have a total of 3 types) + const double volFrac[] = {0.3, 0.6, 0.1}; + extendModelToManyParticleTypes(jpp, {0.9, 0.8}, volFrac); + unitoperation::testTimeDerivativeJacobianFD(jpp, h, absTol, relTol); +} - void testArrowHeadJacobianSpatiallyMixedParticleTypes(const std::string& uoType, double h, double absTol, double relTol) - { - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, "FV"); +void testArrowHeadJacobianSpatiallyMixedParticleTypes(const std::string& uoType, double h, double absTol, double relTol) +{ + cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, "FV"); - // Add more particle types (such that we have a total of 3 types) - extendModelToManyParticleTypes(jpp, {0.9, 0.8}, nullptr); + // Add more particle types (such that we have a total of 3 types) + extendModelToManyParticleTypes(jpp, {0.9, 0.8}, nullptr); - // Spatially inhomogeneous - scrambleParticleTypeFractionsSpatially(jpp, 3); + // Spatially inhomogeneous + scrambleParticleTypeFractionsSpatially(jpp, 3); - column::testArrowHeadJacobianFD(jpp, h, absTol, relTol); - } + column::testArrowHeadJacobianFD(jpp, h, absTol, relTol); +} } // namespace particle } // namespace test diff --git a/test/ParticleHelper.hpp b/test/ParticleHelper.hpp index 7d5292747..e933c015b 100644 --- a/test/ParticleHelper.hpp +++ b/test/ParticleHelper.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines tests for unit operations with particles. */ @@ -33,207 +33,226 @@ namespace test namespace particle { - /** - * @brief Extends a model to multiple particle types by replicating the first type - * @param [in,out] jpp ParameterProvider to extend - * @param [in] unit Index of unit operation - * @param [in] nTypes Total number of particle types - * @param [in] volFrac Array with volume fractions of particle types - */ - void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, unsigned int nTypes, double const* const volFrac); - - /** - * @brief Extends a model to multiple particle types by replicating the first type - * @param [in,out] jpp ParameterProvider to extend - * @param [in] nTypes Total number of particle types - * @param [in] volFrac Array with volume fractions of particle types - */ - void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, unsigned int nTypes, double const* const volFrac); - - /** - * @brief Extends a model to multiple particle types by replicating the first type - * @details Modifies the double-valued parameters of the replicated particle types by a factor given in @p paramFactors. - The source particle type retains a factor of @c 1.0 that is not included in @p paramFactors, which has length @c nTypes-1. - * @param [in,out] jpp ParameterProvider to extend - * @param [in] unit Index of unit operation - * @param [in] nTypes Total number of particle types - * @param [in] paramFactors Array with factors for replicated double-valued parameters - * @param [in] volFrac Array with volume fractions of particle types - */ - void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, unsigned int nTypes, double const* const paramFactors, double const* const volFrac); - - /** - * @brief Extends a model to multiple particle types by replicating the first type - * @details Modifies the double-valued parameters of the replicated particle types by a factor given in @p paramFactors. - The source particle type retains a factor of @c 1.0 that is not included in @p paramFactors, which has length @c nTypes-1. - * @param [in,out] jpp ParameterProvider to extend - * @param [in] nTypes Total number of particle types - * @param [in] paramFactors Array with factors for replicated double-valued parameters - * @param [in] volFrac Array with volume fractions of particle types - */ - void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, unsigned int nTypes, double const* const paramFactors, double const* const volFrac); - - /** - * @brief Sets the volume fractions of the particle types - * @param [in,out] jpp ParameterProvider to extend - * @param [in] unit Index of unit operation - * @param [in] volFrac Array with volume fractions of particle types - */ - void setParticleTypeVolumeFractions(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, const std::vector& volFrac); - - /** - * @brief Sets the volume fractions of the particle types - * @param [in,out] jpp ParameterProvider to extend - * @param [in] volFrac Array with volume fractions of particle types - */ - void setParticleTypeVolumeFractions(cadet::JsonParameterProvider& jpp, const std::vector& volFrac); - - /** - * @brief Assigns spatially varying particle type volume fractions based on the current values - * @param [in,out] jpp ParameterProvider to extend - * @param [in] nParType Number of particle types - */ - void scrambleParticleTypeFractionsSpatially(cadet::JsonParameterProvider& jpp, unsigned int nParType); - - /** - * @brief Checks whether results of simulation with one particle type matches the one with two (identical) particle types - * @details The given simulation is performed. The existing particle type is replicated such that there are - * two identical particle types. Another simulation is run with the two particle types. - * The results of these simulations must match. - * - * @param [in] jpp Unit operation configuration - * @param [in] absTols Array with absolute error tolerances - * @param [in] relTols Array with relative error tolerances - */ - void testOneVsTwoIdenticalParticleTypes(cadet::JsonParameterProvider& jpp, double absTol, double relTol); - - /** - * @brief Checks whether results of simulation with one particle type matches the one with two (identical) particle types - * @details The LWE example is taken and simulated. The existing particle type is replicated such that there are - * two identical particle types. Another simulation is run with the two particle types. - * The results of these simulations must match. - * - * @param [in] uoType Unit operation type - * @param [in] absTols Array with absolute error tolerances - * @param [in] relTols Array with relative error tolerances - */ - void testOneVsTwoIdenticalParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol); - - /** - * @brief Checks whether, when using two separate identical particle types, results of one type match the other - * @details The given model is modified by replicating the existing particle type such that there are - * two identical particle types. Two simulations are run each one using only one particle type. - * The results of these simulations must match. - * - * @param [in] jpp Unit operation configuration - * @param [in] absTols Array with absolute error tolerances - * @param [in] relTols Array with relative error tolerances - */ - void testSeparateIdenticalParticleTypes(cadet::JsonParameterProvider& jpp, double absTol, double relTol); - - /** - * @brief Checks whether, when using two separate identical particle types, results of one type match the other - * @details The LWE example is taken and the existing particle type is replicated such that there are - * two identical particle types. Two simulations are run each one using only one particle type. - * The results of these simulations must match. - * - * @param [in] uoType Unit operation type - * @param [in] absTols Array with absolute error tolerances - * @param [in] relTols Array with relative error tolerances - */ - void testSeparateIdenticalParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol); - - /** - * @brief Checks whether a linear binding model with multiple identical particle types produces the same as result as a single type model - * @details The given model is run. Then, two additional identical particle types are added. - * The results of the multi type simulation must match the ones of the single type simulation. - * This check is conducted with both dynamic and quasi-stationary binding. - * - * @param [in] jpp Unit operation configuration - * @param [in] absTols Array with absolute error tolerances - * @param [in] relTols Array with relative error tolerances - */ - void testLinearMixedParticleTypes(cadet::JsonParameterProvider& jpp, double absTol, double relTol); - - /** - * @brief Checks whether a linear binding model with multiple identical particle types produces the same as result as a single type model - * @details The linear benchmark problem is run. Then, two additional identical particle types are added. - * The results of the multi type simulation must match the ones of the single type simulation. - * This check is conducted with both dynamic and quasi-stationary binding. - * - * @param [in] uoType Unit operation type - * @param [in] absTols Array with absolute error tolerances - * @param [in] relTols Array with relative error tolerances - */ - void testLinearMixedParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol); - - /** - * @brief Checks the full analytic Jacobian against AD for a model with multiple particle types - * @param [in] jpp Unit operation configuration - * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern - */ - void testJacobianMixedParticleTypes(cadet::JsonParameterProvider& jpp, const double absTolFDpattern = 0.0); - - /** - * @brief Checks whether a linear binding model with multiple identical particle types produces the same as result as a single type model - * @details The linear benchmark problem is run. Then, two additional identical particle types are added. - * The results of the multi type simulation must match the ones of the single type simulation. - * This check is conducted with both dynamic and quasi-stationary binding. - * - * The particle volume fractions are spatially inhomogeneous. - * - * @param [in] uoType Unit operation type - * @param [in] absTols Array with absolute error tolerances - * @param [in] relTols Array with relative error tolerances - */ - void testLinearSpatiallyMixedParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol); - - /** - * @brief Checks the full analytic Jacobian against AD for a model with multiple particle types - * @param [in] uoType Unit operation type - * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern - */ - void testJacobianMixedParticleTypes(const std::string& uoType, const std::string& spatialMethod, const double absTolFDpattern = 0.0); - - /** - * @brief Checks the full analytic Jacobian against AD for a model with multiple particle types and spatial dependence of volume fractions - * @param [in] uoType Unit operation type - * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern - */ - void testJacobianSpatiallyMixedParticleTypes(const std::string& uoType, const std::string& spatialMethod, const double absTolFDpattern = 0.0); - - /** - * @brief Checks the (analytic) time derivative Jacobian against FD for a model with multiple particle types - * @details Uses centered finite differences. - * @param [in] jpp Unit operation configuration - * @param [in] h Step size of centered finite differences - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testTimeDerivativeJacobianMixedParticleTypesFD(cadet::JsonParameterProvider& jpp, double h, double absTol, double relTol); - - /** - * @brief Checks the (analytic) time derivative Jacobian against FD for a model with multiple particle types - * @details Uses centered finite differences. - * @param [in] uoType Unit operation type - * @param [in] h Step size of centered finite differences - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testTimeDerivativeJacobianMixedParticleTypesFD(const std::string& uoType, const std::string& spatialMethod, double h, double absTol, double relTol); - - /** - * @brief Checks the bottom macro row and right macro column of the Jacobian against FD for a model with multiple particle types - * @details Uses centered finite differences to check the flux part of the Jacobian. - * @param [in] uoType Unit operation type - * @param [in] h Step size of centered finite differences - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testArrowHeadJacobianSpatiallyMixedParticleTypes(const std::string& uoType, double h, double absTol, double relTol); +/** + * @brief Extends a model to multiple particle types by replicating the first type + * @param [in,out] jpp ParameterProvider to extend + * @param [in] unit Index of unit operation + * @param [in] nTypes Total number of particle types + * @param [in] volFrac Array with volume fractions of particle types + */ +void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, unsigned int nTypes, + double const* const volFrac); + +/** + * @brief Extends a model to multiple particle types by replicating the first type + * @param [in,out] jpp ParameterProvider to extend + * @param [in] nTypes Total number of particle types + * @param [in] volFrac Array with volume fractions of particle types + */ +void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, unsigned int nTypes, + double const* const volFrac); + +/** + * @brief Extends a model to multiple particle types by replicating the first type + * @details Modifies the double-valued parameters of the replicated particle types by a factor given in @p paramFactors. + The source particle type retains a factor of @c 1.0 that is not included in @p paramFactors, which has + length @c nTypes-1. + * @param [in,out] jpp ParameterProvider to extend + * @param [in] unit Index of unit operation + * @param [in] nTypes Total number of particle types + * @param [in] paramFactors Array with factors for replicated double-valued parameters + * @param [in] volFrac Array with volume fractions of particle types + */ +void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, unsigned int nTypes, + double const* const paramFactors, double const* const volFrac); + +/** + * @brief Extends a model to multiple particle types by replicating the first type + * @details Modifies the double-valued parameters of the replicated particle types by a factor given in @p paramFactors. + The source particle type retains a factor of @c 1.0 that is not included in @p paramFactors, which has + length @c nTypes-1. + * @param [in,out] jpp ParameterProvider to extend + * @param [in] nTypes Total number of particle types + * @param [in] paramFactors Array with factors for replicated double-valued parameters + * @param [in] volFrac Array with volume fractions of particle types + */ +void extendModelToManyParticleTypes(cadet::JsonParameterProvider& jpp, unsigned int nTypes, + double const* const paramFactors, double const* const volFrac); + +/** + * @brief Sets the volume fractions of the particle types + * @param [in,out] jpp ParameterProvider to extend + * @param [in] unit Index of unit operation + * @param [in] volFrac Array with volume fractions of particle types + */ +void setParticleTypeVolumeFractions(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, + const std::vector& volFrac); + +/** + * @brief Sets the volume fractions of the particle types + * @param [in,out] jpp ParameterProvider to extend + * @param [in] volFrac Array with volume fractions of particle types + */ +void setParticleTypeVolumeFractions(cadet::JsonParameterProvider& jpp, const std::vector& volFrac); + +/** + * @brief Assigns spatially varying particle type volume fractions based on the current values + * @param [in,out] jpp ParameterProvider to extend + * @param [in] nParType Number of particle types + */ +void scrambleParticleTypeFractionsSpatially(cadet::JsonParameterProvider& jpp, unsigned int nParType); + +/** + * @brief Checks whether results of simulation with one particle type matches the one with two (identical) particle + * types + * @details The given simulation is performed. The existing particle type is replicated such that there are + * two identical particle types. Another simulation is run with the two particle types. + * The results of these simulations must match. + * + * @param [in] jpp Unit operation configuration + * @param [in] absTols Array with absolute error tolerances + * @param [in] relTols Array with relative error tolerances + */ +void testOneVsTwoIdenticalParticleTypes(cadet::JsonParameterProvider& jpp, double absTol, double relTol); + +/** + * @brief Checks whether results of simulation with one particle type matches the one with two (identical) particle + * types + * @details The LWE example is taken and simulated. The existing particle type is replicated such that there are + * two identical particle types. Another simulation is run with the two particle types. + * The results of these simulations must match. + * + * @param [in] uoType Unit operation type + * @param [in] absTols Array with absolute error tolerances + * @param [in] relTols Array with relative error tolerances + */ +void testOneVsTwoIdenticalParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol); + +/** + * @brief Checks whether, when using two separate identical particle types, results of one type match the other + * @details The given model is modified by replicating the existing particle type such that there are + * two identical particle types. Two simulations are run each one using only one particle type. + * The results of these simulations must match. + * + * @param [in] jpp Unit operation configuration + * @param [in] absTols Array with absolute error tolerances + * @param [in] relTols Array with relative error tolerances + */ +void testSeparateIdenticalParticleTypes(cadet::JsonParameterProvider& jpp, double absTol, double relTol); + +/** + * @brief Checks whether, when using two separate identical particle types, results of one type match the other + * @details The LWE example is taken and the existing particle type is replicated such that there are + * two identical particle types. Two simulations are run each one using only one particle type. + * The results of these simulations must match. + * + * @param [in] uoType Unit operation type + * @param [in] absTols Array with absolute error tolerances + * @param [in] relTols Array with relative error tolerances + */ +void testSeparateIdenticalParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol); + +/** + * @brief Checks whether a linear binding model with multiple identical particle types produces the same as result as a + * single type model + * @details The given model is run. Then, two additional identical particle types are added. + * The results of the multi type simulation must match the ones of the single type simulation. + * This check is conducted with both dynamic and quasi-stationary binding. + * + * @param [in] jpp Unit operation configuration + * @param [in] absTols Array with absolute error tolerances + * @param [in] relTols Array with relative error tolerances + */ +void testLinearMixedParticleTypes(cadet::JsonParameterProvider& jpp, double absTol, double relTol); + +/** + * @brief Checks whether a linear binding model with multiple identical particle types produces the same as result as a + * single type model + * @details The linear benchmark problem is run. Then, two additional identical particle types are added. + * The results of the multi type simulation must match the ones of the single type simulation. + * This check is conducted with both dynamic and quasi-stationary binding. + * + * @param [in] uoType Unit operation type + * @param [in] absTols Array with absolute error tolerances + * @param [in] relTols Array with relative error tolerances + */ +void testLinearMixedParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol); + +/** + * @brief Checks the full analytic Jacobian against AD for a model with multiple particle types + * @param [in] jpp Unit operation configuration + * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern + */ +void testJacobianMixedParticleTypes(cadet::JsonParameterProvider& jpp, const double absTolFDpattern = 0.0); + +/** + * @brief Checks whether a linear binding model with multiple identical particle types produces the same as result as a + * single type model + * @details The linear benchmark problem is run. Then, two additional identical particle types are added. + * The results of the multi type simulation must match the ones of the single type simulation. + * This check is conducted with both dynamic and quasi-stationary binding. + * + * The particle volume fractions are spatially inhomogeneous. + * + * @param [in] uoType Unit operation type + * @param [in] absTols Array with absolute error tolerances + * @param [in] relTols Array with relative error tolerances + */ +void testLinearSpatiallyMixedParticleTypes(const char* uoType, const char* spatialMethod, double absTol, double relTol); + +/** + * @brief Checks the full analytic Jacobian against AD for a model with multiple particle types + * @param [in] uoType Unit operation type + * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern + */ +void testJacobianMixedParticleTypes(const std::string& uoType, const std::string& spatialMethod, + const double absTolFDpattern = 0.0); + +/** + * @brief Checks the full analytic Jacobian against AD for a model with multiple particle types and spatial dependence + * of volume fractions + * @param [in] uoType Unit operation type + * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern + */ +void testJacobianSpatiallyMixedParticleTypes(const std::string& uoType, const std::string& spatialMethod, + const double absTolFDpattern = 0.0); + +/** + * @brief Checks the (analytic) time derivative Jacobian against FD for a model with multiple particle types + * @details Uses centered finite differences. + * @param [in] jpp Unit operation configuration + * @param [in] h Step size of centered finite differences + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testTimeDerivativeJacobianMixedParticleTypesFD(cadet::JsonParameterProvider& jpp, double h, double absTol, + double relTol); + +/** + * @brief Checks the (analytic) time derivative Jacobian against FD for a model with multiple particle types + * @details Uses centered finite differences. + * @param [in] uoType Unit operation type + * @param [in] h Step size of centered finite differences + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testTimeDerivativeJacobianMixedParticleTypesFD(const std::string& uoType, const std::string& spatialMethod, + double h, double absTol, double relTol); + +/** + * @brief Checks the bottom macro row and right macro column of the Jacobian against FD for a model with multiple + * particle types + * @details Uses centered finite differences to check the flux part of the Jacobian. + * @param [in] uoType Unit operation type + * @param [in] h Step size of centered finite differences + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testArrowHeadJacobianSpatiallyMixedParticleTypes(const std::string& uoType, double h, double absTol, + double relTol); } // namespace particle } // namespace test } // namespace cadet -#endif // CADETTEST_PARTICLEHELPER_HPP_ +#endif // CADETTEST_PARTICLEHELPER_HPP_ diff --git a/test/Paths.cpp.in b/test/Paths.cpp.in index d2178e79a..a51932b06 100644 --- a/test/Paths.cpp.in +++ b/test/Paths.cpp.in @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and diff --git a/test/RadialGeneralRateModel.cpp b/test/RadialGeneralRateModel.cpp index 5e5b1c7d7..d487434f6 100644 --- a/test/RadialGeneralRateModel.cpp +++ b/test/RadialGeneralRateModel.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -26,12 +26,14 @@ // todo add (more) numerical reference (and EOC) tests -TEST_CASE("Radial GRM numerical Benchmark with parameter sensitivities for linear case", "[RadGRM],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE( + "Radial GRM numerical Benchmark with parameter sensitivities for linear case", + "[RadGRM],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on server { const std::string& modelFilePath = std::string("/data/model_radGRM_dynLin_1comp_sensbenchmark1.json"); const std::string& refFilePath = std::string("/data/ref_radGRM_dynLin_1comp_sensbenchmark1_FV_Z32parZ4.h5"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1e-6, 1e-6, 1e-6, 1e-6 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1e-6, 1e-6, 1e-6, 1e-6}; cadet::test::column::FVparams disc(32, 4); cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, true); } @@ -42,7 +44,8 @@ TEST_CASE("Radial GRM transport Jacobian", "[RadGRM],[UnitOp],[Jacobian],[CI]") cadet::test::column::testJacobianAD(jpp, std::numeric_limits::epsilon() * 100.0); } -// NOTE: THE FOLLOWING TESTS ARE ONLY INCLUDED IN THE RELEASE CI, NOT THE STANDARD CI SINCE THEY ARE (TO A HIGH DEGREE) REDUNDANT WITH THE AXIAL FLOW TESTS +// NOTE: THE FOLLOWING TESTS ARE ONLY INCLUDED IN THE RELEASE CI, NOT THE STANDARD CI SINCE THEY ARE (TO A HIGH DEGREE) +// REDUNDANT WITH THE AXIAL FLOW TESTS TEST_CASE("Radial GRM Jacobian forward vs backward flow", "[RadGRM],[UnitOp],[Residual],[Jacobian],[AD],[ReleaseCI]") { @@ -55,22 +58,26 @@ TEST_CASE("Radial GRM time derivative Jacobian vs FD", "[RadGRM],[UnitOp],[Resid cadet::test::column::testTimeDerivativeJacobianFD("RADIAL_GENERAL_RATE_MODEL", "FV", 1e-6, 0.0, 9e-4); } -TEST_CASE("Radial GRM rapid-equilibrium binding flux Jacobian vs FD", "[RadGRM],[UnitOp],[Residual],[Jacobian],[ReleaseCI],[FD]") +TEST_CASE("Radial GRM rapid-equilibrium binding flux Jacobian vs FD", + "[RadGRM],[UnitOp],[Residual],[Jacobian],[ReleaseCI],[FD]") { cadet::test::column::testArrowHeadJacobianFD("RADIAL_GENERAL_RATE_MODEL", false, 1e-6, 2e-9); } -TEST_CASE("Radial GRM rapid-equilibrium binding with surf diff par dep flux Jacobian vs FD", "[RadGRM],[UnitOp],[Residual],[Jacobian],[ParameterDependence],[ReleaseCI],[FD]") +TEST_CASE("Radial GRM rapid-equilibrium binding with surf diff par dep flux Jacobian vs FD", + "[RadGRM],[UnitOp],[Residual],[Jacobian],[ParameterDependence],[ReleaseCI],[FD]") { cadet::test::column::testArrowHeadJacobianFDVariableParSurfDiff("RADIAL_GENERAL_RATE_MODEL", 1e-6, 5e-9); } -TEST_CASE("Radial GRM dynamic binding with surf diff par dep Jacobian vs AD", "[RadGRM],[UnitOp],[Residual],[Jacobian],[ParameterDependence],[ReleaseCI]") +TEST_CASE("Radial GRM dynamic binding with surf diff par dep Jacobian vs AD", + "[RadGRM],[UnitOp],[Residual],[Jacobian],[ParameterDependence],[ReleaseCI]") { cadet::test::column::testJacobianADVariableParSurfDiff("RADIAL_GENERAL_RATE_MODEL", "FV", true); } -TEST_CASE("Radial GRM rapid-equilibrium binding with surf diff par dep Jacobian vs AD", "[RadGRM],[UnitOp],[Residual],[Jacobian],[ParameterDependence],[ReleaseCI]") +TEST_CASE("Radial GRM rapid-equilibrium binding with surf diff par dep Jacobian vs AD", + "[RadGRM],[UnitOp],[Residual],[Jacobian],[ParameterDependence],[ReleaseCI]") { cadet::test::column::testJacobianADVariableParSurfDiff("RADIAL_GENERAL_RATE_MODEL", "FV", false); } @@ -87,7 +94,8 @@ TEST_CASE("Radial GRM sensitivity Jacobians", "[RadGRM],[UnitOp],[Sensitivity],[ cadet::test::column::testFwdSensJacobians(jpp, 1e-4, 6e-7); } -//TEST_CASE("Radial GRM forward sensitivity vs FD", "[RadGRM],[Sensitivity],[Simulation],[failedFDtestGRM]") // todo fix. fails for SMA_KA for both binding modes +// TEST_CASE("Radial GRM forward sensitivity vs FD", "[RadGRM],[Sensitivity],[Simulation],[failedFDtestGRM]") // todo +// fix. fails for SMA_KA for both binding modes //{ // // todo comment // // Relative error is checked first, we use high absolute error for letting @@ -97,50 +105,58 @@ TEST_CASE("Radial GRM sensitivity Jacobians", "[RadGRM],[UnitOp],[Sensitivity],[ // const double absTols[] = {3e5, 2e-3, 2e-4, 5.0}; // const double relTols[] = {5e-3, 7e-2, 8e-2, 1e-4}; // const double passRatio[] = {0.95, 0.9, 0.91, 0.83}; -// cadet::test::column::testFwdSensSolutionFD("RADIAL_GENERAL_RATE_MODEL", false, fdStepSize, absTols, relTols, passRatio); -//} +// cadet::test::column::testFwdSensSolutionFD("RADIAL_GENERAL_RATE_MODEL", false, fdStepSize, absTols, relTols, +// passRatio); +// } // -//TEST_CASE("Radial GRM forward sensitivity forward vs backward flow", "[RadGRM],[Sensitivity],[Simulation],[fixGRM]") // todo fix. fails for COL_DISPERION for both binding modes +// TEST_CASE("Radial GRM forward sensitivity forward vs backward flow", "[RadGRM],[Sensitivity],[Simulation],[fixGRM]") +// // todo fix. fails for COL_DISPERION for both binding modes //{ // const double absTols[] = {4e-5, 1e-11, 1e-11, 8e-9}; // const double relTols[] = {6e-9, 5e-8, 5e-6, 5e-10}; // const double passRatio[] = {0.99, 0.95, 0.98, 0.98}; // cadet::test::column::testFwdSensSolutionForwardBackward("RADIAL_GENERAL_RATE_MODEL", absTols, relTols, passRatio); -//} +// } TEST_CASE("Radial GRM consistent initialization with linear binding", "[RadGRM],[ConsistentInit],[ReleaseCI]") { cadet::test::column::testConsistentInitializationLinearBinding("RADIAL_GENERAL_RATE_MODEL", "FV", 1e-12, 1e-12); } -//TEST_CASE("Radial GRM consistent initialization with SMA binding", "[RadGRM],[ConsistentInit],[fixGRM]") // todo fix +// TEST_CASE("Radial GRM consistent initialization with SMA binding", "[RadGRM],[ConsistentInit],[fixGRM]") // todo fix //{ // std::vector y(4 + 4 * 16 + 16 * 4 * (4 + 4) + 4 * 16, 0.0); //// Optimal values: -//// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, +//// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, //// 1.0, 1.8, 1.5, 1.6, 856.173, 64.457, 5.73227, 2.85286}; -// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, +// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, // 1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0}; -// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 16); -// cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 16, 4 * 16 / 2); -// cadet::test::util::populate(y.data() + 4 + 4 * 16 + 16 * 4 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); +// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 +//* 16); cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 16, 4 * 16 / 2); +// cadet::test::util::populate(y.data() + 4 + 4 * 16 + 16 * 4 * (4 + 4), [](unsigned int idx) { return +// std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); // // cadet::test::column::testConsistentInitializationSMABinding("RADIAL_GENERAL_RATE_MODEL", y.data(), 1e-14, 1e-5); //} -TEST_CASE("Radial GRM consistent sensitivity initialization with linear binding", "[RadGRM],[ConsistentInit],[Sensitivity],[ReleaseCI]") +TEST_CASE("Radial GRM consistent sensitivity initialization with linear binding", + "[RadGRM],[ConsistentInit],[Sensitivity],[ReleaseCI]") { // Fill state vector with given initial values const unsigned int numDofs = 4 + 4 * 16 + 16 * 4 * (4 + 4) + 4 * 16; std::vector y(numDofs, 0.0); std::vector yDot(numDofs, 0.0); - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - cadet::test::column::testConsistentInitializationSensitivity("RADIAL_GENERAL_RATE_MODEL", "FV", y.data(), yDot.data(), true, 1e-14); + cadet::test::column::testConsistentInitializationSensitivity("RADIAL_GENERAL_RATE_MODEL", "FV", y.data(), + yDot.data(), true, 1e-14); } -TEST_CASE("Radial GRM consistent sensitivity initialization with SMA binding", "[RadGRM],[ConsistentInit],[Sensitivity],[ReleaseCI]") +TEST_CASE("Radial GRM consistent sensitivity initialization with SMA binding", + "[RadGRM],[ConsistentInit],[Sensitivity],[ReleaseCI]") { // Fill state vector with given initial values const unsigned int numDofs = 4 + 4 * 16 + 16 * 4 * (4 + 4) + 4 * 16; @@ -148,13 +164,18 @@ TEST_CASE("Radial GRM consistent sensitivity initialization with SMA binding", " std::vector yDot(numDofs, 0.0); const double bindingCell[] = {1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0}; - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 16); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 16); cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 8, 4 * 16); - cadet::test::util::populate(y.data() + 4 + 4 * 16 + 16 * 4 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); + cadet::test::util::populate( + y.data() + 4 + 4 * 16 + 16 * 4 * (4 + 4), + [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - cadet::test::column::testConsistentInitializationSensitivity("RADIAL_GENERAL_RATE_MODEL", "FV", y.data(), yDot.data(), false, 1e-9); + cadet::test::column::testConsistentInitializationSensitivity("RADIAL_GENERAL_RATE_MODEL", "FV", y.data(), + yDot.data(), false, 1e-9); } TEST_CASE("Radial GRM inlet DOF Jacobian", "[RadGRM],[UnitOp],[Jacobian],[Inlet],[ReleaseCI]") @@ -168,7 +189,8 @@ TEST_CASE("Radial GRM with two component linear binding Jacobian", "[RadGRM],[Un cadet::test::column::testJacobianAD(jpp); } -TEST_CASE("Radial GRM LWE one vs two identical particle types match", "[RadGRM],[Simulation],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial GRM LWE one vs two identical particle types match", + "[RadGRM],[Simulation],[ParticleType],[ReleaseCI]") { cadet::test::particle::testOneVsTwoIdenticalParticleTypes("RADIAL_GENERAL_RATE_MODEL", "FV", 2e-8, 5e-5); } @@ -178,34 +200,42 @@ TEST_CASE("Radial GRM LWE separate identical particle types match", "[RadGRM],[S cadet::test::particle::testSeparateIdenticalParticleTypes("RADIAL_GENERAL_RATE_MODEL", "FV", 1e-15, 1e-15); } -TEST_CASE("Radial GRM linear binding single particle matches particle distribution", "[RadGRM],[Simulation],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial GRM linear binding single particle matches particle distribution", + "[RadGRM],[Simulation],[ParticleType],[ReleaseCI]") { cadet::test::particle::testLinearMixedParticleTypes("RADIAL_GENERAL_RATE_MODEL", "FV", 5e-8, 5e-5); } -TEST_CASE("Radial GRM multiple particle types Jacobian analytic vs AD", "[RadGRM],[Jacobian],[AD],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial GRM multiple particle types Jacobian analytic vs AD", + "[RadGRM],[Jacobian],[AD],[ParticleType],[ReleaseCI]") { cadet::test::particle::testJacobianMixedParticleTypes("RADIAL_GENERAL_RATE_MODEL", "FV"); } -TEST_CASE("Radial GRM multiple particle types time derivative Jacobian vs FD", "[RadGRM],[UnitOp],[Residual],[Jacobian],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial GRM multiple particle types time derivative Jacobian vs FD", + "[RadGRM],[UnitOp],[Residual],[Jacobian],[ParticleType],[ReleaseCI]") { - cadet::test::particle::testTimeDerivativeJacobianMixedParticleTypesFD("RADIAL_GENERAL_RATE_MODEL", "FV", 1e-6, 0.0, 9e-4); + cadet::test::particle::testTimeDerivativeJacobianMixedParticleTypesFD("RADIAL_GENERAL_RATE_MODEL", "FV", 1e-6, 0.0, + 9e-4); } -TEST_CASE("Radial GRM multiple spatially dependent particle types Jacobian analytic vs AD", "[RadGRM],[Jacobian],[AD],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial GRM multiple spatially dependent particle types Jacobian analytic vs AD", + "[RadGRM],[Jacobian],[AD],[ParticleType],[ReleaseCI]") { cadet::test::particle::testJacobianSpatiallyMixedParticleTypes("RADIAL_GENERAL_RATE_MODEL", "FV"); } -TEST_CASE("Radial GRM linear binding single particle matches spatially dependent particle distribution", "[RadGRM],[Simulation],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial GRM linear binding single particle matches spatially dependent particle distribution", + "[RadGRM],[Simulation],[ParticleType],[ReleaseCI]") { cadet::test::particle::testLinearSpatiallyMixedParticleTypes("RADIAL_GENERAL_RATE_MODEL", "FV", 5e-8, 5e-5); } -TEST_CASE("Radial GRM multiple spatially dependent particle types flux Jacobian vs FD", "[RadGRM],[UnitOp],[Residual],[Jacobian],[ParticleType],[ReleaseCI],[FD]") +TEST_CASE("Radial GRM multiple spatially dependent particle types flux Jacobian vs FD", + "[RadGRM],[UnitOp],[Residual],[Jacobian],[ParticleType],[ReleaseCI],[FD]") { - cadet::test::particle::testArrowHeadJacobianSpatiallyMixedParticleTypes("RADIAL_GENERAL_RATE_MODEL", 1e-6, 1e-8, 1e-5); + cadet::test::particle::testArrowHeadJacobianSpatiallyMixedParticleTypes("RADIAL_GENERAL_RATE_MODEL", 1e-6, 1e-8, + 1e-5); } TEST_CASE("Radial GRM dynamic reactions Jacobian vs AD bulk", "[RadGRM],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") @@ -213,49 +243,63 @@ TEST_CASE("Radial GRM dynamic reactions Jacobian vs AD bulk", "[RadGRM],[Jacobia cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_GENERAL_RATE_MODEL", "FV", true, false, false); } -TEST_CASE("Radial GRM dynamic reactions Jacobian vs AD particle", "[RadGRM],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") +TEST_CASE("Radial GRM dynamic reactions Jacobian vs AD particle", + "[RadGRM],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") { cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_GENERAL_RATE_MODEL", "FV", false, true, false); } -TEST_CASE("Radial GRM dynamic reactions Jacobian vs AD modified particle", "[RadGRM],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") +TEST_CASE("Radial GRM dynamic reactions Jacobian vs AD modified particle", + "[RadGRM],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") { cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_GENERAL_RATE_MODEL", "FV", false, true, true); } -TEST_CASE("Radial GRM dynamic reactions Jacobian vs AD bulk and particle", "[RadGRM],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") +TEST_CASE("Radial GRM dynamic reactions Jacobian vs AD bulk and particle", + "[RadGRM],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") { cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_GENERAL_RATE_MODEL", "FV", true, true, false); } -TEST_CASE("Radial GRM dynamic reactions Jacobian vs AD bulk and modified particle", "[RadGRM],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") +TEST_CASE("Radial GRM dynamic reactions Jacobian vs AD bulk and modified particle", + "[RadGRM],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") { cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_GENERAL_RATE_MODEL", "FV", true, true, true); } -TEST_CASE("Radial GRM dynamic reactions time derivative Jacobian vs FD bulk", "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") +TEST_CASE("Radial GRM dynamic reactions time derivative Jacobian vs FD bulk", + "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_GENERAL_RATE_MODEL", "FV", true, false, false, 1e-6, 1e-14, 9e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_GENERAL_RATE_MODEL", "FV", true, false, + false, 1e-6, 1e-14, 9e-4); } -TEST_CASE("Radial GRM dynamic reactions time derivative Jacobian vs FD particle", "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") +TEST_CASE("Radial GRM dynamic reactions time derivative Jacobian vs FD particle", + "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_GENERAL_RATE_MODEL", "FV", false, true, false, 1e-6, 1e-14, 9e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_GENERAL_RATE_MODEL", "FV", false, true, + false, 1e-6, 1e-14, 9e-4); } -TEST_CASE("Radial GRM dynamic reactions time derivative Jacobian vs FD modified particle", "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") +TEST_CASE("Radial GRM dynamic reactions time derivative Jacobian vs FD modified particle", + "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_GENERAL_RATE_MODEL", "FV", false, true, true, 1e-6, 1e-14, 9e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_GENERAL_RATE_MODEL", "FV", false, true, + true, 1e-6, 1e-14, 9e-4); } -TEST_CASE("Radial GRM dynamic reactions time derivative Jacobian vs FD bulk and particle", "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") +TEST_CASE("Radial GRM dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_GENERAL_RATE_MODEL", "FV", true, true, false, 1e-6, 1e-14, 9e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_GENERAL_RATE_MODEL", "FV", true, true, + false, 1e-6, 1e-14, 9e-4); } -TEST_CASE("Radial GRM dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") +TEST_CASE("Radial GRM dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_GENERAL_RATE_MODEL", "FV", true, true, true, 1e-6, 1e-14, 9e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_GENERAL_RATE_MODEL", "FV", true, true, + true, 1e-6, 1e-14, 9e-4); } inline cadet::JsonParameterProvider createColumnWithTwoCompLinearBindingThreeParticleTypes() @@ -269,61 +313,71 @@ inline cadet::JsonParameterProvider createColumnWithTwoCompLinearBindingThreePar return jpp; } -TEST_CASE("Radial GRM multi particle types dynamic reactions Jacobian vs AD bulk", "[RadGRM],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial GRM multi particle types dynamic reactions Jacobian vs AD bulk", + "[RadGRM],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, false, false); } -TEST_CASE("Radial GRM multi particle types dynamic reactions Jacobian vs AD particle", "[RadGRM],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial GRM multi particle types dynamic reactions Jacobian vs AD particle", + "[RadGRM],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, false, true, false); } -TEST_CASE("Radial GRM multi particle types dynamic reactions Jacobian vs AD modified particle", "[RadGRM],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial GRM multi particle types dynamic reactions Jacobian vs AD modified particle", + "[RadGRM],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, false, true, true); } -TEST_CASE("Radial GRM multi particle types dynamic reactions Jacobian vs AD bulk and particle", "[RadGRM],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial GRM multi particle types dynamic reactions Jacobian vs AD bulk and particle", + "[RadGRM],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, true, false); } -TEST_CASE("Radial GRM multi particle types dynamic reactions Jacobian vs AD bulk and modified particle", "[RadGRM],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial GRM multi particle types dynamic reactions Jacobian vs AD bulk and modified particle", + "[RadGRM],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, true, true); } -TEST_CASE("Radial GRM multi particle types dynamic reactions time derivative Jacobian vs FD bulk", "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") +TEST_CASE("Radial GRM multi particle types dynamic reactions time derivative Jacobian vs FD bulk", + "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, false, false, 1e-6, 1e-14, 9e-4); } -TEST_CASE("Radial GRM multi particle types dynamic reactions time derivative Jacobian vs FD particle", "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") +TEST_CASE("Radial GRM multi particle types dynamic reactions time derivative Jacobian vs FD particle", + "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, false, 1e-6, 1e-14, 9e-4); } -TEST_CASE("Radial GRM multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") +TEST_CASE("Radial GRM multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", + "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, true, 1e-6, 1e-14, 9e-4); } -TEST_CASE("Radial GRM multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") +TEST_CASE("Radial GRM multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, false, 1e-6, 1e-14, 9e-4); } -TEST_CASE("Radial GRM multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") +TEST_CASE("Radial GRM multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[RadGRM],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, true, 1e-6, 1e-14, 9e-4); diff --git a/test/RadialLumpedRateModelWithPores.cpp b/test/RadialLumpedRateModelWithPores.cpp index 9267b5a87..018d38156 100644 --- a/test/RadialLumpedRateModelWithPores.cpp +++ b/test/RadialLumpedRateModelWithPores.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -25,24 +25,28 @@ // todo find analytical solution without binding // todo add (more) numerical reference (and EOC) tests - -TEST_CASE("Radial LRMP numerical Benchmark with parameter sensitivities for linear case", "[RadLRMP],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on server + +TEST_CASE( + "Radial LRMP numerical Benchmark with parameter sensitivities for linear case", + "[RadLRMP],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on server { const std::string& modelFilePath = std::string("/data/model_radLRMP_dynLin_1comp_sensbenchmark1.json"); const std::string& refFilePath = std::string("/data/ref_radLRMP_dynLin_1comp_sensbenchmark1_FV_Z32.h5"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1e-6, 1e-6, 1e-6, 1e-6 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1e-6, 1e-6, 1e-6, 1e-6}; cadet::test::column::FVparams disc(32); cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, true); } TEST_CASE("Radial LRMP transport Jacobian", "[RadLRMP],[UnitOp],[Jacobian],[CI]") { - cadet::JsonParameterProvider jpp = createColumnLinearBenchmark(false, true, "RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV"); + cadet::JsonParameterProvider jpp = + createColumnLinearBenchmark(false, true, "RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV"); cadet::test::column::testJacobianAD(jpp); } -// NOTE: THE FOLLOWING TESTS ARE ONLY INCLUDED IN THE RELEASE CI, NOT THE STANDARD CI SINCE THEY ARE (TO A HIGH DEGREE) REDUNDANT WITH THE AXIAL FLOW TESTS +// NOTE: THE FOLLOWING TESTS ARE ONLY INCLUDED IN THE RELEASE CI, NOT THE STANDARD CI SINCE THEY ARE (TO A HIGH DEGREE) +// REDUNDANT WITH THE AXIAL FLOW TESTS TEST_CASE("Radial LRMP Jacobian forward vs backward flow", "[RadLRMP],[UnitOp],[Residual],[Jacobian],[AD],[ReleaseCI]") { @@ -62,12 +66,14 @@ TEST_CASE("Radial LRMP flux Jacobian vs FD", "[RadLRMP],[UnitOp],[Residual],[Jac TEST_CASE("Radial LRMP sensitivity Jacobians", "[RadLRMP],[UnitOp],[Sensitivity],[ReleaseCI]") { - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV"); + cadet::JsonParameterProvider jpp = + createColumnWithTwoCompLinearBinding("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV"); cadet::test::column::testFwdSensJacobians(jpp, 1e-4, 6e-7); } -//TEST_CASE("Radial LRMP forward sensitivity vs FD", "[RadLRMP],[Sensitivity],[Simulation],[failedFDtestLRMP]") // todo fix +// TEST_CASE("Radial LRMP forward sensitivity vs FD", "[RadLRMP],[Sensitivity],[Simulation],[failedFDtestLRMP]") // todo +// fix //{ // // todo comment on FD issue // // Relative error is checked first, we use high absolute error for letting @@ -77,51 +83,62 @@ TEST_CASE("Radial LRMP sensitivity Jacobians", "[RadLRMP],[UnitOp],[Sensitivity] // const double absTols[] = {6e5, 2e-2, 2e-2, 1.0}; // const double relTols[] = {5e-3, 1e-1, 5e-1, 6e-3}; // const double passRatio[] = {0.87, 0.84, 0.88, 0.95}; -// cadet::test::column::testFwdSensSolutionFD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", true, fdStepSize, absTols, relTols, passRatio); -//} +// cadet::test::column::testFwdSensSolutionFD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", true, fdStepSize, absTols, +// relTols, passRatio); +// } -//TEST_CASE("Radial LRMP forward sensitivity forward vs backward flow", "[RadLRMP],[Sensitivity],[Simulation],[fixLRMP]") // todo fix +// TEST_CASE("Radial LRMP forward sensitivity forward vs backward flow", +// "[RadLRMP],[Sensitivity],[Simulation],[fixLRMP]") // todo fix //{ // // todo why is there a pass ratio when we compare fwd and bwd flow? // const double absTols[] = {50.0, 2e-10, 1.0, 5e-7}; // const double relTols[] = {2e-4, 9e-6, 5e-7, 1e-7}; // const double passRatio[] = {1.0, 0.99, 0.98, 0.99}; // todo? works for 0.79, 0.99, 0.98, 0.99 -// cadet::test::column::testFwdSensSolutionForwardBackward("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", absTols, relTols, passRatio); -//} +// cadet::test::column::testFwdSensSolutionForwardBackward("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", absTols, relTols, +// passRatio); +// } TEST_CASE("Radial LRMP consistent initialization with linear binding", "[RadLRMP],[ConsistentInit],[ReleaseCI]") { - cadet::test::column::testConsistentInitializationLinearBinding("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", 1e-12, 1e-12); + cadet::test::column::testConsistentInitializationLinearBinding("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", 1e-12, + 1e-12); } -//TEST_CASE("Radial LRMP consistent initialization with SMA binding", "[RadLRMP],[ConsistentInit],[fixLRMP]") +// TEST_CASE("Radial LRMP consistent initialization with SMA binding", "[RadLRMP],[ConsistentInit],[fixLRMP]") //{ // std::vector y(4 + 4 * 16 + 16 * (4 + 4) + 4 * 16, 0.0); //// Optimal values: -//// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, +//// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, //// 1.0, 1.8, 1.5, 1.6, 856.173, 64.457, 5.73227, 2.85286}; -// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, +// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, // 1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0}; -// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 16); -// cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 16, 8); -// cadet::test::util::populate(y.data() + 4 + 4 * 16 + 16 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); +// cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 +//* 16); cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 16, 8); cadet::test::util::populate(y.data() +//+ 4 +//+ 4 * 16 + 16 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); // -// cadet::test::column::testConsistentInitializationSMABinding("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", y.data(), 1e-14, 1e-5); +// cadet::test::column::testConsistentInitializationSMABinding("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", y.data(), 1e-14, +// 1e-5); //} -TEST_CASE("Radial LRMP consistent sensitivity initialization with linear binding", "[RadLRMP],[ConsistentInit],[Sensitivity],[ReleaseCI]") +TEST_CASE("Radial LRMP consistent sensitivity initialization with linear binding", + "[RadLRMP],[ConsistentInit],[Sensitivity],[ReleaseCI]") { // Fill state vector with given initial values const unsigned int numDofs = 4 + 4 * 16 + 16 * (4 + 4) + 4 * 16; std::vector y(numDofs, 0.0); std::vector yDot(numDofs, 0.0); - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - cadet::test::column::testConsistentInitializationSensitivity("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", y.data(), yDot.data(), true, 1e-14); + cadet::test::column::testConsistentInitializationSensitivity("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", y.data(), + yDot.data(), true, 1e-14); } -TEST_CASE("Radial LRMP consistent sensitivity initialization with SMA binding", "[RadLRMP],[ConsistentInit],[Sensitivity],[ReleaseCI]") +TEST_CASE("Radial LRMP consistent sensitivity initialization with SMA binding", + "[RadLRMP],[ConsistentInit],[Sensitivity],[ReleaseCI]") { // Fill state vector with given initial values const unsigned int numDofs = 4 + 4 * 16 + 16 * (4 + 4) + 4 * 16; @@ -129,13 +146,18 @@ TEST_CASE("Radial LRMP consistent sensitivity initialization with SMA binding", std::vector yDot(numDofs, 0.0); const double bindingCell[] = {1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0}; - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 16); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 + 4 * 16); cadet::test::util::repeat(y.data() + 4 + 4 * 16, bindingCell, 8, 16); - cadet::test::util::populate(y.data() + 4 + 4 * 16 + 16 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4 * 16); + cadet::test::util::populate( + y.data() + 4 + 4 * 16 + 16 * (4 + 4), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, + 4 * 16); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - cadet::test::column::testConsistentInitializationSensitivity("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", y.data(), yDot.data(), false, 1e-10); + cadet::test::column::testConsistentInitializationSensitivity("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", y.data(), + yDot.data(), false, 1e-10); } TEST_CASE("Radial LRMP inlet DOF Jacobian", "[RadLRMP],[UnitOp],[Jacobian],[Inlet],[ReleaseCI]") @@ -145,103 +167,137 @@ TEST_CASE("Radial LRMP inlet DOF Jacobian", "[RadLRMP],[UnitOp],[Jacobian],[Inle TEST_CASE("Radial LRMP with two component linear binding Jacobian", "[RadLRMP],[UnitOp],[Jacobian],[ReleaseCI]") { - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV"); + cadet::JsonParameterProvider jpp = + createColumnWithTwoCompLinearBinding("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV"); cadet::test::column::testJacobianAD(jpp); } -TEST_CASE("Radial LRMP LWE one vs two identical particle types match", "[RadLRMP],[Simulation],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial LRMP LWE one vs two identical particle types match", + "[RadLRMP],[Simulation],[ParticleType],[ReleaseCI]") { - cadet::test::particle::testOneVsTwoIdenticalParticleTypes("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", 2.2e-8, 6e-5); + cadet::test::particle::testOneVsTwoIdenticalParticleTypes("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", 2.2e-8, + 6e-5); } -TEST_CASE("Radial LRMP LWE separate identical particle types match", "[RadLRMP],[Simulation],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial LRMP LWE separate identical particle types match", + "[RadLRMP],[Simulation],[ParticleType],[ReleaseCI]") { - cadet::test::particle::testSeparateIdenticalParticleTypes("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", 1e-15, 1e-15); + cadet::test::particle::testSeparateIdenticalParticleTypes("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", 1e-15, + 1e-15); } -TEST_CASE("Radial LRMP linear binding single particle matches particle distribution", "[RadLRMP],[Simulation],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial LRMP linear binding single particle matches particle distribution", + "[RadLRMP],[Simulation],[ParticleType],[ReleaseCI]") { cadet::test::particle::testLinearMixedParticleTypes("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", 5e-8, 5e-5); } -TEST_CASE("Radial LRMP multiple particle types Jacobian analytic vs AD", "[RadLRMP],[Jacobian],[AD],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial LRMP multiple particle types Jacobian analytic vs AD", + "[RadLRMP],[Jacobian],[AD],[ParticleType],[ReleaseCI]") { cadet::test::particle::testJacobianMixedParticleTypes("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV"); } -TEST_CASE("Radial LRMP multiple particle types time derivative Jacobian vs FD", "[RadLRMP],[UnitOp],[Residual],[Jacobian],[ParticleType],[ReleaseCI],[FD]") +TEST_CASE("Radial LRMP multiple particle types time derivative Jacobian vs FD", + "[RadLRMP],[UnitOp],[Residual],[Jacobian],[ParticleType],[ReleaseCI],[FD]") { - cadet::test::particle::testTimeDerivativeJacobianMixedParticleTypesFD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", 1e-6, 0.0, 9e-4); + cadet::test::particle::testTimeDerivativeJacobianMixedParticleTypesFD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", + 1e-6, 0.0, 9e-4); } -TEST_CASE("Radial LRMP multiple spatially dependent particle types Jacobian analytic vs AD", "[RadLRMP],[Jacobian],[AD],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial LRMP multiple spatially dependent particle types Jacobian analytic vs AD", + "[RadLRMP],[Jacobian],[AD],[ParticleType],[ReleaseCI]") { cadet::test::particle::testJacobianSpatiallyMixedParticleTypes("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV"); } -TEST_CASE("Radial LRMP linear binding single particle matches spatially dependent particle distribution", "[RadLRMP],[Simulation],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial LRMP linear binding single particle matches spatially dependent particle distribution", + "[RadLRMP],[Simulation],[ParticleType],[ReleaseCI]") { - cadet::test::particle::testLinearSpatiallyMixedParticleTypes("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", 5e-8, 5e-5); + cadet::test::particle::testLinearSpatiallyMixedParticleTypes("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", 5e-8, + 5e-5); } -TEST_CASE("Radial LRMP multiple spatially dependent particle types flux Jacobian vs FD", "[RadLRMP],[UnitOp],[Residual],[Jacobian],[ParticleType],[ReleaseCI],[FD]") +TEST_CASE("Radial LRMP multiple spatially dependent particle types flux Jacobian vs FD", + "[RadLRMP],[UnitOp],[Residual],[Jacobian],[ParticleType],[ReleaseCI],[FD]") { - cadet::test::particle::testArrowHeadJacobianSpatiallyMixedParticleTypes("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", 1e-6, 1e-8, 1e-5); + cadet::test::particle::testArrowHeadJacobianSpatiallyMixedParticleTypes("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", 1e-6, + 1e-8, 1e-5); } TEST_CASE("Radial LRMP dynamic reactions Jacobian vs AD bulk", "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", true, false, false); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", true, false, + false); } -TEST_CASE("Radial LRMP dynamic reactions Jacobian vs AD particle", "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") +TEST_CASE("Radial LRMP dynamic reactions Jacobian vs AD particle", + "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", false, true, false); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", false, true, + false); } -TEST_CASE("Radial LRMP dynamic reactions Jacobian vs AD modified particle", "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") +TEST_CASE("Radial LRMP dynamic reactions Jacobian vs AD modified particle", + "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", false, true, true); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", false, true, + true); } -TEST_CASE("Radial LRMP dynamic reactions Jacobian vs AD bulk and particle", "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") +TEST_CASE("Radial LRMP dynamic reactions Jacobian vs AD bulk and particle", + "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", true, true, false); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", true, true, + false); } -TEST_CASE("Radial LRMP dynamic reactions Jacobian vs AD bulk and modified particle", "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") +TEST_CASE("Radial LRMP dynamic reactions Jacobian vs AD bulk and modified particle", + "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", true, true, true); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", true, true, + true); } -TEST_CASE("Radial LRMP dynamic reactions time derivative Jacobian vs FD bulk", "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") +TEST_CASE("Radial LRMP dynamic reactions time derivative Jacobian vs FD bulk", + "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", true, false, false, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", + true, false, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("Radial LRMP dynamic reactions time derivative Jacobian vs FD particle", "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") +TEST_CASE("Radial LRMP dynamic reactions time derivative Jacobian vs FD particle", + "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", false, true, false, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", + false, true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("Radial LRMP dynamic reactions time derivative Jacobian vs FD modified particle", "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") +TEST_CASE("Radial LRMP dynamic reactions time derivative Jacobian vs FD modified particle", + "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", false, true, true, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", + false, true, true, 1e-6, 1e-14, 8e-4); } -TEST_CASE("Radial LRMP dynamic reactions time derivative Jacobian vs FD bulk and particle", "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") +TEST_CASE("Radial LRMP dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", true, true, false, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", + true, true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("Radial LRMP dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") +TEST_CASE("Radial LRMP dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", true, true, true, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV", + true, true, true, 1e-6, 1e-14, 8e-4); } inline cadet::JsonParameterProvider createColumnWithTwoCompLinearBindingThreeParticleTypes() { - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV"); + cadet::JsonParameterProvider jpp = + createColumnWithTwoCompLinearBinding("RADIAL_LUMPED_RATE_MODEL_WITH_PORES", "FV"); const double parVolFrac[] = {0.3, 0.6, 0.1}; const double parFactor[] = {0.9, 0.8}; @@ -250,61 +306,72 @@ inline cadet::JsonParameterProvider createColumnWithTwoCompLinearBindingThreePar return jpp; } -TEST_CASE("Radial LRMP multi particle types dynamic reactions Jacobian vs AD bulk", "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial LRMP multi particle types dynamic reactions Jacobian vs AD bulk", + "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, false, false); } -TEST_CASE("Radial LRMP multi particle types dynamic reactions Jacobian vs AD particle", "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial LRMP multi particle types dynamic reactions Jacobian vs AD particle", + "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, false, true, false); } -TEST_CASE("Radial LRMP multi particle types dynamic reactions Jacobian vs AD modified particle", "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial LRMP multi particle types dynamic reactions Jacobian vs AD modified particle", + "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, false, true, true); } -TEST_CASE("Radial LRMP multi particle types dynamic reactions Jacobian vs AD bulk and particle", "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial LRMP multi particle types dynamic reactions Jacobian vs AD bulk and particle", + "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, true, false); } -TEST_CASE("Radial LRMP multi particle types dynamic reactions Jacobian vs AD bulk and modified particle", "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") +TEST_CASE("Radial LRMP multi particle types dynamic reactions Jacobian vs AD bulk and modified particle", + "[RadLRMP],[Jacobian],[AD],[ReactionModel],[ParticleType],[ReleaseCI]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testUnitJacobianDynamicReactionsAD(jpp, true, true, true); } -TEST_CASE("Radial LRMP multi particle types dynamic reactions time derivative Jacobian vs FD bulk", "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") +TEST_CASE("Radial LRMP multi particle types dynamic reactions time derivative Jacobian vs FD bulk", + "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, false, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("Radial LRMP multi particle types dynamic reactions time derivative Jacobian vs FD particle", "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") +TEST_CASE("Radial LRMP multi particle types dynamic reactions time derivative Jacobian vs FD particle", + "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("Radial LRMP multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") +TEST_CASE("Radial LRMP multi particle types dynamic reactions time derivative Jacobian vs FD modified particle", + "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, false, true, true, 1e-6, 1e-14, 8e-4); } -TEST_CASE("Radial LRMP multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") +TEST_CASE("Radial LRMP multi particle types dynamic reactions time derivative Jacobian vs FD bulk and particle", + "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("Radial LRMP multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") +TEST_CASE( + "Radial LRMP multi particle types dynamic reactions time derivative Jacobian vs FD bulk and modified particle", + "[RadLRMP],[Jacobian],[Residual],[ReactionModel],[ParticleType],[ReleaseCI],[FD]") { cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBindingThreeParticleTypes(); cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD(jpp, true, true, true, 1e-6, 1e-14, 8e-4); diff --git a/test/RadialLumpedRateModelWithoutPores.cpp b/test/RadialLumpedRateModelWithoutPores.cpp index 792ff5cda..2e1a0d2ba 100644 --- a/test/RadialLumpedRateModelWithoutPores.cpp +++ b/test/RadialLumpedRateModelWithoutPores.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -25,23 +25,27 @@ // todo add (more) numerical reference (and EOC) tests -TEST_CASE("Radial LRM numerical Benchmark with parameter sensitivities for linear case", "[RadLRM],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on server +TEST_CASE( + "Radial LRM numerical Benchmark with parameter sensitivities for linear case", + "[RadLRM],[Simulation],[Reference],[Sensitivity]") // todo CI flag: currently only runs locally but fails on server { const std::string& modelFilePath = std::string("/data/model_radLRM_dynLin_1comp_sensbenchmark1.json"); const std::string& refFilePath = std::string("/data/ref_radLRM_dynLin_1comp_sensbenchmark1_FV_Z32.h5"); - const std::vector absTol = { 1e-12, 1e-12, 1e-12, 1e-12 }; - const std::vector relTol = { 1e-5, 1e-6, 1e-6, 1e-6 }; + const std::vector absTol = {1e-12, 1e-12, 1e-12, 1e-12}; + const std::vector relTol = {1e-5, 1e-6, 1e-6, 1e-6}; cadet::test::column::FVparams disc(32); cadet::test::column::testReferenceBenchmark(modelFilePath, refFilePath, "001", absTol, relTol, disc, false); } TEST_CASE("Radial LRM transport Jacobian", "[RadLRM],[UnitOp],[Jacobian],[CI]") { - cadet::JsonParameterProvider jpp = createColumnLinearBenchmark(false, true, "RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV"); + cadet::JsonParameterProvider jpp = + createColumnLinearBenchmark(false, true, "RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV"); cadet::test::column::testJacobianAD(jpp); } -// NOTE: THE FOLLOWING TESTS ARE ONLY INCLUDED IN THE RELEASE CI, NOT THE STANDARD CI SINCE THEY ARE (TO A HIGH DEGREE) REDUNDANT WITH THE AXIAL FLOW TESTS +// NOTE: THE FOLLOWING TESTS ARE ONLY INCLUDED IN THE RELEASE CI, NOT THE STANDARD CI SINCE THEY ARE (TO A HIGH DEGREE) +// REDUNDANT WITH THE AXIAL FLOW TESTS TEST_CASE("Radial LRM Jacobian forward vs backward flow", "[RadLRM],[UnitOp],[Residual],[Jacobian],[AD],[ReleaseCI]") { @@ -56,12 +60,14 @@ TEST_CASE("Radial LRM time derivative Jacobian vs FD", "[RadLRM],[UnitOp],[Resid TEST_CASE("Radial LRM sensitivity Jacobians", "[RadLRM],[UnitOp],[Sensitivity],[ReleaseCI]") { - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV"); + cadet::JsonParameterProvider jpp = + createColumnWithTwoCompLinearBinding("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV"); cadet::test::column::testFwdSensJacobians(jpp, 1e-4, 3e-7, 5e-5); } -//TEST_CASE("Radial LRM forward sensitivity vs FD", "[RadLRM],[Sensitivity],[Simulation],[failedFDtestLRM],[FD]") // todo (for all models) find tolerances +// TEST_CASE("Radial LRM forward sensitivity vs FD", "[RadLRM],[Sensitivity],[Simulation],[failedFDtestLRM],[FD]") // +// todo (for all models) find tolerances //{ // // Relative error is checked first, we use high absolute error for letting // // some points that are far off pass the error test, too. This is required @@ -70,49 +76,60 @@ TEST_CASE("Radial LRM sensitivity Jacobians", "[RadLRM],[UnitOp],[Sensitivity],[ // const double absTols[] = {2e8, 8e-3, 2e-2, 3e-1}; // const double relTols[] = {1e-1, 5e-1, 5e-2, 1e-2}; // const double passRatio[] = {0.88, 0.84, 0.73, 0.87}; -// cadet::test::column::testFwdSensSolutionFD("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", false, fdStepSize, absTols, relTols, passRatio); -//} +// cadet::test::column::testFwdSensSolutionFD("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", false, fdStepSize, absTols, +// relTols, passRatio); +// } // -//TEST_CASE("Radial LRM forward sensitivity forward vs backward flow", "[RadLRM],[Sensitivity],[Simulation],[fixLRM]") // todo (for all models) find tolerances? why is there a pass ratio here, shouldnt this be precise? +// TEST_CASE("Radial LRM forward sensitivity forward vs backward flow", "[RadLRM],[Sensitivity],[Simulation],[fixLRM]") +// // todo (for all models) find tolerances? why is there a pass ratio here, shouldnt this be precise? //{ // const double absTols[] = {500.0, 8e-7, 9e-7, 2e-3}; // const double relTols[] = {7e-3, 5e-5, 5e-5, 9e-4}; // const double passRatio[] = {0.99, 0.97, 0.97, 0.98}; -// cadet::test::column::testFwdSensSolutionForwardBackward("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", absTols, relTols, passRatio); -//} +// cadet::test::column::testFwdSensSolutionForwardBackward("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", absTols, relTols, +// passRatio); +// } TEST_CASE("Radial LRM consistent initialization with linear binding", "[RadLRM],[ConsistentInit],[ReleaseCI]") { - cadet::test::column::testConsistentInitializationLinearBinding("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", 1e-12, 1e-12); + cadet::test::column::testConsistentInitializationLinearBinding("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", + 1e-12, 1e-12); } -//TEST_CASE("Radial LRM consistent initialization with SMA binding", "[RadLRM],[ConsistentInit],[fixLRM]") // todo (for all models) fix +// TEST_CASE("Radial LRM consistent initialization with SMA binding", "[RadLRM],[ConsistentInit],[fixLRM]") // todo (for +// all models) fix //{ // std::vector y(4 + 16 * (4 + 4), 0.0); // // Optimal values: -// // const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, +// // const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 858.034, 66.7896, 3.53273, 2.53153, // // 1.0, 1.8, 1.5, 1.6, 856.173, 64.457, 5.73227, 2.85286}; -// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, +// const double bindingCell[] = {1.2, 2.0, 1.0, 1.5, 840.0, 63.0, 3.0, 3.0, // 1.0, 1.8, 1.5, 1.6, 840.0, 63.0, 6.0, 3.0}; // cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4); // cadet::test::util::repeat(y.data() + 4, bindingCell, 16, 8); // -// cadet::test::column::testConsistentInitializationSMABinding("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", y.data(), 1e-14, 1e-5); -//} +// cadet::test::column::testConsistentInitializationSMABinding("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", y.data(), +// 1e-14, 1e-5); +// } -TEST_CASE("Radial LRM consistent sensitivity initialization with linear binding", "[RadLRM],[ConsistentInit],[Sensitivity],[ReleaseCI]") +TEST_CASE("Radial LRM consistent sensitivity initialization with linear binding", + "[RadLRM],[ConsistentInit],[Sensitivity],[ReleaseCI]") { // Fill state vector with given initial values const unsigned int numDofs = 4 + 16 * (4 + 4); std::vector y(numDofs, 0.0); std::vector yDot(numDofs, 0.0); - cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - cadet::test::column::testConsistentInitializationSensitivity("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", y.data(), yDot.data(), true, 1e-12); + cadet::test::column::testConsistentInitializationSensitivity("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", + y.data(), yDot.data(), true, 1e-12); } -TEST_CASE("Radial LRM consistent sensitivity initialization with SMA binding", "[RadLRM],[ConsistentInit],[Sensitivity],[ReleaseCI]") +TEST_CASE("Radial LRM consistent sensitivity initialization with SMA binding", + "[RadLRM],[ConsistentInit],[Sensitivity],[ReleaseCI]") { // Fill state vector with given initial values const unsigned int numDofs = 4 + 16 * (4 + 4); @@ -123,9 +140,11 @@ TEST_CASE("Radial LRM consistent sensitivity initialization with SMA binding", " cadet::test::util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, 4); cadet::test::util::repeat(y.data() + 4, bindingCell, 8, 16); - cadet::test::util::populate(yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); + cadet::test::util::populate( + yDot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, numDofs); - cadet::test::column::testConsistentInitializationSensitivity("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", y.data(), yDot.data(), false, 1e-9); + cadet::test::column::testConsistentInitializationSensitivity("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", + y.data(), yDot.data(), false, 1e-9); } TEST_CASE("Radial LRM inlet DOF Jacobian", "[RadLRM],[UnitOp],[Jacobian],[Inlet],[ReleaseCI]") @@ -135,26 +154,34 @@ TEST_CASE("Radial LRM inlet DOF Jacobian", "[RadLRM],[UnitOp],[Jacobian],[Inlet] TEST_CASE("Radial LRM with two component linear binding Jacobian", "[RadLRM],[UnitOp],[Jacobian],[ReleaseCI]") { - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV"); + cadet::JsonParameterProvider jpp = + createColumnWithTwoCompLinearBinding("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV"); cadet::test::column::testJacobianAD(jpp); } TEST_CASE("Radial LRM dynamic reactions Jacobian vs AD bulk", "[RadLRM],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", true, false, false); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", true, + false, false); } -TEST_CASE("Radial LRM dynamic reactions Jacobian vs AD modified bulk", "[RadLRM],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") +TEST_CASE("Radial LRM dynamic reactions Jacobian vs AD modified bulk", + "[RadLRM],[Jacobian],[AD],[ReactionModel],[ReleaseCI]") { - cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", true, false, true); + cadet::test::reaction::testUnitJacobianDynamicReactionsAD("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", true, + false, true); } -TEST_CASE("Radial LRM dynamic reactions time derivative Jacobian vs FD bulk", "[RadLRM],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") +TEST_CASE("Radial LRM dynamic reactions time derivative Jacobian vs FD bulk", + "[RadLRM],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", true, false, false, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", + true, false, false, 1e-6, 1e-14, 8e-4); } -TEST_CASE("Radial LRM dynamic reactions time derivative Jacobian vs FD modified bulk", "[RadLRM],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") +TEST_CASE("Radial LRM dynamic reactions time derivative Jacobian vs FD modified bulk", + "[RadLRM],[Jacobian],[Residual],[ReactionModel],[ReleaseCI],[FD]") { - cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", true, false, true, 1e-6, 1e-14, 8e-4); + cadet::test::reaction::testTimeDerivativeJacobianDynamicReactionsFD("RADIAL_LUMPED_RATE_MODEL_WITHOUT_PORES", "FV", + true, false, true, 1e-6, 1e-14, 8e-4); } diff --git a/test/ReactionModelTests.cpp b/test/ReactionModelTests.cpp index 34ac61c93..d59ab93be 100644 --- a/test/ReactionModelTests.cpp +++ b/test/ReactionModelTests.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -48,35 +48,63 @@ const char* getTestDirectory(); namespace { - inline cadet::model::IDynamicReactionModel* createDynamicReactionModel(const char* name) +inline cadet::model::IDynamicReactionModel* createDynamicReactionModel(const char* name) +{ + cadet::ReactionModelFactory rmf; + cadet::model::IDynamicReactionModel* const rm = rmf.createDynamic(name); + + REQUIRE(nullptr != rm); + return rm; +} + +class ConstExternalFunction : public cadet::IExternalFunction +{ +public: + virtual bool configure(cadet::IParameterProvider* paramProvider) + { + return true; + } + virtual const char* name() const CADET_NOEXCEPT + { + return "CONSTFUN"; + } + virtual double externalProfile(double t, double z, double rho, double r, unsigned int sec) + { + return 1.0; + } + virtual double timeDerivative(double t, double z, double rho, double r, unsigned int sec) + { + return 0.0; + } + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { - cadet::ReactionModelFactory rmf; - cadet::model::IDynamicReactionModel* const rm = rmf.createDynamic(name); - - REQUIRE(nullptr != rm); - return rm; } +}; - class ConstExternalFunction : public cadet::IExternalFunction +class LinearExternalFunction : public cadet::IExternalFunction +{ +public: + virtual bool configure(cadet::IParameterProvider* paramProvider) + { + return true; + } + virtual const char* name() const CADET_NOEXCEPT { - public: - virtual bool configure(cadet::IParameterProvider* paramProvider) { return true; } - virtual const char* name() const CADET_NOEXCEPT { return "CONSTFUN"; } - virtual double externalProfile(double t, double z, double rho, double r, unsigned int sec) { return 1.0; } - virtual double timeDerivative(double t, double z, double rho, double r, unsigned int sec) { return 0.0; } - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { } - }; - - class LinearExternalFunction : public cadet::IExternalFunction + return "LINFUN"; + } + virtual double externalProfile(double t, double z, double rho, double r, unsigned int sec) { - public: - virtual bool configure(cadet::IParameterProvider* paramProvider) { return true; } - virtual const char* name() const CADET_NOEXCEPT { return "LINFUN"; } - virtual double externalProfile(double t, double z, double rho, double r, unsigned int sec) { return t; } - virtual double timeDerivative(double t, double z, double rho, double r, unsigned int sec) { return 1.0; } - virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) { } - }; -} + return t; + } + virtual double timeDerivative(double t, double z, double rho, double r, unsigned int sec) + { + return 1.0; + } + virtual void setSectionTimes(double const* secTimes, bool const* secContinuity, unsigned int nSections) + { + } +}; +} // namespace namespace cadet { @@ -87,397 +115,427 @@ namespace test namespace reaction { - ConfiguredDynamicReactionModel::~ConfiguredDynamicReactionModel() - { - ::operator delete(_bufferMemory); - - delete[] _extFuns; - delete[] _boundOffset; - delete _reaction; - } +ConfiguredDynamicReactionModel::~ConfiguredDynamicReactionModel() +{ + ::operator delete(_bufferMemory); - ConfiguredDynamicReactionModel ConfiguredDynamicReactionModel::create(const char* name, unsigned int nComp, unsigned int const* nBound, const char* config) - { - cadet::model::IDynamicReactionModel* const rm = createDynamicReactionModel(name); + delete[] _extFuns; + delete[] _boundOffset; + delete _reaction; +} - // Calculate offset of bound states - unsigned int* boundOffset = new unsigned int[nComp]; - boundOffset[0] = 0; - for (unsigned int i = 1; i < nComp; ++i) - { - boundOffset[i] = boundOffset[i-1] + nBound[i-1]; - } - const unsigned int totalBoundStates = boundOffset[nComp - 1] + nBound[nComp - 1]; +ConfiguredDynamicReactionModel ConfiguredDynamicReactionModel::create(const char* name, unsigned int nComp, + unsigned int const* nBound, const char* config) +{ + cadet::model::IDynamicReactionModel* const rm = createDynamicReactionModel(name); - // Configure - cadet::JsonParameterProvider jpp(config); - rm->configureModelDiscretization(jpp, nComp, nBound, boundOffset); - if (rm->requiresConfiguration()) - { - jpp.set("EXTFUN", std::vector(1, 0)); - REQUIRE(rm->configure(jpp, 0, 0)); - } + // Calculate offset of bound states + unsigned int* boundOffset = new unsigned int[nComp]; + boundOffset[0] = 0; + for (unsigned int i = 1; i < nComp; ++i) + { + boundOffset[i] = boundOffset[i - 1] + nBound[i - 1]; + } + const unsigned int totalBoundStates = boundOffset[nComp - 1] + nBound[nComp - 1]; - // Assign external functions - cadet::IExternalFunction* extFuns = new LinearExternalFunction[50]; - rm->setExternalFunctions(&extFuns, 50); + // Configure + cadet::JsonParameterProvider jpp(config); + rm->configureModelDiscretization(jpp, nComp, nBound, boundOffset); + if (rm->requiresConfiguration()) + { + jpp.set("EXTFUN", std::vector(1, 0)); + REQUIRE(rm->configure(jpp, 0, 0)); + } - // Allocate memory buffer - unsigned int requiredMem = 0; - if (rm->requiresWorkspace()) - requiredMem = rm->workspaceSize(nComp, totalBoundStates, boundOffset); + // Assign external functions + cadet::IExternalFunction* extFuns = new LinearExternalFunction[50]; + rm->setExternalFunctions(&extFuns, 50); - void* buffer = nullptr; - void* bufferEnd = nullptr; - if (requiredMem > 0) - { - buffer = ::operator new(requiredMem); - bufferEnd = static_cast(buffer) + requiredMem; - std::memset(buffer, 0, requiredMem); - } + // Allocate memory buffer + unsigned int requiredMem = 0; + if (rm->requiresWorkspace()) + requiredMem = rm->workspaceSize(nComp, totalBoundStates, boundOffset); - return ConfiguredDynamicReactionModel(rm, nComp, nBound, boundOffset, buffer, bufferEnd, extFuns); + void* buffer = nullptr; + void* bufferEnd = nullptr; + if (requiredMem > 0) + { + buffer = ::operator new(requiredMem); + bufferEnd = static_cast(buffer) + requiredMem; + std::memset(buffer, 0, requiredMem); } - void ConfiguredDynamicReactionModel::increaseBufferSize(int inc) - { - const int bufSize = requiredBufferSize() + inc; + return ConfiguredDynamicReactionModel(rm, nComp, nBound, boundOffset, buffer, bufferEnd, extFuns); +} + +void ConfiguredDynamicReactionModel::increaseBufferSize(int inc) +{ + const int bufSize = requiredBufferSize() + inc; - ::operator delete(_bufferMemory); - _bufferMemory = ::operator new(bufSize); - std::memset(_bufferMemory, 0, bufSize); + ::operator delete(_bufferMemory); + _bufferMemory = ::operator new(bufSize); + std::memset(_bufferMemory, 0, bufSize); #ifdef CADET_DEBUG - _buffer.setBuffer(_bufferMemory, static_cast(_bufferMemory) + bufSize); + _buffer.setBuffer(_bufferMemory, static_cast(_bufferMemory) + bufSize); #else - _buffer.setBuffer(_bufferMemory); + _buffer.setBuffer(_bufferMemory); #endif - } +} - int ConfiguredDynamicReactionModel::requiredBufferSize() CADET_NOEXCEPT +int ConfiguredDynamicReactionModel::requiredBufferSize() CADET_NOEXCEPT +{ + if (_reaction->requiresWorkspace()) { - if (_reaction->requiresWorkspace()) - { - unsigned int totalBoundStates = 0; - for (unsigned int i = 0; i < _nComp; ++i) - totalBoundStates += _nBound[i]; - - return _reaction->workspaceSize(_nComp, totalBoundStates, _boundOffset); - } + unsigned int totalBoundStates = 0; + for (unsigned int i = 0; i < _nComp; ++i) + totalBoundStates += _nBound[i]; - return 0; + return _reaction->workspaceSize(_nComp, totalBoundStates, _boundOffset); } - void extendModelWithDynamicReactions(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, bool bulk, bool particle, bool particleModifiers) - { - auto gs = util::makeModelGroupScope(jpp, unit); - const int nComp = jpp.getInt("NCOMP"); - std::vector nBound(0); - if (jpp.exists("NBOUND")) - nBound = jpp.getIntArray("NBOUND"); - - int nParType = 0; - { - auto ds = cadet::test::util::makeOptionalGroupScope(jpp, "discretization"); + return 0; +} - // Try to detect number of particle types - if (jpp.exists("NPAR")) - nParType = jpp.numElements("NPAR"); - - if (jpp.exists("NPARTYPE")) - { - nParType = jpp.getInt("NPARTYPE"); - } - else - nParType = nBound.size() / nComp; - } +void extendModelWithDynamicReactions(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, bool bulk, bool particle, + bool particleModifiers) +{ + auto gs = util::makeModelGroupScope(jpp, unit); + const int nComp = jpp.getInt("NCOMP"); + std::vector nBound(0); + if (jpp.exists("NBOUND")) + nBound = jpp.getIntArray("NBOUND"); - const std::string uoType = jpp.getString("UNIT_TYPE"); - const bool isLRMP = (uoType == "LUMPED_RATE_MODEL_WITHOUT_PORES"); - const int nTotalBound = std::accumulate(nBound.begin(), nBound.end(), 0); + int nParType = 0; + { + auto ds = cadet::test::util::makeOptionalGroupScope(jpp, "discretization"); - if (!isLRMP && bulk) - { - char const* const scopeName = "reaction_bulk"; - jpp.addScope(scopeName); - auto gs2 = util::makeGroupScope(jpp, scopeName); - - const int nReactions = 4; - - std::vector stoichMat(nReactions * nComp, 0.0); - std::vector expFwd(nReactions * nComp, 0.0); - std::vector expBwd(nReactions * nComp, 0.0); - std::vector rateFwd(nReactions, 0.0); - std::vector rateBwd(nReactions, 0.0); - - util::populate(stoichMat.data(), [](unsigned int idx) { return 2.0 * std::sin(0.5 + idx * 0.7); }, stoichMat.size()); - util::populate(expFwd.data(), [](unsigned int idx) { return std::sin(0.1 + idx * 0.7) + 1.4; }, expFwd.size()); - util::populate(expBwd.data(), [](unsigned int idx) { return std::sin(0.2 + idx * 0.3) + 1.1; }, expBwd.size()); - util::populate(rateFwd.data(), [](unsigned int idx) { return std::sin(0.3 + idx * 0.2) * 0.5 + 1.5; }, rateFwd.size()); - util::populate(rateBwd.data(), [](unsigned int idx) { return std::sin(0.4 + idx * 0.4) * 0.5 + 1.6; }, rateBwd.size()); - - jpp.set("MAL_STOICHIOMETRY_BULK", stoichMat); - jpp.set("MAL_EXPONENTS_BULK_FWD", expFwd); - jpp.set("MAL_EXPONENTS_BULK_BWD", expBwd); - jpp.set("MAL_KFWD_BULK", rateFwd); - jpp.set("MAL_KBWD_BULK", rateBwd); - } + // Try to detect number of particle types + if (jpp.exists("NPAR")) + nParType = jpp.numElements("NPAR"); - if ((!isLRMP && particle) || (isLRMP && bulk)) + if (jpp.exists("NPARTYPE")) { - const int nReactions = 3; - - for (int i = 0; i < nParType; ++i) - { - std::ostringstream oss; - if (isLRMP) - oss << "reaction"; - else - oss << "reaction_particle_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << i; - - jpp.addScope(oss.str()); - auto gs2 = util::makeGroupScope(jpp, oss.str()); - - std::vector stoichMatLiquid(nReactions * nComp, 0.0); - std::vector expFwdLiquid(nReactions * nComp, 0.0); - std::vector expBwdLiquid(nReactions * nComp, 0.0); - std::vector rateFwdLiquid(nReactions, 0.0); - std::vector rateBwdLiquid(nReactions, 0.0); - - std::vector stoichMatSolid(nReactions * nTotalBound, 0.0); - std::vector expFwdSolid(nReactions * nTotalBound, 0.0); - std::vector expBwdSolid(nReactions * nTotalBound, 0.0); - std::vector rateFwdSolid(nReactions, 0.0); - std::vector rateBwdSolid(nReactions, 0.0); - - util::populate(stoichMatLiquid.data(), [](unsigned int idx) { return 2.0 * std::sin(0.5 + idx * 0.7); }, stoichMatLiquid.size()); - util::populate(expFwdLiquid.data(), [](unsigned int idx) { return std::sin(0.1 + idx * 0.7) + 1.4; }, expFwdLiquid.size()); - util::populate(expBwdLiquid.data(), [](unsigned int idx) { return std::sin(0.2 + idx * 0.3) + 1.1; }, expBwdLiquid.size()); - util::populate(rateFwdLiquid.data(), [](unsigned int idx) { return std::sin(0.3 + idx * 0.2) * 0.5 + 1.5; }, rateFwdLiquid.size()); - util::populate(rateBwdLiquid.data(), [](unsigned int idx) { return std::sin(0.4 + idx * 0.4) * 0.5 + 1.6; }, rateBwdLiquid.size()); - - util::populate(stoichMatSolid.data(), [](unsigned int idx) { return 2.0 * std::sin(0.3 + idx * 0.9); }, stoichMatSolid.size()); - util::populate(expFwdSolid.data(), [](unsigned int idx) { return std::sin(0.2 + idx * 0.6) + 1.3; }, expFwdSolid.size()); - util::populate(expBwdSolid.data(), [](unsigned int idx) { return std::sin(0.1 + idx * 0.4) + 1.2; }, expBwdSolid.size()); - util::populate(rateFwdSolid.data(), [](unsigned int idx) { return std::sin(0.5 + idx * 0.4) * 0.5 + 1.6; }, rateFwdSolid.size()); - util::populate(rateBwdSolid.data(), [](unsigned int idx) { return std::sin(0.2 + idx * 0.3) * 0.5 + 1.5; }, rateBwdSolid.size()); - - jpp.set("MAL_STOICHIOMETRY_LIQUID", stoichMatLiquid); - jpp.set("MAL_EXPONENTS_LIQUID_FWD", expFwdLiquid); - jpp.set("MAL_EXPONENTS_LIQUID_BWD", expBwdLiquid); - jpp.set("MAL_KFWD_LIQUID", rateFwdLiquid); - jpp.set("MAL_KBWD_LIQUID", rateBwdLiquid); - - jpp.set("MAL_STOICHIOMETRY_SOLID", stoichMatSolid); - jpp.set("MAL_EXPONENTS_SOLID_FWD", expFwdSolid); - jpp.set("MAL_EXPONENTS_SOLID_BWD", expBwdSolid); - jpp.set("MAL_KFWD_SOLID", rateFwdSolid); - jpp.set("MAL_KBWD_SOLID", rateBwdSolid); - - if (particleModifiers) - { - std::vector expFwdLiquidModSolid(nReactions * nTotalBound, 0.0); - std::vector expBwdLiquidModSolid(nReactions * nTotalBound, 0.0); - std::vector expFwdSolidModLiquid(nReactions * nComp, 0.0); - std::vector expBwdSolidModLiquid(nReactions * nComp, 0.0); - - util::populate(expFwdLiquidModSolid.data(), [](unsigned int idx) { return std::sin(idx * 0.7 + 0.1) * 0.5 + 1.6; }, expFwdLiquidModSolid.size()); - util::populate(expBwdLiquidModSolid.data(), [](unsigned int idx) { return std::sin(idx * 0.7 + 0.2) * 0.5 + 1.6; }, expBwdLiquidModSolid.size()); - util::populate(expFwdSolidModLiquid.data(), [](unsigned int idx) { return std::sin(idx * 0.7 + 0.3) * 0.5 + 1.6; }, expFwdSolidModLiquid.size()); - util::populate(expBwdSolidModLiquid.data(), [](unsigned int idx) { return std::sin(idx * 0.7 + 0.4) * 0.5 + 1.6; }, expBwdSolidModLiquid.size()); - - jpp.set("MAL_EXPONENTS_LIQUID_FWD_MODSOLID", expFwdLiquidModSolid); - jpp.set("MAL_EXPONENTS_LIQUID_BWD_MODSOLID", expBwdLiquidModSolid); - jpp.set("MAL_EXPONENTS_SOLID_FWD_MODLIQUID", expFwdSolidModLiquid); - jpp.set("MAL_EXPONENTS_SOLID_BWD_MODLIQUID", expBwdSolidModLiquid); - } - } + nParType = jpp.getInt("NPARTYPE"); } + else + nParType = nBound.size() / nComp; } - void testMichaelisMentenToSMAMicroKinetic(const std::string configFilePathMM, const std::string configFilePathSMA, const double absTol, const double relTol) + const std::string uoType = jpp.getString("UNIT_TYPE"); + const bool isLRMP = (uoType == "LUMPED_RATE_MODEL_WITHOUT_PORES"); + const int nTotalBound = std::accumulate(nBound.begin(), nBound.end(), 0); + + if (!isLRMP && bulk) { - // read json model setup file - const std::string setupFileMM = std::string(getTestDirectory()) + configFilePathMM; - const std::string setupFileSMA = std::string(getTestDirectory()) + configFilePathSMA; - JsonParameterProvider pp_setup_MM(JsonParameterProvider::fromFile(setupFileMM)); - JsonParameterProvider pp_setup_SMA(JsonParameterProvider::fromFile(setupFileSMA)); - - nlohmann::json* setupJsonMM = pp_setup_MM.data(); - nlohmann::json* setupJsonSMA = pp_setup_SMA.data(); - - // MM simulation - cadet::Driver drvMM; - drvMM.configure(pp_setup_MM); - drvMM.run(); - - // SMA micro-kinetics simulation - cadet::Driver drvSMA; - drvSMA.configure(pp_setup_SMA); - drvSMA.run(); - - cadet::InternalStorageUnitOpRecorder const* const MMData = drvMM.solution()->unitOperation(0); - cadet::InternalStorageUnitOpRecorder const* const SMAData = drvSMA.solution()->unitOperation(0); - - double const* outletMM = MMData->outlet(); - double const* outletSMA = SMAData->outlet(); - - const unsigned int nCompMM = MMData->numComponents(); - const unsigned int nCompSMA = SMAData->numComponents(); - for (unsigned int i = 0; i < SMAData->numDataPoints(); ++i, outletMM += nCompMM, outletSMA += nCompSMA) - { - CAPTURE(i); - CHECK((outletMM[0]) == cadet::test::makeApprox(outletSMA[1], relTol, absTol)); - CHECK((outletMM[1]) == cadet::test::makeApprox(outletSMA[3], relTol, absTol)); - } + char const* const scopeName = "reaction_bulk"; + jpp.addScope(scopeName); + auto gs2 = util::makeGroupScope(jpp, scopeName); + + const int nReactions = 4; + + std::vector stoichMat(nReactions * nComp, 0.0); + std::vector expFwd(nReactions * nComp, 0.0); + std::vector expBwd(nReactions * nComp, 0.0); + std::vector rateFwd(nReactions, 0.0); + std::vector rateBwd(nReactions, 0.0); + + util::populate( + stoichMat.data(), [](unsigned int idx) { return 2.0 * std::sin(0.5 + idx * 0.7); }, stoichMat.size()); + util::populate(expFwd.data(), [](unsigned int idx) { return std::sin(0.1 + idx * 0.7) + 1.4; }, expFwd.size()); + util::populate(expBwd.data(), [](unsigned int idx) { return std::sin(0.2 + idx * 0.3) + 1.1; }, expBwd.size()); + util::populate( + rateFwd.data(), [](unsigned int idx) { return std::sin(0.3 + idx * 0.2) * 0.5 + 1.5; }, rateFwd.size()); + util::populate( + rateBwd.data(), [](unsigned int idx) { return std::sin(0.4 + idx * 0.4) * 0.5 + 1.6; }, rateBwd.size()); + + jpp.set("MAL_STOICHIOMETRY_BULK", stoichMat); + jpp.set("MAL_EXPONENTS_BULK_FWD", expFwd); + jpp.set("MAL_EXPONENTS_BULK_BWD", expBwd); + jpp.set("MAL_KFWD_BULK", rateFwd); + jpp.set("MAL_KBWD_BULK", rateBwd); } - void testDynamicJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, double const* point, double absTol, double relTol) + if ((!isLRMP && particle) || (isLRMP && bulk)) { - ConfiguredDynamicReactionModel crm = ConfiguredDynamicReactionModel::create(modelName, nComp, nBound, config); - - const unsigned int numDofs = crm.nComp() + crm.numBoundStates(); - std::vector yState(numDofs, 0.0); - std::copy_n(point, numDofs, yState.data()); - - std::vector dir(numDofs, 0.0); - std::vector colA(numDofs, 0.0); - std::vector colB(numDofs, 0.0); - - // Enable AD - cadet::ad::setDirections(cadet::ad::getMaxDirections()); - cadet::active* adRes = new cadet::active[numDofs]; - cadet::active* adY = new cadet::active[numDofs]; - - // Combined liquid and solid phase - - // Evaluate with AD - ad::prepareAdVectorSeedsForDenseMatrix(adY, 0, numDofs); - ad::copyToAd(yState.data(), adY, numDofs); - ad::resetAd(adRes, numDofs); - crm.model().residualCombinedAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, adY, adY + crm.nComp(), adRes, adRes + crm.nComp(), 1.0, crm.buffer()); - - // Extract Jacobian - cadet::linalg::DenseMatrix jacAD; - jacAD.resize(numDofs, numDofs); - ad::extractDenseJacobianFromAd(adRes, 0, jacAD); - - // Calculate analytic Jacobian - cadet::linalg::DenseMatrix jacAna; - jacAna.resize(numDofs, numDofs); - crm.model().analyticJacobianCombinedAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yState.data(), yState.data() + crm.nComp(), 1.0, jacAna.row(0), jacAna.row(crm.nComp()), crm.buffer()); - - cadet::test::checkJacobianPatternFD( - [&](double const* lDir, double* res) -> void - { - std::fill_n(res, numDofs, 0.0); - crm.model().residualCombinedAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, lDir, lDir + crm.nComp(), res, res + crm.nComp(), 1.0, crm.buffer()); - }, - [&](double const* lDir, double* res) -> void - { - jacAna.multiplyVector(lDir, res); - }, - yState.data(), dir.data(), colA.data(), colB.data(), numDofs, numDofs); - - cadet::test::checkJacobianPatternFD( - [&](double const* lDir, double* res) -> void - { - std::fill_n(res, numDofs, 0.0); - crm.model().residualCombinedAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, lDir, lDir + crm.nComp(), res, res + crm.nComp(), 1.0, crm.buffer()); - }, - [&](double const* lDir, double* res) -> void - { - jacAD.multiplyVector(lDir, res); - }, - yState.data(), dir.data(), colA.data(), colB.data(), numDofs, numDofs); - - // Check Jacobians against each other - for (unsigned int row = 0; row < numDofs; ++row) - { - for (unsigned int col = 0; col < numDofs; ++col) - { - CAPTURE(row); - CAPTURE(col); - CHECK(jacAna.native(row, col) == makeApprox(jacAD.native(row, col), absTol, relTol)); - } - } + const int nReactions = 3; - // Only liquid phase - - // Evaluate with AD - ad::resetAd(adRes, numDofs); - crm.model().residualLiquidAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, adY, adRes, 1.0, crm.buffer()); - - // Extract Jacobian - jacAD.setAll(0.0); - ad::extractDenseJacobianFromAd(adRes, 0, jacAD); - - // Calculate analytic Jacobian - jacAna.setAll(0.0); - crm.model().analyticJacobianLiquidAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yState.data(), 1.0, jacAna.row(0), crm.buffer()); - - delete[] adY; - delete[] adRes; - - cadet::test::checkJacobianPatternFD( - [&](double const* lDir, double* res) -> void - { - std::fill_n(res, nComp, 0.0); - crm.model().residualLiquidAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, lDir, res, 1.0, crm.buffer()); - }, - [&](double const* lDir, double* res) -> void - { - jacAna.submatrixMultiplyVector(lDir, 0, 0, nComp, nComp, res); - }, - yState.data(), dir.data(), colA.data(), colB.data(), nComp, nComp); - - cadet::test::checkJacobianPatternFD( - [&](double const* lDir, double* res) -> void - { - std::fill_n(res, nComp, 0.0); - crm.model().residualLiquidAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, lDir, res, 1.0, crm.buffer()); - }, - [&](double const* lDir, double* res) -> void - { - jacAD.submatrixMultiplyVector(lDir, 0, 0, nComp, nComp, res); - }, - yState.data(), dir.data(), colA.data(), colB.data(), nComp, nComp); - - // Check Jacobians against each other - for (unsigned int row = 0; row < nComp; ++row) + for (int i = 0; i < nParType; ++i) { - for (unsigned int col = 0; col < nComp; ++col) + std::ostringstream oss; + if (isLRMP) + oss << "reaction"; + else + oss << "reaction_particle_" << std::setfill('0') << std::setw(3) << std::setprecision(0) << i; + + jpp.addScope(oss.str()); + auto gs2 = util::makeGroupScope(jpp, oss.str()); + + std::vector stoichMatLiquid(nReactions * nComp, 0.0); + std::vector expFwdLiquid(nReactions * nComp, 0.0); + std::vector expBwdLiquid(nReactions * nComp, 0.0); + std::vector rateFwdLiquid(nReactions, 0.0); + std::vector rateBwdLiquid(nReactions, 0.0); + + std::vector stoichMatSolid(nReactions * nTotalBound, 0.0); + std::vector expFwdSolid(nReactions * nTotalBound, 0.0); + std::vector expBwdSolid(nReactions * nTotalBound, 0.0); + std::vector rateFwdSolid(nReactions, 0.0); + std::vector rateBwdSolid(nReactions, 0.0); + + util::populate( + stoichMatLiquid.data(), [](unsigned int idx) { return 2.0 * std::sin(0.5 + idx * 0.7); }, + stoichMatLiquid.size()); + util::populate( + expFwdLiquid.data(), [](unsigned int idx) { return std::sin(0.1 + idx * 0.7) + 1.4; }, + expFwdLiquid.size()); + util::populate( + expBwdLiquid.data(), [](unsigned int idx) { return std::sin(0.2 + idx * 0.3) + 1.1; }, + expBwdLiquid.size()); + util::populate( + rateFwdLiquid.data(), [](unsigned int idx) { return std::sin(0.3 + idx * 0.2) * 0.5 + 1.5; }, + rateFwdLiquid.size()); + util::populate( + rateBwdLiquid.data(), [](unsigned int idx) { return std::sin(0.4 + idx * 0.4) * 0.5 + 1.6; }, + rateBwdLiquid.size()); + + util::populate( + stoichMatSolid.data(), [](unsigned int idx) { return 2.0 * std::sin(0.3 + idx * 0.9); }, + stoichMatSolid.size()); + util::populate( + expFwdSolid.data(), [](unsigned int idx) { return std::sin(0.2 + idx * 0.6) + 1.3; }, + expFwdSolid.size()); + util::populate( + expBwdSolid.data(), [](unsigned int idx) { return std::sin(0.1 + idx * 0.4) + 1.2; }, + expBwdSolid.size()); + util::populate( + rateFwdSolid.data(), [](unsigned int idx) { return std::sin(0.5 + idx * 0.4) * 0.5 + 1.6; }, + rateFwdSolid.size()); + util::populate( + rateBwdSolid.data(), [](unsigned int idx) { return std::sin(0.2 + idx * 0.3) * 0.5 + 1.5; }, + rateBwdSolid.size()); + + jpp.set("MAL_STOICHIOMETRY_LIQUID", stoichMatLiquid); + jpp.set("MAL_EXPONENTS_LIQUID_FWD", expFwdLiquid); + jpp.set("MAL_EXPONENTS_LIQUID_BWD", expBwdLiquid); + jpp.set("MAL_KFWD_LIQUID", rateFwdLiquid); + jpp.set("MAL_KBWD_LIQUID", rateBwdLiquid); + + jpp.set("MAL_STOICHIOMETRY_SOLID", stoichMatSolid); + jpp.set("MAL_EXPONENTS_SOLID_FWD", expFwdSolid); + jpp.set("MAL_EXPONENTS_SOLID_BWD", expBwdSolid); + jpp.set("MAL_KFWD_SOLID", rateFwdSolid); + jpp.set("MAL_KBWD_SOLID", rateBwdSolid); + + if (particleModifiers) { - CAPTURE(row); - CAPTURE(col); - CHECK(jacAna.native(row, col) == makeApprox(jacAD.native(row, col), absTol, relTol)); + std::vector expFwdLiquidModSolid(nReactions * nTotalBound, 0.0); + std::vector expBwdLiquidModSolid(nReactions * nTotalBound, 0.0); + std::vector expFwdSolidModLiquid(nReactions * nComp, 0.0); + std::vector expBwdSolidModLiquid(nReactions * nComp, 0.0); + + util::populate( + expFwdLiquidModSolid.data(), [](unsigned int idx) { return std::sin(idx * 0.7 + 0.1) * 0.5 + 1.6; }, + expFwdLiquidModSolid.size()); + util::populate( + expBwdLiquidModSolid.data(), [](unsigned int idx) { return std::sin(idx * 0.7 + 0.2) * 0.5 + 1.6; }, + expBwdLiquidModSolid.size()); + util::populate( + expFwdSolidModLiquid.data(), [](unsigned int idx) { return std::sin(idx * 0.7 + 0.3) * 0.5 + 1.6; }, + expFwdSolidModLiquid.size()); + util::populate( + expBwdSolidModLiquid.data(), [](unsigned int idx) { return std::sin(idx * 0.7 + 0.4) * 0.5 + 1.6; }, + expBwdSolidModLiquid.size()); + + jpp.set("MAL_EXPONENTS_LIQUID_FWD_MODSOLID", expFwdLiquidModSolid); + jpp.set("MAL_EXPONENTS_LIQUID_BWD_MODSOLID", expBwdLiquidModSolid); + jpp.set("MAL_EXPONENTS_SOLID_FWD_MODLIQUID", expFwdSolidModLiquid); + jpp.set("MAL_EXPONENTS_SOLID_BWD_MODLIQUID", expBwdSolidModLiquid); } } } +} - void testUnitJacobianDynamicReactionsAD(cadet::JsonParameterProvider& jpp, bool bulk, bool particle, bool particleModifiers, const double absTolFDpattern) +void testMichaelisMentenToSMAMicroKinetic(const std::string configFilePathMM, const std::string configFilePathSMA, + const double absTol, const double relTol) +{ + // read json model setup file + const std::string setupFileMM = std::string(getTestDirectory()) + configFilePathMM; + const std::string setupFileSMA = std::string(getTestDirectory()) + configFilePathSMA; + JsonParameterProvider pp_setup_MM(JsonParameterProvider::fromFile(setupFileMM)); + JsonParameterProvider pp_setup_SMA(JsonParameterProvider::fromFile(setupFileSMA)); + + nlohmann::json* setupJsonMM = pp_setup_MM.data(); + nlohmann::json* setupJsonSMA = pp_setup_SMA.data(); + + // MM simulation + cadet::Driver drvMM; + drvMM.configure(pp_setup_MM); + drvMM.run(); + + // SMA micro-kinetics simulation + cadet::Driver drvSMA; + drvSMA.configure(pp_setup_SMA); + drvSMA.run(); + + cadet::InternalStorageUnitOpRecorder const* const MMData = drvMM.solution()->unitOperation(0); + cadet::InternalStorageUnitOpRecorder const* const SMAData = drvSMA.solution()->unitOperation(0); + + double const* outletMM = MMData->outlet(); + double const* outletSMA = SMAData->outlet(); + + const unsigned int nCompMM = MMData->numComponents(); + const unsigned int nCompSMA = SMAData->numComponents(); + for (unsigned int i = 0; i < SMAData->numDataPoints(); ++i, outletMM += nCompMM, outletSMA += nCompSMA) { - extendModelWithDynamicReactions(jpp, 0, bulk, particle, particleModifiers); - unitoperation::testJacobianAD(jpp, absTolFDpattern); + CAPTURE(i); + CHECK((outletMM[0]) == cadet::test::makeApprox(outletSMA[1], relTol, absTol)); + CHECK((outletMM[1]) == cadet::test::makeApprox(outletSMA[3], relTol, absTol)); } +} - void testUnitJacobianDynamicReactionsAD(const std::string& uoType, const std::string& spatialMethod, bool bulk, bool particle, bool particleModifiers, const double absTolFDpattern) +void testDynamicJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, + double const* point, double absTol, double relTol) +{ + ConfiguredDynamicReactionModel crm = ConfiguredDynamicReactionModel::create(modelName, nComp, nBound, config); + + const unsigned int numDofs = crm.nComp() + crm.numBoundStates(); + std::vector yState(numDofs, 0.0); + std::copy_n(point, numDofs, yState.data()); + + std::vector dir(numDofs, 0.0); + std::vector colA(numDofs, 0.0); + std::vector colB(numDofs, 0.0); + + // Enable AD + cadet::ad::setDirections(cadet::ad::getMaxDirections()); + cadet::active* adRes = new cadet::active[numDofs]; + cadet::active* adY = new cadet::active[numDofs]; + + // Combined liquid and solid phase + + // Evaluate with AD + ad::prepareAdVectorSeedsForDenseMatrix(adY, 0, numDofs); + ad::copyToAd(yState.data(), adY, numDofs); + ad::resetAd(adRes, numDofs); + crm.model().residualCombinedAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, adY, adY + crm.nComp(), adRes, + adRes + crm.nComp(), 1.0, crm.buffer()); + + // Extract Jacobian + cadet::linalg::DenseMatrix jacAD; + jacAD.resize(numDofs, numDofs); + ad::extractDenseJacobianFromAd(adRes, 0, jacAD); + + // Calculate analytic Jacobian + cadet::linalg::DenseMatrix jacAna; + jacAna.resize(numDofs, numDofs); + crm.model().analyticJacobianCombinedAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yState.data(), + yState.data() + crm.nComp(), 1.0, jacAna.row(0), jacAna.row(crm.nComp()), + crm.buffer()); + + cadet::test::checkJacobianPatternFD( + [&](double const* lDir, double* res) -> void { + std::fill_n(res, numDofs, 0.0); + crm.model().residualCombinedAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, lDir, lDir + crm.nComp(), res, + res + crm.nComp(), 1.0, crm.buffer()); + }, + [&](double const* lDir, double* res) -> void { jacAna.multiplyVector(lDir, res); }, yState.data(), dir.data(), + colA.data(), colB.data(), numDofs, numDofs); + + cadet::test::checkJacobianPatternFD( + [&](double const* lDir, double* res) -> void { + std::fill_n(res, numDofs, 0.0); + crm.model().residualCombinedAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, lDir, lDir + crm.nComp(), res, + res + crm.nComp(), 1.0, crm.buffer()); + }, + [&](double const* lDir, double* res) -> void { jacAD.multiplyVector(lDir, res); }, yState.data(), dir.data(), + colA.data(), colB.data(), numDofs, numDofs); + + // Check Jacobians against each other + for (unsigned int row = 0; row < numDofs; ++row) { - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); - testUnitJacobianDynamicReactionsAD(jpp, bulk, particle, particleModifiers, absTolFDpattern); + for (unsigned int col = 0; col < numDofs; ++col) + { + CAPTURE(row); + CAPTURE(col); + CHECK(jacAna.native(row, col) == makeApprox(jacAD.native(row, col), absTol, relTol)); + } } - void testTimeDerivativeJacobianDynamicReactionsFD(cadet::JsonParameterProvider& jpp, bool bulk, bool particle, bool particleModifiers, double h, double absTol, double relTol) + // Only liquid phase + + // Evaluate with AD + ad::resetAd(adRes, numDofs); + crm.model().residualLiquidAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, adY, adRes, 1.0, crm.buffer()); + + // Extract Jacobian + jacAD.setAll(0.0); + ad::extractDenseJacobianFromAd(adRes, 0, jacAD); + + // Calculate analytic Jacobian + jacAna.setAll(0.0); + crm.model().analyticJacobianLiquidAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, yState.data(), 1.0, jacAna.row(0), + crm.buffer()); + + delete[] adY; + delete[] adRes; + + cadet::test::checkJacobianPatternFD( + [&](double const* lDir, double* res) -> void { + std::fill_n(res, nComp, 0.0); + crm.model().residualLiquidAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, lDir, res, 1.0, crm.buffer()); + }, + [&](double const* lDir, double* res) -> void { jacAna.submatrixMultiplyVector(lDir, 0, 0, nComp, nComp, res); }, + yState.data(), dir.data(), colA.data(), colB.data(), nComp, nComp); + + cadet::test::checkJacobianPatternFD( + [&](double const* lDir, double* res) -> void { + std::fill_n(res, nComp, 0.0); + crm.model().residualLiquidAdd(1.0, 0u, ColumnPosition{0.0, 0.0, 0.0}, lDir, res, 1.0, crm.buffer()); + }, + [&](double const* lDir, double* res) -> void { jacAD.submatrixMultiplyVector(lDir, 0, 0, nComp, nComp, res); }, + yState.data(), dir.data(), colA.data(), colB.data(), nComp, nComp); + + // Check Jacobians against each other + for (unsigned int row = 0; row < nComp; ++row) { - extendModelWithDynamicReactions(jpp, 0, bulk, particle, particleModifiers); - unitoperation::testTimeDerivativeJacobianFD(jpp, h, absTol, relTol); + for (unsigned int col = 0; col < nComp; ++col) + { + CAPTURE(row); + CAPTURE(col); + CHECK(jacAna.native(row, col) == makeApprox(jacAD.native(row, col), absTol, relTol)); + } } +} - void testTimeDerivativeJacobianDynamicReactionsFD(const std::string& uoType, const std::string& spatialMethod, bool bulk, bool particle, bool particleModifiers, double h, double absTol, double relTol) - { - cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); - testTimeDerivativeJacobianDynamicReactionsFD(jpp, bulk, particle, particleModifiers, h, absTol, relTol); - } +void testUnitJacobianDynamicReactionsAD(cadet::JsonParameterProvider& jpp, bool bulk, bool particle, + bool particleModifiers, const double absTolFDpattern) +{ + extendModelWithDynamicReactions(jpp, 0, bulk, particle, particleModifiers); + unitoperation::testJacobianAD(jpp, absTolFDpattern); +} + +void testUnitJacobianDynamicReactionsAD(const std::string& uoType, const std::string& spatialMethod, bool bulk, + bool particle, bool particleModifiers, const double absTolFDpattern) +{ + cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); + testUnitJacobianDynamicReactionsAD(jpp, bulk, particle, particleModifiers, absTolFDpattern); +} + +void testTimeDerivativeJacobianDynamicReactionsFD(cadet::JsonParameterProvider& jpp, bool bulk, bool particle, + bool particleModifiers, double h, double absTol, double relTol) +{ + extendModelWithDynamicReactions(jpp, 0, bulk, particle, particleModifiers); + unitoperation::testTimeDerivativeJacobianFD(jpp, h, absTol, relTol); +} + +void testTimeDerivativeJacobianDynamicReactionsFD(const std::string& uoType, const std::string& spatialMethod, + bool bulk, bool particle, bool particleModifiers, double h, + double absTol, double relTol) +{ + cadet::JsonParameterProvider jpp = createColumnWithTwoCompLinearBinding(uoType, spatialMethod); + testTimeDerivativeJacobianDynamicReactionsFD(jpp, bulk, particle, particleModifiers, h, absTol, relTol); +} } // namespace reaction } // namespace test diff --git a/test/ReactionModelTests.hpp b/test/ReactionModelTests.hpp index 4c8a754ab..390c4da30 100644 --- a/test/ReactionModelTests.hpp +++ b/test/ReactionModelTests.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines tests for reaction models. */ @@ -22,7 +22,6 @@ #include #include "Memory.hpp" - namespace cadet { @@ -31,7 +30,7 @@ class IExternalFunction; namespace model { - class IDynamicReactionModel; +class IDynamicReactionModel; } namespace test @@ -39,152 +38,195 @@ namespace test namespace reaction { - class ConfiguredDynamicReactionModel +class ConfiguredDynamicReactionModel +{ +public: + ConfiguredDynamicReactionModel(ConfiguredDynamicReactionModel&& cpy) CADET_NOEXCEPT + : _reaction(cpy._reaction), + _nComp(cpy._nComp), + _nBound(cpy._nBound), + _boundOffset(cpy._boundOffset), + _buffer(std::move(cpy._buffer)), + _bufferMemory(cpy._bufferMemory), + _extFuns(cpy._extFuns) + { + cpy._reaction = nullptr; + cpy._nBound = nullptr; + cpy._boundOffset = nullptr; + cpy._bufferMemory = nullptr; + cpy._extFuns = nullptr; + } + + ~ConfiguredDynamicReactionModel(); + + inline ConfiguredDynamicReactionModel& operator=(ConfiguredDynamicReactionModel&& cpy) CADET_NOEXCEPT + { + _reaction = cpy._reaction; + _nComp = cpy._nComp; + _nBound = cpy._nBound; + _boundOffset = cpy._boundOffset; + _buffer = std::move(cpy._buffer); + _bufferMemory = cpy._bufferMemory; + _extFuns = cpy._extFuns; + + cpy._reaction = nullptr; + cpy._nBound = nullptr; + cpy._boundOffset = nullptr; + cpy._bufferMemory = nullptr; + cpy._extFuns = nullptr; + + return *this; + } + + static ConfiguredDynamicReactionModel create(const char* name, unsigned int nComp, unsigned int const* nBound, + const char* config); + + void increaseBufferSize(int inc); + int requiredBufferSize() CADET_NOEXCEPT; + + inline cadet::model::IDynamicReactionModel& model() + { + return *_reaction; + } + inline const cadet::model::IDynamicReactionModel& model() const + { + return *_reaction; + } + + inline cadet::LinearBufferAllocator buffer() + { + return _buffer; + } + inline unsigned int nComp() const { - public: - - ConfiguredDynamicReactionModel(ConfiguredDynamicReactionModel&& cpy) CADET_NOEXCEPT - : _reaction(cpy._reaction), _nComp(cpy._nComp), _nBound(cpy._nBound), _boundOffset(cpy._boundOffset), _buffer(std::move(cpy._buffer)), _bufferMemory(cpy._bufferMemory), _extFuns(cpy._extFuns) - { - cpy._reaction = nullptr; - cpy._nBound = nullptr; - cpy._boundOffset = nullptr; - cpy._bufferMemory = nullptr; - cpy._extFuns = nullptr; - } - - ~ConfiguredDynamicReactionModel(); - - inline ConfiguredDynamicReactionModel& operator=(ConfiguredDynamicReactionModel&& cpy) CADET_NOEXCEPT - { - _reaction = cpy._reaction; - _nComp = cpy._nComp; - _nBound = cpy._nBound; - _boundOffset = cpy._boundOffset; - _buffer = std::move(cpy._buffer); - _bufferMemory = cpy._bufferMemory; - _extFuns = cpy._extFuns; - - cpy._reaction = nullptr; - cpy._nBound = nullptr; - cpy._boundOffset = nullptr; - cpy._bufferMemory = nullptr; - cpy._extFuns = nullptr; - - return *this; - } - - static ConfiguredDynamicReactionModel create(const char* name, unsigned int nComp, unsigned int const* nBound, const char* config); - - void increaseBufferSize(int inc); - int requiredBufferSize() CADET_NOEXCEPT; - - inline cadet::model::IDynamicReactionModel& model() { return *_reaction; } - inline const cadet::model::IDynamicReactionModel& model() const { return *_reaction; } - - inline cadet::LinearBufferAllocator buffer() { return _buffer; } - inline unsigned int nComp() const { return _nComp; } - inline unsigned int const* nBound() const { return _nBound; } - inline unsigned int const* boundOffset() const { return _boundOffset; } - - inline unsigned int numBoundStates() const { return _boundOffset[_nComp - 1] + _nBound[_nComp - 1]; } - - private: - - ConfiguredDynamicReactionModel(cadet::model::IDynamicReactionModel* reaction, unsigned int nComp, unsigned int const* nBound, unsigned int const* boundOffset, void* bufferStart, void* bufferEnd, cadet::IExternalFunction* extFuns) - : _reaction(reaction), _nComp(nComp), _nBound(nBound), _boundOffset(boundOffset), _buffer(bufferStart, bufferEnd), _bufferMemory(bufferStart), _extFuns(extFuns) - { - } - - cadet::model::IDynamicReactionModel* _reaction; - unsigned int _nComp; - unsigned int const* _nBound; - unsigned int const* _boundOffset; - cadet::LinearBufferAllocator _buffer; - void* _bufferMemory; - cadet::IExternalFunction* _extFuns; - }; - - /** - * @brief Compares two simulations (SMA and MM) wrt specific components - * @param [in] configFilePathMM relative file path to Michaelis Menten configuration file - * @param [in] configFilePathSMA relative file path to SMA micro-kinetic configuration file - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testMichaelisMentenToSMAMicroKinetic(const std::string configFilePathMM, const std::string configFilePathSMA, const double absTol, const double relTol); - - /** - * @brief Checks the analytic Jacobians of the dynamic reaction model against AD - * @param [in] modelName Name of the reaction model - * @param [in] nComp Number of components - * @param [in] nBound Array with number of bound states for each component - * @param [in] config JSON string with reaction model parameters - * @param [in] point Liquid phase and solid phase values to check Jacobian at - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testDynamicJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, double const* point, double absTol = 0.0, double relTol = std::numeric_limits::epsilon() * 100.0); - - /** - * @brief Extends a model with dynamic reactions in each phase and particle type - * @param [in,out] jpp ParameterProvider to extend - * @param [in] unit Index of unit operation - * @param [in] bulk Determines whether reactions are added to bulk volume - * @param [in] particle Determines whether reactions are added to each particle type - * @param [in] particleModifiers Determines whether reaction rates in particles are modified by the respective other phase - */ - void extendModelWithDynamicReactions(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, bool bulk, bool particle, bool particleModifiers); - - /** - * @brief Checks the full analytic Jacobian of a unit operation model with dynamic reactions against AD - * @param [in] jpp Unit operation configuration - * @param [in] bulk Determines whether reactions are added to bulk volume - * @param [in] particle Determines whether reactions are added to each particle type - * @param [in] particleModifiers Determines whether reaction rates in particles are modified by the respective other phase - * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern - */ - void testUnitJacobianDynamicReactionsAD(cadet::JsonParameterProvider& jpp, bool bulk, bool particle, bool particleModifiers, const double absTolFDpattern=0.0); - - /** - * @brief Checks the full analytic Jacobian of a unit operation model with dynamic reactions against AD - * @details Uses a model with linear binding model and two components. - * @param [in] uoType Unit operation type - * @param [in] bulk Determines whether reactions are added to bulk volume - * @param [in] particle Determines whether reactions are added to each particle type - * @param [in] particleModifiers Determines whether reaction rates in particles are modified by the respective other phase - * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern - */ - void testUnitJacobianDynamicReactionsAD(const std::string& uoType, const std::string& spatialMethod, bool bulk, bool particle, bool particleModifiers, const double absTolFDpattern = 0.0); - - /** - * @brief Checks the (analytic) time derivative Jacobian against FD for a model with dynamic reactions - * @details Uses centered finite differences. - * @param [in] jpp Unit operation configuration - * @param [in] bulk Determines whether reactions are added to bulk volume - * @param [in] particle Determines whether reactions are added to each particle type - * @param [in] particleModifiers Determines whether reaction rates in particles are modified by the respective other phase - * @param [in] h Step size of centered finite differences - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testTimeDerivativeJacobianDynamicReactionsFD(cadet::JsonParameterProvider& jpp, bool bulk, bool particle, bool particleModifiers, double h, double absTol, double relTol); - - /** - * @brief Checks the (analytic) time derivative Jacobian against FD for a model with dynamic reactions - * @details Uses centered finite differences. Uses a model with linear binding model and two components. - * @param [in] uoType Unit operation type - * @param [in] bulk Determines whether reactions are added to bulk volume - * @param [in] particle Determines whether reactions are added to each particle type - * @param [in] particleModifiers Determines whether reaction rates in particles are modified by the respective other phase - * @param [in] h Step size of centered finite differences - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testTimeDerivativeJacobianDynamicReactionsFD(const std::string& uoType, const std::string& spatialMethod, bool bulk, bool particle, bool particleModifiers, double h, double absTol, double relTol); + return _nComp; + } + inline unsigned int const* nBound() const + { + return _nBound; + } + inline unsigned int const* boundOffset() const + { + return _boundOffset; + } + + inline unsigned int numBoundStates() const + { + return _boundOffset[_nComp - 1] + _nBound[_nComp - 1]; + } + +private: + ConfiguredDynamicReactionModel(cadet::model::IDynamicReactionModel* reaction, unsigned int nComp, + unsigned int const* nBound, unsigned int const* boundOffset, void* bufferStart, + void* bufferEnd, cadet::IExternalFunction* extFuns) + : _reaction(reaction), _nComp(nComp), _nBound(nBound), _boundOffset(boundOffset), + _buffer(bufferStart, bufferEnd), _bufferMemory(bufferStart), _extFuns(extFuns) + { + } + + cadet::model::IDynamicReactionModel* _reaction; + unsigned int _nComp; + unsigned int const* _nBound; + unsigned int const* _boundOffset; + cadet::LinearBufferAllocator _buffer; + void* _bufferMemory; + cadet::IExternalFunction* _extFuns; +}; + +/** + * @brief Compares two simulations (SMA and MM) wrt specific components + * @param [in] configFilePathMM relative file path to Michaelis Menten configuration file + * @param [in] configFilePathSMA relative file path to SMA micro-kinetic configuration file + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testMichaelisMentenToSMAMicroKinetic(const std::string configFilePathMM, const std::string configFilePathSMA, + const double absTol, const double relTol); + +/** + * @brief Checks the analytic Jacobians of the dynamic reaction model against AD + * @param [in] modelName Name of the reaction model + * @param [in] nComp Number of components + * @param [in] nBound Array with number of bound states for each component + * @param [in] config JSON string with reaction model parameters + * @param [in] point Liquid phase and solid phase values to check Jacobian at + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testDynamicJacobianAD(const char* modelName, unsigned int nComp, unsigned int const* nBound, const char* config, + double const* point, double absTol = 0.0, + double relTol = std::numeric_limits::epsilon() * 100.0); + +/** + * @brief Extends a model with dynamic reactions in each phase and particle type + * @param [in,out] jpp ParameterProvider to extend + * @param [in] unit Index of unit operation + * @param [in] bulk Determines whether reactions are added to bulk volume + * @param [in] particle Determines whether reactions are added to each particle type + * @param [in] particleModifiers Determines whether reaction rates in particles are modified by the respective other + * phase + */ +void extendModelWithDynamicReactions(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, bool bulk, bool particle, + bool particleModifiers); + +/** + * @brief Checks the full analytic Jacobian of a unit operation model with dynamic reactions against AD + * @param [in] jpp Unit operation configuration + * @param [in] bulk Determines whether reactions are added to bulk volume + * @param [in] particle Determines whether reactions are added to each particle type + * @param [in] particleModifiers Determines whether reaction rates in particles are modified by the respective other + * phase + * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern + */ +void testUnitJacobianDynamicReactionsAD(cadet::JsonParameterProvider& jpp, bool bulk, bool particle, + bool particleModifiers, const double absTolFDpattern = 0.0); + +/** + * @brief Checks the full analytic Jacobian of a unit operation model with dynamic reactions against AD + * @details Uses a model with linear binding model and two components. + * @param [in] uoType Unit operation type + * @param [in] bulk Determines whether reactions are added to bulk volume + * @param [in] particle Determines whether reactions are added to each particle type + * @param [in] particleModifiers Determines whether reaction rates in particles are modified by the respective other + * phase + * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern + */ +void testUnitJacobianDynamicReactionsAD(const std::string& uoType, const std::string& spatialMethod, bool bulk, + bool particle, bool particleModifiers, const double absTolFDpattern = 0.0); + +/** + * @brief Checks the (analytic) time derivative Jacobian against FD for a model with dynamic reactions + * @details Uses centered finite differences. + * @param [in] jpp Unit operation configuration + * @param [in] bulk Determines whether reactions are added to bulk volume + * @param [in] particle Determines whether reactions are added to each particle type + * @param [in] particleModifiers Determines whether reaction rates in particles are modified by the respective other + * phase + * @param [in] h Step size of centered finite differences + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testTimeDerivativeJacobianDynamicReactionsFD(cadet::JsonParameterProvider& jpp, bool bulk, bool particle, + bool particleModifiers, double h, double absTol, double relTol); + +/** + * @brief Checks the (analytic) time derivative Jacobian against FD for a model with dynamic reactions + * @details Uses centered finite differences. Uses a model with linear binding model and two components. + * @param [in] uoType Unit operation type + * @param [in] bulk Determines whether reactions are added to bulk volume + * @param [in] particle Determines whether reactions are added to each particle type + * @param [in] particleModifiers Determines whether reaction rates in particles are modified by the respective other + * phase + * @param [in] h Step size of centered finite differences + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testTimeDerivativeJacobianDynamicReactionsFD(const std::string& uoType, const std::string& spatialMethod, + bool bulk, bool particle, bool particleModifiers, double h, + double absTol, double relTol); } // namespace reaction } // namespace test } // namespace cadet -#endif // CADETTEST_REACTIONODELTEST_HPP_ +#endif // CADETTEST_REACTIONODELTEST_HPP_ diff --git a/test/ReactionModels.cpp b/test/ReactionModels.cpp index 0a440042f..98d57c763 100644 --- a/test/ReactionModels.cpp +++ b/test/ReactionModels.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -20,7 +20,7 @@ TEST_CASE("MassActionLaw kinetic analytic Jacobian vs AD", "[MassActionLaw],[Rea const unsigned int nBound[] = {1, 2, 1}; const double point[] = {1.0, 2.0, 1.4, 2.1, 0.2, 1.1, 1.8}; cadet::test::reaction::testDynamicJacobianAD("MASS_ACTION_LAW", 3, nBound, - R"json({ + R"json({ "MAL_KFWD_BULK": [1.0, 2.0, 0.4], "MAL_KBWD_BULK": [0.0, 0.2, 1.5], "MAL_STOICHIOMETRY_BULK": [ 1.0, -2.0, 3.0, @@ -36,21 +36,21 @@ TEST_CASE("MassActionLaw kinetic analytic Jacobian vs AD", "[MassActionLaw],[Rea "MAL_KFWD_LIQUID": [1.0, 2.0, 0.8, 1.2, 2.1], "MAL_KBWD_LIQUID": [0.1, 0.2, 2.4, 1.9, 0.8], "MAL_STOICHIOMETRY_LIQUID": [ 1.0, -2.0, 3.0, 1.0, -2.0, - -1.0, 0.0, -2.0, -3.0, -1.0, + -1.0, 0.0, -2.0, -3.0, -1.0, 0.0, 1.0, 1.0, 2.0, 3.0], "MAL_EXPONENTS_LIQUID_FWD": [ 0.4, 0.0, 2.0, 1.2, 0.0, - 1.0, 1.0, 2.0, 0.0, 1.0, + 1.0, 1.0, 2.0, 0.0, 1.0, 0.0, 2.0, 0.0, 2.4, 1.4], "MAL_EXPONENTS_LIQUID_BWD": [ 1.0, 2.0, 0.0, 0.2, 2.0, - 0.0, 1.0, 2.0, 3.0, 1.0, + 0.0, 1.0, 2.0, 3.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0], "MAL_EXPONENTS_LIQUID_FWD_MODSOLID": [ 1.0, 0.0, 0.0, 1.2, 0.0, - 1.0, 1.6, 2.1, 0.0, 1.0, - 0.0, 0.0, 0.0, 0.0, 1.0, + 1.0, 1.6, 2.1, 0.0, 1.0, + 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 2.4, 0.0], "MAL_EXPONENTS_LIQUID_BWD_MODSOLID": [ 0.0, 2.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 1.0, 0.0, - 0.0, 1.0, 2.0, 1.2, 0.0, + 0.0, 0.0, 0.0, 1.0, 0.0, + 0.0, 1.0, 2.0, 1.2, 0.0, 1.0, 0.0, 0.0, 0.0, 1.4], "MAL_KFWD_SOLID": [1.0, 2.0], @@ -74,11 +74,11 @@ TEST_CASE("MassActionLaw kinetic analytic Jacobian vs AD", "[MassActionLaw],[Rea 2.0, 0.0, 1.8, 0.0] })json", - point, 1e-15, 1e-15 - ); + point, 1e-15, 1e-15); } -TEST_CASE("MichaelisMenten kinetic and specific mass action law micro-kinetics yield same result", "[MichaelisMenten],[ReactionModel],[Simulation],[CI]") +TEST_CASE("MichaelisMenten kinetic and specific mass action law micro-kinetics yield same result", + "[MichaelisMenten],[ReactionModel],[Simulation],[CI]") { const std::string& configFilePath1 = std::string("/data/configuration_CSTR_MichaelisMenten_benchmark1.json"); const std::string& configFilePath2 = std::string("/data/configuration_CSTR_MicroKineticsSMA_benchmark1.json"); @@ -89,7 +89,8 @@ TEST_CASE("MichaelisMenten kinetic and specific mass action law micro-kinetics y cadet::test::reaction::testMichaelisMentenToSMAMicroKinetic(configFilePath1, configFilePath2, absTol, relTol); } -TEST_CASE("MichaelisMenten kinetic and numerical reference with Crank-Nicolson yield same result", "[MichaelisMenten],[ReactionModel],[Simulation],[Reference],[CI]") +TEST_CASE("MichaelisMenten kinetic and numerical reference with Crank-Nicolson yield same result", + "[MichaelisMenten],[ReactionModel],[Simulation],[Reference],[CI]") { const std::string& configFileRelPath = std::string("/data/configuration_CSTR_MichaelisMenten_benchmark2.json"); const std::string& refFileRelPath = std::string("/data/ref_CSTR_MichaelisMenten_benchmark2.h5"); @@ -100,12 +101,13 @@ TEST_CASE("MichaelisMenten kinetic and numerical reference with Crank-Nicolson y cadet::test::column::testForeignReferenceBenchmark(configFileRelPath, refFileRelPath, "000", absTol, relTol, 1); } -TEST_CASE("MichaelisMenten kinetic analytic Jacobian vs AD without inhibition", "[MichaelisMenten],[ReactionModel],[Jacobian],[AD]") +TEST_CASE("MichaelisMenten kinetic analytic Jacobian vs AD without inhibition", + "[MichaelisMenten],[ReactionModel],[Jacobian],[AD]") { const unsigned int nBound[] = {1, 2, 1}; const double point[] = {1.0, 2.0, 1.4, 2.1, 0.2, 1.1, 1.8}; cadet::test::reaction::testDynamicJacobianAD("MICHAELIS_MENTEN", 3, nBound, - R"json({ + R"json({ "MM_KMM": [1.0, 2.0, 0.4], "MM_KI": [-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0], "MM_VMAX": [1.0, 0.2, 1.5], @@ -113,16 +115,16 @@ TEST_CASE("MichaelisMenten kinetic analytic Jacobian vs AD without inhibition", -1.0, 0.0, -2.0, 0.0, 1.0, 1.0] })json", - point, 1e-15, 1e-15 - ); + point, 1e-15, 1e-15); } -TEST_CASE("MichaelisMenten kinetic analytic Jacobian vs AD with inhibition", "[MichaelisMenten],[ReactionModel],[Jacobian],[AD]") +TEST_CASE("MichaelisMenten kinetic analytic Jacobian vs AD with inhibition", + "[MichaelisMenten],[ReactionModel],[Jacobian],[AD]") { const unsigned int nBound[] = {1, 2, 1}; const double point[] = {1.0, 2.0, 1.4, 2.1, 0.2, 1.1, 1.8}; cadet::test::reaction::testDynamicJacobianAD("MICHAELIS_MENTEN", 3, nBound, - R"json({ + R"json({ "MM_KMM": [1.0, 2.0, 0.4], "MM_KI": [-1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 3.0, 2.0, -1.0], "MM_VMAX": [1.0, 0.2, 1.5], @@ -130,6 +132,5 @@ TEST_CASE("MichaelisMenten kinetic analytic Jacobian vs AD with inhibition", "[M -1.0, 0.0, -2.0, 0.0, 1.0, 1.0] })json", - point, 1e-15, 1e-15 - ); + point, 1e-15, 1e-15); } diff --git a/test/SimHelper.cpp b/test/SimHelper.cpp index 8a008e861..84632d58a 100644 --- a/test/SimHelper.cpp +++ b/test/SimHelper.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -22,254 +22,260 @@ namespace cadet namespace test { - void setNumberOfComponents(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, int nComp) - { - auto gs = util::makeModelGroupScope(jpp, unit); - jpp.set("NCOMP", nComp); - } +void setNumberOfComponents(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, int nComp) +{ + auto gs = util::makeModelGroupScope(jpp, unit); + jpp.set("NCOMP", nComp); +} - void setInitialConditions(cadet::JsonParameterProvider& jpp, const std::vector& c, const std::vector& q, double v) - { - auto gs = util::makeModelGroupScope(jpp); +void setInitialConditions(cadet::JsonParameterProvider& jpp, const std::vector& c, const std::vector& q, + double v) +{ + auto gs = util::makeModelGroupScope(jpp); - jpp.set("INIT_C", c); - jpp.set("INIT_VOLUME", v); - if (!q.empty()) - jpp.set("INIT_Q", q); - } + jpp.set("INIT_C", c); + jpp.set("INIT_VOLUME", v); + if (!q.empty()) + jpp.set("INIT_Q", q); +} - void setInitialConditions(cadet::JsonParameterProvider& jpp, const std::vector& c, const std::vector& cp, const std::vector& q) - { - auto gs = util::makeModelGroupScope(jpp); +void setInitialConditions(cadet::JsonParameterProvider& jpp, const std::vector& c, + const std::vector& cp, const std::vector& q) +{ + auto gs = util::makeModelGroupScope(jpp); - jpp.set("INIT_C", c); - if (!cp.empty()) - jpp.set("INIT_CP", cp); - if (!q.empty()) - jpp.set("INIT_Q", q); - } + jpp.set("INIT_C", c); + if (!cp.empty()) + jpp.set("INIT_CP", cp); + if (!q.empty()) + jpp.set("INIT_Q", q); +} - void setFlowRates(cadet::JsonParameterProvider& jpp, unsigned int secIdx, double in, double out, double filter) - { - setFlowRates(jpp, secIdx, filter); +void setFlowRates(cadet::JsonParameterProvider& jpp, unsigned int secIdx, double in, double out, double filter) +{ + setFlowRates(jpp, secIdx, filter); - // Change to /model - auto gs = util::makeOptionalGroupScope(jpp, "model"); + // Change to /model + auto gs = util::makeOptionalGroupScope(jpp, "model"); - jpp.pushScope("connections"); + jpp.pushScope("connections"); - std::ostringstream ss; - ss << "switch_" << std::setfill('0') << std::setw(3) << secIdx; - jpp.pushScope(ss.str()); + std::ostringstream ss; + ss << "switch_" << std::setfill('0') << std::setw(3) << secIdx; + jpp.pushScope(ss.str()); - std::vector con = jpp.getDoubleArray("CONNECTIONS"); - con[6] = in; - con[13] = out; - jpp.set("CONNECTIONS", con); + std::vector con = jpp.getDoubleArray("CONNECTIONS"); + con[6] = in; + con[13] = out; + jpp.set("CONNECTIONS", con); - jpp.popScope(); - jpp.popScope(); - } + jpp.popScope(); + jpp.popScope(); +} - void setFlowRates(cadet::JsonParameterProvider& jpp, unsigned int secIdx, double filter) - { - // Change to /model/unit_000 - auto gs = util::makeModelGroupScope(jpp); +void setFlowRates(cadet::JsonParameterProvider& jpp, unsigned int secIdx, double filter) +{ + // Change to /model/unit_000 + auto gs = util::makeModelGroupScope(jpp); - std::vector frf = jpp.getDoubleArray("FLOWRATE_FILTER"); - if (frf.size() <= secIdx) - std::fill_n(std::back_inserter(frf), frf.size() - secIdx + 1, 0.0); + std::vector frf = jpp.getDoubleArray("FLOWRATE_FILTER"); + if (frf.size() <= secIdx) + std::fill_n(std::back_inserter(frf), frf.size() - secIdx + 1, 0.0); - frf[secIdx] = filter; - jpp.set("FLOWRATE_FILTER", frf); - } + frf[secIdx] = filter; + jpp.set("FLOWRATE_FILTER", frf); +} - void setInletProfile(cadet::JsonParameterProvider& jpp, unsigned int secIdx, unsigned int comp, double con, double lin, double quad, double cub) - { - auto gs = util::makeModelGroupScope(jpp, 1); - - std::ostringstream ss; - ss << "sec_" << std::setfill('0') << std::setw(3) << secIdx; - jpp.pushScope(ss.str()); - - std::vector cCon = jpp.getDoubleArray("CONST_COEFF"); - if (cCon.size() <= comp) - cCon.resize(comp + 1); - cCon[comp] = con; - jpp.set("CONST_COEFF", cCon); - - std::vector cLin = jpp.getDoubleArray("LIN_COEFF"); - if (cLin.size() <= comp) - cLin.resize(comp + 1); - cLin[comp] = lin; - jpp.set("LIN_COEFF", cLin); - - std::vector cQuad = jpp.getDoubleArray("QUAD_COEFF"); - if (cQuad.size() <= comp) - cQuad.resize(comp + 1); - cQuad[comp] = quad; - jpp.set("QUAD_COEFF", cQuad); - - std::vector cCube = jpp.getDoubleArray("CUBE_COEFF"); - if (cCube.size() <= comp) - cCube.resize(comp + 1); - cCube[comp] = cub; - jpp.set("CUBE_COEFF", cCube); - - jpp.popScope(); - } +void setInletProfile(cadet::JsonParameterProvider& jpp, unsigned int secIdx, unsigned int comp, double con, double lin, + double quad, double cub) +{ + auto gs = util::makeModelGroupScope(jpp, 1); + + std::ostringstream ss; + ss << "sec_" << std::setfill('0') << std::setw(3) << secIdx; + jpp.pushScope(ss.str()); + + std::vector cCon = jpp.getDoubleArray("CONST_COEFF"); + if (cCon.size() <= comp) + cCon.resize(comp + 1); + cCon[comp] = con; + jpp.set("CONST_COEFF", cCon); + + std::vector cLin = jpp.getDoubleArray("LIN_COEFF"); + if (cLin.size() <= comp) + cLin.resize(comp + 1); + cLin[comp] = lin; + jpp.set("LIN_COEFF", cLin); + + std::vector cQuad = jpp.getDoubleArray("QUAD_COEFF"); + if (cQuad.size() <= comp) + cQuad.resize(comp + 1); + cQuad[comp] = quad; + jpp.set("QUAD_COEFF", cQuad); + + std::vector cCube = jpp.getDoubleArray("CUBE_COEFF"); + if (cCube.size() <= comp) + cCube.resize(comp + 1); + cCube[comp] = cub; + jpp.set("CUBE_COEFF", cCube); + + jpp.popScope(); +} + +void setSectionTimes(cadet::JsonParameterProvider& jpp, const std::vector& secTimes) +{ + jpp.pushScope("solver"); + jpp.pushScope("sections"); - void setSectionTimes(cadet::JsonParameterProvider& jpp, const std::vector& secTimes) - { - jpp.pushScope("solver"); - jpp.pushScope("sections"); + jpp.set("SECTION_TIMES", secTimes); - jpp.set("SECTION_TIMES", secTimes); + jpp.popScope(); + jpp.popScope(); +} - jpp.popScope(); - jpp.popScope(); - } +void addBoundStates(cadet::JsonParameterProvider& jpp, const std::vector& nBound, double porosity) +{ + auto gs = util::makeModelGroupScope(jpp); - void addBoundStates(cadet::JsonParameterProvider& jpp, const std::vector& nBound, double porosity) - { - auto gs = util::makeModelGroupScope(jpp); + jpp.set("NBOUND", nBound); + jpp.set("POROSITY", porosity); +} - jpp.set("NBOUND", nBound); - jpp.set("POROSITY", porosity); - } +void addDummyBindingModel(cadet::JsonParameterProvider& jpp) +{ + auto gs = util::makeModelGroupScope(jpp); + jpp.set("ADSORPTION_MODEL", "NONE"); +} - void addDummyBindingModel(cadet::JsonParameterProvider& jpp) - { - auto gs = util::makeModelGroupScope(jpp); - jpp.set("ADSORPTION_MODEL", "NONE"); - } +void addLinearBindingModel(cadet::JsonParameterProvider& jpp, bool kinetic, const std::vector& kA, + const std::vector& kD) +{ + auto gs = util::makeModelGroupScope(jpp); - void addLinearBindingModel(cadet::JsonParameterProvider& jpp, bool kinetic, const std::vector& kA, const std::vector& kD) - { - auto gs = util::makeModelGroupScope(jpp); + jpp.set("ADSORPTION_MODEL", "LINEAR"); - jpp.set("ADSORPTION_MODEL", "LINEAR"); + jpp.addScope("adsorption"); + jpp.pushScope("adsorption"); - jpp.addScope("adsorption"); - jpp.pushScope("adsorption"); + jpp.set("IS_KINETIC", kinetic); + jpp.set("LIN_KA", kA); + jpp.set("LIN_KD", kD); - jpp.set("IS_KINETIC", kinetic); - jpp.set("LIN_KA", kA); - jpp.set("LIN_KD", kD); + jpp.popScope(); +} - jpp.popScope(); - } +void addSMABindingModel(cadet::JsonParameterProvider& jpp, bool kinetic, double lambda, const std::vector& kA, + const std::vector& kD, const std::vector& nu, const std::vector& sigma) +{ + auto gs = util::makeModelGroupScope(jpp); - void addSMABindingModel(cadet::JsonParameterProvider& jpp, bool kinetic, double lambda, const std::vector& kA, const std::vector& kD, const std::vector& nu, const std::vector& sigma) - { - auto gs = util::makeModelGroupScope(jpp); + jpp.set("ADSORPTION_MODEL", "STERIC_MASS_ACTION"); - jpp.set("ADSORPTION_MODEL", "STERIC_MASS_ACTION"); + jpp.addScope("adsorption"); + jpp.pushScope("adsorption"); - jpp.addScope("adsorption"); - jpp.pushScope("adsorption"); + jpp.set("IS_KINETIC", kinetic); + jpp.set("SMA_LAMBDA", lambda); + jpp.set("SMA_KA", kA); + jpp.set("SMA_KD", kD); + jpp.set("SMA_NU", nu); + jpp.set("SMA_SIGMA", sigma); - jpp.set("IS_KINETIC", kinetic); - jpp.set("SMA_LAMBDA", lambda); - jpp.set("SMA_KA", kA); - jpp.set("SMA_KD", kD); - jpp.set("SMA_NU", nu); - jpp.set("SMA_SIGMA", sigma); + jpp.popScope(); +} - jpp.popScope(); - } +void addLangmuirBindingModel(cadet::JsonParameterProvider& jpp, bool kinetic, const std::vector& kA, + const std::vector& kD, const std::vector& qMax) +{ + auto gs = util::makeModelGroupScope(jpp); - void addLangmuirBindingModel(cadet::JsonParameterProvider& jpp, bool kinetic, const std::vector& kA, const std::vector& kD, const std::vector& qMax) - { - auto gs = util::makeModelGroupScope(jpp); + jpp.set("ADSORPTION_MODEL", "MULTI_COMPONENT_LANGMUIR"); - jpp.set("ADSORPTION_MODEL", "MULTI_COMPONENT_LANGMUIR"); + jpp.addScope("adsorption"); + jpp.pushScope("adsorption"); - jpp.addScope("adsorption"); - jpp.pushScope("adsorption"); + jpp.set("IS_KINETIC", kinetic); + jpp.set("MCL_KA", kA); + jpp.set("MCL_KD", kD); + jpp.set("MCL_QMAX", qMax); - jpp.set("IS_KINETIC", kinetic); - jpp.set("MCL_KA", kA); - jpp.set("MCL_KD", kD); - jpp.set("MCL_QMAX", qMax); + jpp.popScope(); +} - jpp.popScope(); - } +void setBindingMode(cadet::JsonParameterProvider& jpp, bool isKinetic) +{ + auto ms = util::makeOptionalGroupScope(jpp, "model"); + auto us = util::makeOptionalGroupScope(jpp, "unit_000"); - void setBindingMode(cadet::JsonParameterProvider& jpp, bool isKinetic) - { - auto ms = util::makeOptionalGroupScope(jpp, "model"); - auto us = util::makeOptionalGroupScope(jpp, "unit_000"); + jpp.pushScope("adsorption"); + jpp.set("IS_KINETIC", isKinetic); + jpp.popScope(); +} - jpp.pushScope("adsorption"); - jpp.set("IS_KINETIC", isKinetic); - jpp.popScope(); - } +void addSensitivity(cadet::JsonParameterProvider& jpp, const std::string& name, const ParameterId& id, double absTol) +{ + jpp.addScope("sensitivity"); + jpp.pushScope("sensitivity"); - void addSensitivity(cadet::JsonParameterProvider& jpp, const std::string& name, const ParameterId& id, double absTol) + jpp.set("SENS_METHOD", "ad1"); + int sensIdx = 0; + if (jpp.exists("NSENS")) { - jpp.addScope("sensitivity"); - jpp.pushScope("sensitivity"); - - jpp.set("SENS_METHOD", "ad1"); - int sensIdx = 0; - if (jpp.exists("NSENS")) - { - sensIdx = jpp.getInt("NSENS"); - } - jpp.set("NSENS", sensIdx + 1); - - std::ostringstream ss; - ss << "param_" << std::setfill('0') << std::setw(3) << sensIdx; - jpp.addScope(ss.str()); - jpp.pushScope(ss.str()); - - jpp.set("SENS_UNIT", std::vector(1, id.unitOperation)); - jpp.set("SENS_NAME", std::vector(1, name)); - jpp.set("SENS_COMP", std::vector(1, id.component)); - jpp.set("SENS_BOUNDPHASE", std::vector(1, id.boundState)); - jpp.set("SENS_REACTION", std::vector(1, id.reaction)); - jpp.set("SENS_SECTION", std::vector(1, id.section)); - jpp.set("SENS_PARTYPE", std::vector(1, id.particleType)); - jpp.set("SENS_FACTOR", std::vector(1, 1.0)); - jpp.set("SENS_ABSTOL", absTol); - - jpp.popScope(); - jpp.popScope(); + sensIdx = jpp.getInt("NSENS"); } + jpp.set("NSENS", sensIdx + 1); + + std::ostringstream ss; + ss << "param_" << std::setfill('0') << std::setw(3) << sensIdx; + jpp.addScope(ss.str()); + jpp.pushScope(ss.str()); + + jpp.set("SENS_UNIT", std::vector(1, id.unitOperation)); + jpp.set("SENS_NAME", std::vector(1, name)); + jpp.set("SENS_COMP", std::vector(1, id.component)); + jpp.set("SENS_BOUNDPHASE", std::vector(1, id.boundState)); + jpp.set("SENS_REACTION", std::vector(1, id.reaction)); + jpp.set("SENS_SECTION", std::vector(1, id.section)); + jpp.set("SENS_PARTYPE", std::vector(1, id.particleType)); + jpp.set("SENS_FACTOR", std::vector(1, 1.0)); + jpp.set("SENS_ABSTOL", absTol); + + jpp.popScope(); + jpp.popScope(); +} + +void returnSensitivities(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, bool inlet) +{ + jpp.pushScope("return"); - void returnSensitivities(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, bool inlet) - { - jpp.pushScope("return"); - - std::ostringstream ss; - ss << "unit_" << std::setfill('0') << std::setw(3) << unit; - jpp.pushScope(ss.str()); - - jpp.set("WRITE_SENS_INLET", inlet); - jpp.set("WRITE_SENS_OUTLET", true); - jpp.set("WRITE_SENS_VOLUME", true); - jpp.popScope(); - jpp.popScope(); - } + std::ostringstream ss; + ss << "unit_" << std::setfill('0') << std::setw(3) << unit; + jpp.pushScope(ss.str()); - void disableSensitivityErrorTest(cadet::JsonParameterProvider& jpp, bool isDisabled) - { - jpp.pushScope("solver"); - jpp.pushScope("time_integrator"); - jpp.set("ERRORTEST_SENS", !isDisabled); - jpp.popScope(); - jpp.popScope(); - } + jpp.set("WRITE_SENS_INLET", inlet); + jpp.set("WRITE_SENS_OUTLET", true); + jpp.set("WRITE_SENS_VOLUME", true); + jpp.popScope(); + jpp.popScope(); +} - void setMaxStepSize(cadet::JsonParameterProvider& jpp, double maxStepSize) - { - jpp.pushScope("solver"); - jpp.pushScope("time_integrator"); - jpp.set("MAX_STEP_SIZE", std::max(maxStepSize, 0.0)); - jpp.popScope(); - jpp.popScope(); - } +void disableSensitivityErrorTest(cadet::JsonParameterProvider& jpp, bool isDisabled) +{ + jpp.pushScope("solver"); + jpp.pushScope("time_integrator"); + jpp.set("ERRORTEST_SENS", !isDisabled); + jpp.popScope(); + jpp.popScope(); +} + +void setMaxStepSize(cadet::JsonParameterProvider& jpp, double maxStepSize) +{ + jpp.pushScope("solver"); + jpp.pushScope("time_integrator"); + jpp.set("MAX_STEP_SIZE", std::max(maxStepSize, 0.0)); + jpp.popScope(); + jpp.popScope(); +} } // namespace test } // namespace cadet diff --git a/test/SimHelper.hpp b/test/SimHelper.hpp index 8cdb6e30b..178948afe 100644 --- a/test/SimHelper.hpp +++ b/test/SimHelper.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines helper functions for building simulations. */ @@ -29,163 +29,169 @@ namespace cadet namespace test { - /** - * @brief Sets the number of components for a unit operation - * @details Overwrites the NCOMP field of the given ParameterProvider. - * @param [in,out] jpp ParameterProvider - * @param [in] unit Index of unit operation - * @param [in] nComp Number of components - */ - void setNumberOfComponents(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, int nComp); - - /** - * @brief Sets the initial conditions of a CSTR - * @details Overwrites the INIT_C, INIT_Q, and INIT_VOLUME fields of the given ParameterProvider. - * @param [in,out] jpp ParameterProvider - * @param [in] c Initial liquid phase concentration - * @param [in] q Initial bound phase concentration - * @param [in] v Initial volume - */ - void setInitialConditions(cadet::JsonParameterProvider& jpp, const std::vector& c, const std::vector& q, double v); - - /** - * @brief Sets the initial conditions of a column - * @details Overwrites the INIT_C, INIT_CP, and INIT_Q fields of the given ParameterProvider. - * @param [in,out] jpp ParameterProvider - * @param [in] c Initial liquid phase concentration - * @param [in] cp Initial bead liquid phase concentration - * @param [in] q Initial bound phase concentration - */ - void setInitialConditions(cadet::JsonParameterProvider& jpp, const std::vector& c, const std::vector& cp, const std::vector& q); - - /** - * @brief Sets all flow rates of a section - * @details Sets inflow and outflow as well as filter flow rate. - * @param [in,out] jpp ParameterProvider - * @param [in] secIdx Section index - * @param [in] in Inflow rate - * @param [in] out Outflow rate - * @param [in] filter Filter flow rate - */ - void setFlowRates(cadet::JsonParameterProvider& jpp, unsigned int secIdx, double in, double out, double filter); - - /** - * @brief Sets the filter flow rate of a section - * @details Sets filter flow rate. - * @param [in,out] jpp ParameterProvider - * @param [in] secIdx Section index - * @param [in] filter Filter flow rate - */ - void setFlowRates(cadet::JsonParameterProvider& jpp, unsigned int secIdx, double filter); - - /** - * @brief Sets the inlet profile of a section and component - * @details Overwrites the spline piece of the given section and component. - * @param [in,out] jpp ParameterProvider - * @param [in] secIdx Section index - * @param [in] comp Component index - * @param [in] con Constant polynomial coefficient - * @param [in] lin Linear polynomial coefficient - * @param [in] quad Quadratic polynomial coefficient - * @param [in] cub Cubic polynomial coefficient - */ - void setInletProfile(cadet::JsonParameterProvider& jpp, unsigned int secIdx, unsigned int comp, double con, double lin, double quad, double cub); - - /** - * @brief Sets the section times - * @details Overwrites the SECTION_TIMES field of the given ParameterProvider. - * @param [in,out] jpp ParameterProvider - * @param [in] secTimes Section times vector - */ - void setSectionTimes(cadet::JsonParameterProvider& jpp, const std::vector& secTimes); - - /** - * @brief Adds bound states to a CSTR model - * @param [in,out] jpp ParameterProvider - * @param [in] nBound Array with number of bound states for each component - * @param [in] porosity Porosity - */ - void addBoundStates(cadet::JsonParameterProvider& jpp, const std::vector& nBound, double porosity); - - /** - * @brief Adds dummy binding model to a CSTR model - * @param [in,out] jpp ParameterProvider - */ - void addDummyBindingModel(cadet::JsonParameterProvider& jpp); - - /** - * @brief Adds linear binding model to a CSTR model - * @param [in,out] jpp ParameterProvider - * @param [in] kinetic Determines whether kinetic or quasi-stationary binding is used - * @param [in] kA Vector with kA rates - * @param [in] kD Vector with kD rates - */ - void addLinearBindingModel(cadet::JsonParameterProvider& jpp, bool kinetic, const std::vector& kA, const std::vector& kD); - - /** - * @brief Adds SMA binding model to a CSTR model - * @param [in,out] jpp ParameterProvider - * @param [in] kinetic Determines whether kinetic or quasi-stationary binding is used - * @param [in] lambda Ionic capacity Lambda - * @param [in] kA Vector with kA rates - * @param [in] kD Vector with kD rates - * @param [in] nu Vector with characteristic charges nu - * @param [in] sigma Vector with steric factors sigma - */ - void addSMABindingModel(cadet::JsonParameterProvider& jpp, bool kinetic, double lambda, const std::vector& kA, const std::vector& kD, const std::vector& nu, const std::vector& sigma); - - /** - * @brief Adds Langmuir binding model to a CSTR model - * @param [in,out] jpp ParameterProvider - * @param [in] kinetic Determines whether kinetic or quasi-stationary binding is used - * @param [in] kA Vector with kA rates - * @param [in] kD Vector with kD rates - * @param [in] qMax Vector with maximum capacities qMax - */ - void addLangmuirBindingModel(cadet::JsonParameterProvider& jpp, bool kinetic, const std::vector& kA, const std::vector& kD, const std::vector& qMax); - - /** - * @brief Sets the binding mode of a binding model - * @details Changes IS_KINETIC field in the adsorption group - * @param [in,out] jpp ParameterProvider - * @param [in] isKinetic Determines whether kinetic or quasi-stationary binding is used - */ - void setBindingMode(cadet::JsonParameterProvider& jpp, bool isKinetic); - - /** - * @brief Adds a parameter sensitivity - * @param [in,out] jpp ParameterProvider - * @param [in] name Parameter name - * @param [in] id Parameter ID - * @param [in] absTol Absolute tolerance - */ - void addSensitivity(cadet::JsonParameterProvider& jpp, const std::string& name, const ParameterId& id, double absTol); - - /** - * @brief Adds return info for parameter sensitivities to parameter provider - * @param [in,out] jpp ParameterProvider - * @param [in] unit Index of unit operation - * @param [in] inlet Determines whether sensitivity at inlet is returned (@c true) or not (@c false) - */ - void returnSensitivities(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, bool inlet = false); - - /** - * @brief Disables error test of sensitivities - * @details Sensitivities are included in the local error test by default. - * @param [in,out] jpp ParameterProvider - * @param [in] isDisabled Determines whether the error test for sensitivities is disabled (@c true) or not (@c false) - */ - void disableSensitivityErrorTest(cadet::JsonParameterProvider& jpp, bool isDisabled = true); - - /** - * @brief Sets maximum step size in time integration - * @details Default is @c 0 which means unlimited step size. - * @param [in,out] jpp ParameterProvider - * @param [in] maxStepSize Maximum time step size - */ - void setMaxStepSize(cadet::JsonParameterProvider& jpp, double maxStepSize); +/** + * @brief Sets the number of components for a unit operation + * @details Overwrites the NCOMP field of the given ParameterProvider. + * @param [in,out] jpp ParameterProvider + * @param [in] unit Index of unit operation + * @param [in] nComp Number of components + */ +void setNumberOfComponents(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, int nComp); + +/** + * @brief Sets the initial conditions of a CSTR + * @details Overwrites the INIT_C, INIT_Q, and INIT_VOLUME fields of the given ParameterProvider. + * @param [in,out] jpp ParameterProvider + * @param [in] c Initial liquid phase concentration + * @param [in] q Initial bound phase concentration + * @param [in] v Initial volume + */ +void setInitialConditions(cadet::JsonParameterProvider& jpp, const std::vector& c, const std::vector& q, + double v); + +/** + * @brief Sets the initial conditions of a column + * @details Overwrites the INIT_C, INIT_CP, and INIT_Q fields of the given ParameterProvider. + * @param [in,out] jpp ParameterProvider + * @param [in] c Initial liquid phase concentration + * @param [in] cp Initial bead liquid phase concentration + * @param [in] q Initial bound phase concentration + */ +void setInitialConditions(cadet::JsonParameterProvider& jpp, const std::vector& c, + const std::vector& cp, const std::vector& q); + +/** + * @brief Sets all flow rates of a section + * @details Sets inflow and outflow as well as filter flow rate. + * @param [in,out] jpp ParameterProvider + * @param [in] secIdx Section index + * @param [in] in Inflow rate + * @param [in] out Outflow rate + * @param [in] filter Filter flow rate + */ +void setFlowRates(cadet::JsonParameterProvider& jpp, unsigned int secIdx, double in, double out, double filter); + +/** + * @brief Sets the filter flow rate of a section + * @details Sets filter flow rate. + * @param [in,out] jpp ParameterProvider + * @param [in] secIdx Section index + * @param [in] filter Filter flow rate + */ +void setFlowRates(cadet::JsonParameterProvider& jpp, unsigned int secIdx, double filter); + +/** + * @brief Sets the inlet profile of a section and component + * @details Overwrites the spline piece of the given section and component. + * @param [in,out] jpp ParameterProvider + * @param [in] secIdx Section index + * @param [in] comp Component index + * @param [in] con Constant polynomial coefficient + * @param [in] lin Linear polynomial coefficient + * @param [in] quad Quadratic polynomial coefficient + * @param [in] cub Cubic polynomial coefficient + */ +void setInletProfile(cadet::JsonParameterProvider& jpp, unsigned int secIdx, unsigned int comp, double con, double lin, + double quad, double cub); + +/** + * @brief Sets the section times + * @details Overwrites the SECTION_TIMES field of the given ParameterProvider. + * @param [in,out] jpp ParameterProvider + * @param [in] secTimes Section times vector + */ +void setSectionTimes(cadet::JsonParameterProvider& jpp, const std::vector& secTimes); + +/** + * @brief Adds bound states to a CSTR model + * @param [in,out] jpp ParameterProvider + * @param [in] nBound Array with number of bound states for each component + * @param [in] porosity Porosity + */ +void addBoundStates(cadet::JsonParameterProvider& jpp, const std::vector& nBound, double porosity); + +/** + * @brief Adds dummy binding model to a CSTR model + * @param [in,out] jpp ParameterProvider + */ +void addDummyBindingModel(cadet::JsonParameterProvider& jpp); + +/** + * @brief Adds linear binding model to a CSTR model + * @param [in,out] jpp ParameterProvider + * @param [in] kinetic Determines whether kinetic or quasi-stationary binding is used + * @param [in] kA Vector with kA rates + * @param [in] kD Vector with kD rates + */ +void addLinearBindingModel(cadet::JsonParameterProvider& jpp, bool kinetic, const std::vector& kA, + const std::vector& kD); + +/** + * @brief Adds SMA binding model to a CSTR model + * @param [in,out] jpp ParameterProvider + * @param [in] kinetic Determines whether kinetic or quasi-stationary binding is used + * @param [in] lambda Ionic capacity Lambda + * @param [in] kA Vector with kA rates + * @param [in] kD Vector with kD rates + * @param [in] nu Vector with characteristic charges nu + * @param [in] sigma Vector with steric factors sigma + */ +void addSMABindingModel(cadet::JsonParameterProvider& jpp, bool kinetic, double lambda, const std::vector& kA, + const std::vector& kD, const std::vector& nu, const std::vector& sigma); + +/** + * @brief Adds Langmuir binding model to a CSTR model + * @param [in,out] jpp ParameterProvider + * @param [in] kinetic Determines whether kinetic or quasi-stationary binding is used + * @param [in] kA Vector with kA rates + * @param [in] kD Vector with kD rates + * @param [in] qMax Vector with maximum capacities qMax + */ +void addLangmuirBindingModel(cadet::JsonParameterProvider& jpp, bool kinetic, const std::vector& kA, + const std::vector& kD, const std::vector& qMax); + +/** + * @brief Sets the binding mode of a binding model + * @details Changes IS_KINETIC field in the adsorption group + * @param [in,out] jpp ParameterProvider + * @param [in] isKinetic Determines whether kinetic or quasi-stationary binding is used + */ +void setBindingMode(cadet::JsonParameterProvider& jpp, bool isKinetic); + +/** + * @brief Adds a parameter sensitivity + * @param [in,out] jpp ParameterProvider + * @param [in] name Parameter name + * @param [in] id Parameter ID + * @param [in] absTol Absolute tolerance + */ +void addSensitivity(cadet::JsonParameterProvider& jpp, const std::string& name, const ParameterId& id, double absTol); + +/** + * @brief Adds return info for parameter sensitivities to parameter provider + * @param [in,out] jpp ParameterProvider + * @param [in] unit Index of unit operation + * @param [in] inlet Determines whether sensitivity at inlet is returned (@c true) or not (@c false) + */ +void returnSensitivities(cadet::JsonParameterProvider& jpp, UnitOpIdx unit, bool inlet = false); + +/** + * @brief Disables error test of sensitivities + * @details Sensitivities are included in the local error test by default. + * @param [in,out] jpp ParameterProvider + * @param [in] isDisabled Determines whether the error test for sensitivities is disabled (@c true) or not (@c false) + */ +void disableSensitivityErrorTest(cadet::JsonParameterProvider& jpp, bool isDisabled = true); + +/** + * @brief Sets maximum step size in time integration + * @details Default is @c 0 which means unlimited step size. + * @param [in,out] jpp ParameterProvider + * @param [in] maxStepSize Maximum time step size + */ +void setMaxStepSize(cadet::JsonParameterProvider& jpp, double maxStepSize); } // namespace test } // namespace cadet -#endif // CADETTEST_SIMHELPER_HPP_ +#endif // CADETTEST_SIMHELPER_HPP_ diff --git a/test/SparseFactorizableMatrix.cpp b/test/SparseFactorizableMatrix.cpp index 06c07046e..355a3affb 100644 --- a/test/SparseFactorizableMatrix.cpp +++ b/test/SparseFactorizableMatrix.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -21,231 +21,228 @@ #include "linalg/DenseMatrix.hpp" #ifdef SUPERLU_FOUND - #include "linalg/SuperLUSparseMatrix.hpp" +#include "linalg/SuperLUSparseMatrix.hpp" #endif #ifdef UMFPACK_FOUND - #include "linalg/UMFPackSparseMatrix.hpp" +#include "linalg/UMFPackSparseMatrix.hpp" #endif namespace { - cadet::linalg::CompressedSparseMatrix createSmallMatrix() +cadet::linalg::CompressedSparseMatrix createSmallMatrix() +{ + /* + Matrix pattern + . X . X . . . . . X + X . X . . . . . . . + . X . X . . . . . . + . . X . X . . . . . + . . . X . X . . . . + . . . . X . X . . . + . . . . . X . X . . + . . . . . . X . X . + . . . . . . . X . X + X . . . . . . . X . + */ + + // Create pattern + cadet::linalg::SparsityPattern pattern(10, 2); + cadet::linalg::SparsityPatternRowIterator ri = pattern.row(0); + for (int i = 0; i < pattern.rows(); ++i) { - /* - Matrix pattern - . X . X . . . . . X - X . X . . . . . . . - . X . X . . . . . . - . . X . X . . . . . - . . . X . X . . . . - . . . . X . X . . . - . . . . . X . X . . - . . . . . . X . X . - . . . . . . . X . X - X . . . . . . . X . - */ - - // Create pattern - cadet::linalg::SparsityPattern pattern(10, 2); - cadet::linalg::SparsityPatternRowIterator ri = pattern.row(0); - for (int i = 0; i < pattern.rows(); ++i) - { - if (i > 0) - ri[-1] = 1.0; - if (i < pattern.rows() - 1) - ri[1] = 1.0; - ++ri; - } - pattern.add(0, 3); - pattern.add(0, 9); - pattern.add(9, 0); - - REQUIRE(pattern.numNonZeros() == 21); + if (i > 0) + ri[-1] = 1.0; + if (i < pattern.rows() - 1) + ri[1] = 1.0; + ++ri; + } + pattern.add(0, 3); + pattern.add(0, 9); + pattern.add(9, 0); - // Create matrix with pattern - cadet::linalg::CompressedSparseMatrix mat(pattern); - REQUIRE(mat.numNonZeros() == pattern.numNonZeros()); - REQUIRE(mat.rows() == pattern.rows()); + REQUIRE(pattern.numNonZeros() == 21); - // Assign some data - double* const data = mat.data(); - for (int i = 0; i < pattern.numNonZeros(); ++i) - data[i] = i + 1.0; + // Create matrix with pattern + cadet::linalg::CompressedSparseMatrix mat(pattern); + REQUIRE(mat.numNonZeros() == pattern.numNonZeros()); + REQUIRE(mat.rows() == pattern.rows()); - return mat; - } + // Assign some data + double* const data = mat.data(); + for (int i = 0; i < pattern.numNonZeros(); ++i) + data[i] = i + 1.0; - inline bool isNonZeroInSmallMatrix(int row, int diag) - { - if ((diag + row < 0) || (diag + row >= 10)) - return false; - - if ((diag == -1) || (diag == 1)) - return true; - if ((row == 0) && (diag == 9)) - return true; - if ((row == 9) && (diag == -9)) - return true; - if ((row == 0) && (diag == 3)) - return true; + return mat; +} +inline bool isNonZeroInSmallMatrix(int row, int diag) +{ + if ((diag + row < 0) || (diag + row >= 10)) return false; - } - inline cadet::linalg::DenseMatrix createSmallMatrixDense() - { - cadet::linalg::DenseMatrix dm; - dm.resize(10, 10); - dm.setAll(0.0); + if ((diag == -1) || (diag == 1)) + return true; + if ((row == 0) && (diag == 9)) + return true; + if ((row == 9) && (diag == -9)) + return true; + if ((row == 0) && (diag == 3)) + return true; + + return false; +} + +inline cadet::linalg::DenseMatrix createSmallMatrixDense() +{ + cadet::linalg::DenseMatrix dm; + dm.resize(10, 10); + dm.setAll(0.0); - double curVal = 1.0; - for (int row = 0; row < 10; ++row) + double curVal = 1.0; + for (int row = 0; row < 10; ++row) + { + for (int diag = -row; diag < 10 - row; ++diag) { - for (int diag = -row; diag < 10 - row; ++diag) + if (isNonZeroInSmallMatrix(row, diag)) { - if (isNonZeroInSmallMatrix(row, diag)) - { - dm.diagonalElement(row, diag) = curVal; - curVal += 1.0; - } + dm.diagonalElement(row, diag) = curVal; + curVal += 1.0; } } - return dm; } + return dm; +} - template - inline void checkSparseAgainstDenseInverse(matrix_t& factMat, cadet::linalg::DenseMatrix& dm) +template +inline void checkSparseAgainstDenseInverse(matrix_t& factMat, cadet::linalg::DenseMatrix& dm) +{ + const int numRows = static_cast(factMat.rows()); + std::vector rhs1(factMat.rows(), 0.0); + std::vector rhs2(factMat.rows(), 0.0); + for (int col = 0; col < numRows; ++col) { - const int numRows = static_cast(factMat.rows()); - std::vector rhs1(factMat.rows(), 0.0); - std::vector rhs2(factMat.rows(), 0.0); - for (int col = 0; col < numRows; ++col) - { - std::fill(rhs1.begin(), rhs1.end(), 0.0); - std::fill(rhs2.begin(), rhs2.end(), 0.0); - rhs1[col] = 1.0; - rhs2[col] = 1.0; + std::fill(rhs1.begin(), rhs1.end(), 0.0); + std::fill(rhs2.begin(), rhs2.end(), 0.0); + rhs1[col] = 1.0; + rhs2[col] = 1.0; - factMat.solve(rhs1.data()); - dm.solve(rhs2.data()); + factMat.solve(rhs1.data()); + dm.solve(rhs2.data()); - for (int row = 0; row < numRows; ++row) - { - CAPTURE(row); - CAPTURE(col); - CHECK(rhs1[row] == cadet::test::makeApprox(rhs2[row], std::numeric_limits::epsilon() * 100.0, 0.0)); - } + for (int row = 0; row < numRows; ++row) + { + CAPTURE(row); + CAPTURE(col); + CHECK(rhs1[row] == cadet::test::makeApprox(rhs2[row], std::numeric_limits::epsilon() * 100.0, 0.0)); } } +} - template - inline void sparseMatrixFactorize() - { - // Create matrix with pattern - cadet::linalg::CompressedSparseMatrix mat = createSmallMatrix(); +template inline void sparseMatrixFactorize() +{ + // Create matrix with pattern + cadet::linalg::CompressedSparseMatrix mat = createSmallMatrix(); - matrix_t factMat; - factMat.assignPattern(mat); - factMat.copyFromSamePattern(mat); + matrix_t factMat; + factMat.assignPattern(mat); + factMat.copyFromSamePattern(mat); - factMat.prepare(); - REQUIRE(factMat.factorize()); - } + factMat.prepare(); + REQUIRE(factMat.factorize()); +} - template - inline void sparseMatrixInverse() - { - // Create matrix with pattern - cadet::linalg::CompressedSparseMatrix mat = createSmallMatrix(); +template inline void sparseMatrixInverse() +{ + // Create matrix with pattern + cadet::linalg::CompressedSparseMatrix mat = createSmallMatrix(); - // Create equivalent dense matrix - cadet::linalg::DenseMatrix dm = createSmallMatrixDense(); + // Create equivalent dense matrix + cadet::linalg::DenseMatrix dm = createSmallMatrixDense(); - matrix_t factMat; - factMat.assignPattern(mat); - factMat.copyFromSamePattern(mat); + matrix_t factMat; + factMat.assignPattern(mat); + factMat.copyFromSamePattern(mat); - factMat.prepare(); - REQUIRE(factMat.factorize()); + factMat.prepare(); + REQUIRE(factMat.factorize()); - REQUIRE(dm.factorize()); + REQUIRE(dm.factorize()); - checkSparseAgainstDenseInverse(factMat, dm); - } + checkSparseAgainstDenseInverse(factMat, dm); +} - template - inline void sparseMatrixRepeatedFactorization() - { - // Create matrix with pattern - cadet::linalg::CompressedSparseMatrix mat = createSmallMatrix(); +template inline void sparseMatrixRepeatedFactorization() +{ + // Create matrix with pattern + cadet::linalg::CompressedSparseMatrix mat = createSmallMatrix(); - // Create equivalent dense matrix - cadet::linalg::DenseMatrix dm = createSmallMatrixDense(); + // Create equivalent dense matrix + cadet::linalg::DenseMatrix dm = createSmallMatrixDense(); - matrix_t factMat; - factMat.assignPattern(mat); - factMat.copyFromSamePattern(mat); + matrix_t factMat; + factMat.assignPattern(mat); + factMat.copyFromSamePattern(mat); - factMat.prepare(); - REQUIRE(factMat.factorize()); + factMat.prepare(); + REQUIRE(factMat.factorize()); - REQUIRE(dm.factorize()); + REQUIRE(dm.factorize()); - checkSparseAgainstDenseInverse(factMat, dm); + checkSparseAgainstDenseInverse(factMat, dm); - // Modify matrices - double* const vs = factMat.valuesOfRow(0); - for (int i = 0; i < factMat.numNonZeros(); ++i) - vs[i] *= 2.0; + // Modify matrices + double* const vs = factMat.valuesOfRow(0); + for (int i = 0; i < factMat.numNonZeros(); ++i) + vs[i] *= 2.0; - dm = createSmallMatrixDense(); - double* const vd = dm.data(); - for (int i = 0; i < dm.rows() * dm.columns(); ++i) - vd[i] *= 2.0; + dm = createSmallMatrixDense(); + double* const vd = dm.data(); + for (int i = 0; i < dm.rows() * dm.columns(); ++i) + vd[i] *= 2.0; - // Factorize again - REQUIRE(factMat.factorize()); - REQUIRE(dm.factorize()); + // Factorize again + REQUIRE(factMat.factorize()); + REQUIRE(dm.factorize()); - checkSparseAgainstDenseInverse(factMat, dm); - } + checkSparseAgainstDenseInverse(factMat, dm); } +} // namespace #ifdef SUPERLU_FOUND - TEST_CASE("SuperLU sparse matrix factorize", "[SuperLU],[SparseMatrix],[LinAlg]") - { - sparseMatrixFactorize(); - } +TEST_CASE("SuperLU sparse matrix factorize", "[SuperLU],[SparseMatrix],[LinAlg]") +{ + sparseMatrixFactorize(); +} - TEST_CASE("SuperLU sparse matrix inverse", "[SuperLU],[SparseMatrix],[LinAlg]") - { - sparseMatrixInverse(); - } +TEST_CASE("SuperLU sparse matrix inverse", "[SuperLU],[SparseMatrix],[LinAlg]") +{ + sparseMatrixInverse(); +} - TEST_CASE("SuperLU sparse matrix repeated factorization", "[SuperLU],[SparseMatrix],[LinAlg]") - { - sparseMatrixRepeatedFactorization(); - } +TEST_CASE("SuperLU sparse matrix repeated factorization", "[SuperLU],[SparseMatrix],[LinAlg]") +{ + sparseMatrixRepeatedFactorization(); +} #endif #ifdef UMFPACK_FOUND - TEST_CASE("UMFPACK sparse matrix factorize", "[UMFPACK],[SparseMatrix],[LinAlg]") - { - sparseMatrixFactorize(); - } +TEST_CASE("UMFPACK sparse matrix factorize", "[UMFPACK],[SparseMatrix],[LinAlg]") +{ + sparseMatrixFactorize(); +} - TEST_CASE("UMFPACK sparse matrix inverse", "[UMFPACK],[SparseMatrix],[LinAlg]") - { - sparseMatrixInverse(); - } +TEST_CASE("UMFPACK sparse matrix inverse", "[UMFPACK],[SparseMatrix],[LinAlg]") +{ + sparseMatrixInverse(); +} - TEST_CASE("UMFPACK sparse matrix repeated factorization", "[UMFPACK],[SparseMatrix],[LinAlg]") - { - sparseMatrixRepeatedFactorization(); - } +TEST_CASE("UMFPACK sparse matrix repeated factorization", "[UMFPACK],[SparseMatrix],[LinAlg]") +{ + sparseMatrixRepeatedFactorization(); +} #endif diff --git a/test/SparseMatrix.cpp b/test/SparseMatrix.cpp index 129de58b2..529c3f60c 100644 --- a/test/SparseMatrix.cpp +++ b/test/SparseMatrix.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -23,90 +23,90 @@ namespace { - cadet::linalg::CompressedSparseMatrix createSmallMatrix() +cadet::linalg::CompressedSparseMatrix createSmallMatrix() +{ + /* + Matrix pattern + . X . X . . . . . X + X . X . . . . . . . + . X . X . . . . . . + . . X . X . . . . . + . . . X . X . . . . + . . . . X . X . . . + . . . . . X . X . . + . . . . . . X . X . + . . . . . . . X . X + X . . . . . . . X . + */ + + // Create pattern + cadet::linalg::SparsityPattern pattern(10, 2); + cadet::linalg::SparsityPatternRowIterator ri = pattern.row(0); + for (int i = 0; i < pattern.rows(); ++i) { - /* - Matrix pattern - . X . X . . . . . X - X . X . . . . . . . - . X . X . . . . . . - . . X . X . . . . . - . . . X . X . . . . - . . . . X . X . . . - . . . . . X . X . . - . . . . . . X . X . - . . . . . . . X . X - X . . . . . . . X . - */ - - // Create pattern - cadet::linalg::SparsityPattern pattern(10, 2); - cadet::linalg::SparsityPatternRowIterator ri = pattern.row(0); - for (int i = 0; i < pattern.rows(); ++i) - { - if (i > 0) - ri[-1] = 1.0; - if (i < pattern.rows() - 1) - ri[1] = 1.0; - ++ri; - } - pattern.add(0, 3); - pattern.add(0, 9); - pattern.add(9, 0); - - REQUIRE(pattern.numNonZeros() == 21); + if (i > 0) + ri[-1] = 1.0; + if (i < pattern.rows() - 1) + ri[1] = 1.0; + ++ri; + } + pattern.add(0, 3); + pattern.add(0, 9); + pattern.add(9, 0); - // Create matrix with pattern - cadet::linalg::CompressedSparseMatrix mat(pattern); - REQUIRE(mat.numNonZeros() == pattern.numNonZeros()); - REQUIRE(mat.rows() == pattern.rows()); + REQUIRE(pattern.numNonZeros() == 21); - // Assign some data - double* const data = mat.data(); - for (int i = 0; i < pattern.numNonZeros(); ++i) - data[i] = i + 1.0; + // Create matrix with pattern + cadet::linalg::CompressedSparseMatrix mat(pattern); + REQUIRE(mat.numNonZeros() == pattern.numNonZeros()); + REQUIRE(mat.rows() == pattern.rows()); - return mat; - } + // Assign some data + double* const data = mat.data(); + for (int i = 0; i < pattern.numNonZeros(); ++i) + data[i] = i + 1.0; - inline bool isNonZeroInSmallMatrix(int row, int diag) - { - if ((diag + row < 0) || (diag + row >= 10)) - return false; - - if ((diag == -1) || (diag == 1)) - return true; - if ((row == 0) && (diag == 9)) - return true; - if ((row == 9) && (diag == -9)) - return true; - if ((row == 0) && (diag == 3)) - return true; + return mat; +} +inline bool isNonZeroInSmallMatrix(int row, int diag) +{ + if ((diag + row < 0) || (diag + row >= 10)) return false; - } - inline cadet::linalg::DenseMatrix createSmallMatrixDense() - { - cadet::linalg::DenseMatrix dm; - dm.resize(10, 10); - dm.setAll(0.0); + if ((diag == -1) || (diag == 1)) + return true; + if ((row == 0) && (diag == 9)) + return true; + if ((row == 9) && (diag == -9)) + return true; + if ((row == 0) && (diag == 3)) + return true; - double curVal = 1.0; - for (int row = 0; row < 10; ++row) + return false; +} + +inline cadet::linalg::DenseMatrix createSmallMatrixDense() +{ + cadet::linalg::DenseMatrix dm; + dm.resize(10, 10); + dm.setAll(0.0); + + double curVal = 1.0; + for (int row = 0; row < 10; ++row) + { + for (int diag = -row; diag < 10 - row; ++diag) { - for (int diag = -row; diag < 10 - row; ++diag) + if (isNonZeroInSmallMatrix(row, diag)) { - if (isNonZeroInSmallMatrix(row, diag)) - { - dm.diagonalElement(row, diag) = curVal; - curVal += 1.0; - } + dm.diagonalElement(row, diag) = curVal; + curVal += 1.0; } } - return dm; } + return dm; } +} // namespace TEST_CASE("CompressedSparseMatrix assembly and iterator access", "[SparseMatrix],[LinAlg]") { diff --git a/test/StringHashing.cpp b/test/StringHashing.cpp index d298d895a..c9f377715 100644 --- a/test/StringHashing.cpp +++ b/test/StringHashing.cpp @@ -49,254 +49,280 @@ #define cROUNDS 2 #define dROUNDS 4 -#define ROTL(x,b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) ) - -#define U32TO8_LE(p, v) \ - (p)[0] = (uint8_t)((v) ); (p)[1] = (uint8_t)((v) >> 8); \ - (p)[2] = (uint8_t)((v) >> 16); (p)[3] = (uint8_t)((v) >> 24); - -#define U64TO8_LE(p, v) \ - U32TO8_LE((p), (uint32_t)((v) )); \ - U32TO8_LE((p) + 4, (uint32_t)((v) >> 32)); - -#define U8TO64_LE(p) \ - (((uint64_t)((p)[0]) ) | \ - ((uint64_t)((p)[1]) << 8) | \ - ((uint64_t)((p)[2]) << 16) | \ - ((uint64_t)((p)[3]) << 24) | \ - ((uint64_t)((p)[4]) << 32) | \ - ((uint64_t)((p)[5]) << 40) | \ - ((uint64_t)((p)[6]) << 48) | \ - ((uint64_t)((p)[7]) << 56)) - -#define SIPROUND \ - do { \ - v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \ - v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \ - v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \ - v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \ - } while(0) - +#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) + +#define U32TO8_LE(p, v) \ + (p)[0] = (uint8_t)((v)); \ + (p)[1] = (uint8_t)((v) >> 8); \ + (p)[2] = (uint8_t)((v) >> 16); \ + (p)[3] = (uint8_t)((v) >> 24); + +#define U64TO8_LE(p, v) \ + U32TO8_LE((p), (uint32_t)((v))); \ + U32TO8_LE((p) + 4, (uint32_t)((v) >> 32)); + +#define U8TO64_LE(p) \ + (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \ + ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | ((uint64_t)((p)[6]) << 48) | \ + ((uint64_t)((p)[7]) << 56)) + +#define SIPROUND \ + do \ + { \ + v0 += v1; \ + v1 = ROTL(v1, 13); \ + v1 ^= v0; \ + v0 = ROTL(v0, 32); \ + v2 += v3; \ + v3 = ROTL(v3, 16); \ + v3 ^= v2; \ + v0 += v3; \ + v3 = ROTL(v3, 21); \ + v3 ^= v0; \ + v2 += v1; \ + v1 = ROTL(v1, 17); \ + v1 ^= v2; \ + v2 = ROTL(v2, 32); \ + } while (0) #define TRACE -int siphashRefMain( uint8_t *out, const uint8_t *in, uint64_t inlen, const uint8_t *k ) +int siphashRefMain(uint8_t* out, const uint8_t* in, uint64_t inlen, const uint8_t* k) { - /* "somepseudorandomlygeneratedbytes" */ - uint64_t v0 = 0x736f6d6570736575ULL; - uint64_t v1 = 0x646f72616e646f6dULL; - uint64_t v2 = 0x6c7967656e657261ULL; - uint64_t v3 = 0x7465646279746573ULL; - uint64_t b; - uint64_t k0 = U8TO64_LE( k ); - uint64_t k1 = U8TO64_LE( k + 8 ); - uint64_t m; - int i; - const uint8_t *end = in + inlen - ( inlen % sizeof( uint64_t ) ); - const int left = inlen & 7; - b = ( ( uint64_t )inlen ) << 56; - v3 ^= k1; - v2 ^= k0; - v1 ^= k1; - v0 ^= k0; + /* "somepseudorandomlygeneratedbytes" */ + uint64_t v0 = 0x736f6d6570736575ULL; + uint64_t v1 = 0x646f72616e646f6dULL; + uint64_t v2 = 0x6c7967656e657261ULL; + uint64_t v3 = 0x7465646279746573ULL; + uint64_t b; + uint64_t k0 = U8TO64_LE(k); + uint64_t k1 = U8TO64_LE(k + 8); + uint64_t m; + int i; + const uint8_t* end = in + inlen - (inlen % sizeof(uint64_t)); + const int left = inlen & 7; + b = ((uint64_t)inlen) << 56; + v3 ^= k1; + v2 ^= k0; + v1 ^= k1; + v0 ^= k0; #ifdef DOUBLE - v1 ^= 0xee; + v1 ^= 0xee; #endif - for ( ; in != end; in += 8 ) - { - m = U8TO64_LE( in ); - v3 ^= m; - - TRACE; - for( i=0; i(data), len, k ); + siphashRefMain(out, reinterpret_cast(data), len, k); - return *(reinterpret_cast(out)); + return *(reinterpret_cast(out)); } - //////////////////////////////////////////////////////////////////////////////////// ////////////////////////// END REFERENCE IMPLEMENTATION //////////////////////////// //////////////////////////////////////////////////////////////////////////////////// using cadet::util::SipHash24; using cadet::util::SipHash24runtime; -using cadet::operator "" _hash; +using cadet::operator"" _hash; #if CADET_COMPILETIME_HASH +template +inline typename std::underlying_type::type as_integer(Enumeration const value) +{ + return static_cast::type>(value); +} - template - inline typename std::underlying_type::type as_integer(Enumeration const value) - { - return static_cast::type>(value); - } - - // Force compile time evaluation - enum class Bla : uint64_t - { - Hallo = "Hallo"_hash, - DasIstEinLangerTestAuchMitZahlenDrin12 = "DasIstEinLangerTestAuchMitZahlenDrin12"_hash, - A = "A"_hash, - AB = "AB"_hash, - ABC = "ABC"_hash, - ABCD = "ABCD"_hash, - ABCDE = "ABCDE"_hash, - ABCDEF = "ABCDEF"_hash, - ABCDEFG = "ABCDEFG"_hash, - ABCDEFGH = "ABCDEFGH"_hash, - ABCDEFGHI = "ABCDEFGHI"_hash, - ABCDEFGHIJ = "ABCDEFGHIJ"_hash, - ABCDEFGHIJK = "ABCDEFGHIJK"_hash, - ABCDEFGHIJKL = "ABCDEFGHIJKL"_hash, - ABCDEFGHIJKLM = "ABCDEFGHIJKLM"_hash, - ABCDEFGHIJKLMN = "ABCDEFGHIJKLMN"_hash, - ABCDEFGHIJKLMNO = "ABCDEFGHIJKLMNO"_hash, - ABCDEFGHIJKLMNOP = "ABCDEFGHIJKLMNOP"_hash, - ABCDEFGHIJKLMNOPQ = "ABCDEFGHIJKLMNOPQ"_hash, - ABCDEFGHIJKLMNOPQR = "ABCDEFGHIJKLMNOPQR"_hash, - ABCDEFGHIJKLMNOPQRS = "ABCDEFGHIJKLMNOPQRS"_hash, - ABCDEFGHIJKLMNOPQRST = "ABCDEFGHIJKLMNOPQRST"_hash, - ABCDEFGHIJKLMNOPQRSTU = "ABCDEFGHIJKLMNOPQRSTU"_hash, - ABCDEFGHIJKLMNOPQRSTUV = "ABCDEFGHIJKLMNOPQRSTUV"_hash, - ABCDEFGHIJKLMNOPQRSTUVW = "ABCDEFGHIJKLMNOPQRSTUVW"_hash, - ABCDEFGHIJKLMNOPQRSTUVWX = "ABCDEFGHIJKLMNOPQRSTUVWX"_hash, - ABCDEFGHIJKLMNOPQRSTUVWXY = "ABCDEFGHIJKLMNOPQRSTUVWXY"_hash, - ABCDEFGHIJKLMNOPQRSTUVWXYZ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"_hash, - }; - - TEST_CASE("String literal compile time hash", "[Hash],[CompileTime]") - { - REQUIRE(sipHashRef("Hallo", 5) == as_integer(Bla::Hallo)); - REQUIRE(sipHashRef("DasIstEinLangerTestAuchMitZahlenDrin12", 38) == as_integer(Bla::DasIstEinLangerTestAuchMitZahlenDrin12)); - REQUIRE(sipHashRef("A", 1) == as_integer(Bla::A)); - REQUIRE(sipHashRef("AB", 2) == as_integer(Bla::AB)); - REQUIRE(sipHashRef("ABC", 3) == as_integer(Bla::ABC)); - REQUIRE(sipHashRef("ABCD", 4) == as_integer(Bla::ABCD)); - REQUIRE(sipHashRef("ABCDE", 5) == as_integer(Bla::ABCDE)); - REQUIRE(sipHashRef("ABCDEF", 6) == as_integer(Bla::ABCDEF)); - REQUIRE(sipHashRef("ABCDEFG", 7) == as_integer(Bla::ABCDEFG)); - REQUIRE(sipHashRef("ABCDEFGH", 8) == as_integer(Bla::ABCDEFGH)); - REQUIRE(sipHashRef("ABCDEFGHI", 9) == as_integer(Bla::ABCDEFGHI)); - REQUIRE(sipHashRef("ABCDEFGHIJ", 10) == as_integer(Bla::ABCDEFGHIJ)); - REQUIRE(sipHashRef("ABCDEFGHIJK", 11) == as_integer(Bla::ABCDEFGHIJK)); - REQUIRE(sipHashRef("ABCDEFGHIJKL", 12) == as_integer(Bla::ABCDEFGHIJKL)); - REQUIRE(sipHashRef("ABCDEFGHIJKLM", 13) == as_integer(Bla::ABCDEFGHIJKLM)); - REQUIRE(sipHashRef("ABCDEFGHIJKLMN", 14) == as_integer(Bla::ABCDEFGHIJKLMN)); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNO", 15) == as_integer(Bla::ABCDEFGHIJKLMNO)); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOP", 16) == as_integer(Bla::ABCDEFGHIJKLMNOP)); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQ", 17) == as_integer(Bla::ABCDEFGHIJKLMNOPQ)); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQR", 18) == as_integer(Bla::ABCDEFGHIJKLMNOPQR)); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRS", 19) == as_integer(Bla::ABCDEFGHIJKLMNOPQRS)); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRST", 20) == as_integer(Bla::ABCDEFGHIJKLMNOPQRST)); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTU", 21) == as_integer(Bla::ABCDEFGHIJKLMNOPQRSTU)); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUV", 22) == as_integer(Bla::ABCDEFGHIJKLMNOPQRSTUV)); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVW", 23) == as_integer(Bla::ABCDEFGHIJKLMNOPQRSTUVW)); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVWX", 24) == as_integer(Bla::ABCDEFGHIJKLMNOPQRSTUVWX)); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVWXY", 25) == as_integer(Bla::ABCDEFGHIJKLMNOPQRSTUVWXY)); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26) == as_integer(Bla::ABCDEFGHIJKLMNOPQRSTUVWXYZ)); - } +// Force compile time evaluation +enum class Bla : uint64_t +{ + Hallo = "Hallo"_hash, + DasIstEinLangerTestAuchMitZahlenDrin12 = "DasIstEinLangerTestAuchMitZahlenDrin12"_hash, + A = "A"_hash, + AB = "AB"_hash, + ABC = "ABC"_hash, + ABCD = "ABCD"_hash, + ABCDE = "ABCDE"_hash, + ABCDEF = "ABCDEF"_hash, + ABCDEFG = "ABCDEFG"_hash, + ABCDEFGH = "ABCDEFGH"_hash, + ABCDEFGHI = "ABCDEFGHI"_hash, + ABCDEFGHIJ = "ABCDEFGHIJ"_hash, + ABCDEFGHIJK = "ABCDEFGHIJK"_hash, + ABCDEFGHIJKL = "ABCDEFGHIJKL"_hash, + ABCDEFGHIJKLM = "ABCDEFGHIJKLM"_hash, + ABCDEFGHIJKLMN = "ABCDEFGHIJKLMN"_hash, + ABCDEFGHIJKLMNO = "ABCDEFGHIJKLMNO"_hash, + ABCDEFGHIJKLMNOP = "ABCDEFGHIJKLMNOP"_hash, + ABCDEFGHIJKLMNOPQ = "ABCDEFGHIJKLMNOPQ"_hash, + ABCDEFGHIJKLMNOPQR = "ABCDEFGHIJKLMNOPQR"_hash, + ABCDEFGHIJKLMNOPQRS = "ABCDEFGHIJKLMNOPQRS"_hash, + ABCDEFGHIJKLMNOPQRST = "ABCDEFGHIJKLMNOPQRST"_hash, + ABCDEFGHIJKLMNOPQRSTU = "ABCDEFGHIJKLMNOPQRSTU"_hash, + ABCDEFGHIJKLMNOPQRSTUV = "ABCDEFGHIJKLMNOPQRSTUV"_hash, + ABCDEFGHIJKLMNOPQRSTUVW = "ABCDEFGHIJKLMNOPQRSTUVW"_hash, + ABCDEFGHIJKLMNOPQRSTUVWX = "ABCDEFGHIJKLMNOPQRSTUVWX"_hash, + ABCDEFGHIJKLMNOPQRSTUVWXY = "ABCDEFGHIJKLMNOPQRSTUVWXY"_hash, + ABCDEFGHIJKLMNOPQRSTUVWXYZ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"_hash, +}; + +TEST_CASE("String literal compile time hash", "[Hash],[CompileTime]") +{ + REQUIRE(sipHashRef("Hallo", 5) == as_integer(Bla::Hallo)); + REQUIRE(sipHashRef("DasIstEinLangerTestAuchMitZahlenDrin12", 38) == + as_integer(Bla::DasIstEinLangerTestAuchMitZahlenDrin12)); + REQUIRE(sipHashRef("A", 1) == as_integer(Bla::A)); + REQUIRE(sipHashRef("AB", 2) == as_integer(Bla::AB)); + REQUIRE(sipHashRef("ABC", 3) == as_integer(Bla::ABC)); + REQUIRE(sipHashRef("ABCD", 4) == as_integer(Bla::ABCD)); + REQUIRE(sipHashRef("ABCDE", 5) == as_integer(Bla::ABCDE)); + REQUIRE(sipHashRef("ABCDEF", 6) == as_integer(Bla::ABCDEF)); + REQUIRE(sipHashRef("ABCDEFG", 7) == as_integer(Bla::ABCDEFG)); + REQUIRE(sipHashRef("ABCDEFGH", 8) == as_integer(Bla::ABCDEFGH)); + REQUIRE(sipHashRef("ABCDEFGHI", 9) == as_integer(Bla::ABCDEFGHI)); + REQUIRE(sipHashRef("ABCDEFGHIJ", 10) == as_integer(Bla::ABCDEFGHIJ)); + REQUIRE(sipHashRef("ABCDEFGHIJK", 11) == as_integer(Bla::ABCDEFGHIJK)); + REQUIRE(sipHashRef("ABCDEFGHIJKL", 12) == as_integer(Bla::ABCDEFGHIJKL)); + REQUIRE(sipHashRef("ABCDEFGHIJKLM", 13) == as_integer(Bla::ABCDEFGHIJKLM)); + REQUIRE(sipHashRef("ABCDEFGHIJKLMN", 14) == as_integer(Bla::ABCDEFGHIJKLMN)); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNO", 15) == as_integer(Bla::ABCDEFGHIJKLMNO)); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOP", 16) == as_integer(Bla::ABCDEFGHIJKLMNOP)); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQ", 17) == as_integer(Bla::ABCDEFGHIJKLMNOPQ)); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQR", 18) == as_integer(Bla::ABCDEFGHIJKLMNOPQR)); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRS", 19) == as_integer(Bla::ABCDEFGHIJKLMNOPQRS)); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRST", 20) == as_integer(Bla::ABCDEFGHIJKLMNOPQRST)); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTU", 21) == as_integer(Bla::ABCDEFGHIJKLMNOPQRSTU)); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUV", 22) == as_integer(Bla::ABCDEFGHIJKLMNOPQRSTUV)); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVW", 23) == as_integer(Bla::ABCDEFGHIJKLMNOPQRSTUVW)); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVWX", 24) == as_integer(Bla::ABCDEFGHIJKLMNOPQRSTUVWX)); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVWXY", 25) == as_integer(Bla::ABCDEFGHIJKLMNOPQRSTUVWXY)); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26) == as_integer(Bla::ABCDEFGHIJKLMNOPQRSTUVWXYZ)); +} #endif #if CADET_COMPILER_CXX_CONSTEXPR - TEST_CASE("Compile time hash", "[Hash],[CompileTime]") - { - REQUIRE(sipHashRef("Hallo", 5) == SipHash24("Hallo")); - REQUIRE(sipHashRef("DasIstEinLangerTestAuchMitZahlenDrin12", 38) == SipHash24("DasIstEinLangerTestAuchMitZahlenDrin12")); - REQUIRE(sipHashRef("A", 1) == SipHash24("A")); - REQUIRE(sipHashRef("AB", 2) == SipHash24("AB")); - REQUIRE(sipHashRef("ABC", 3) == SipHash24("ABC")); - REQUIRE(sipHashRef("ABCD", 4) == SipHash24("ABCD")); - REQUIRE(sipHashRef("ABCDE", 5) == SipHash24("ABCDE")); - REQUIRE(sipHashRef("ABCDEF", 6) == SipHash24("ABCDEF")); - REQUIRE(sipHashRef("ABCDEFG", 7) == SipHash24("ABCDEFG")); - REQUIRE(sipHashRef("ABCDEFGH", 8) == SipHash24("ABCDEFGH")); - REQUIRE(sipHashRef("ABCDEFGHI", 9) == SipHash24("ABCDEFGHI")); - REQUIRE(sipHashRef("ABCDEFGHIJ", 10) == SipHash24("ABCDEFGHIJ")); - REQUIRE(sipHashRef("ABCDEFGHIJK", 11) == SipHash24("ABCDEFGHIJK")); - REQUIRE(sipHashRef("ABCDEFGHIJKL", 12) == SipHash24("ABCDEFGHIJKL")); - REQUIRE(sipHashRef("ABCDEFGHIJKLM", 13) == SipHash24("ABCDEFGHIJKLM")); - REQUIRE(sipHashRef("ABCDEFGHIJKLMN", 14) == SipHash24("ABCDEFGHIJKLMN")); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNO", 15) == SipHash24("ABCDEFGHIJKLMNO")); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOP", 16) == SipHash24("ABCDEFGHIJKLMNOP")); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQ", 17) == SipHash24("ABCDEFGHIJKLMNOPQ")); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQR", 18) == SipHash24("ABCDEFGHIJKLMNOPQR")); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRS", 19) == SipHash24("ABCDEFGHIJKLMNOPQRS")); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRST", 20) == SipHash24("ABCDEFGHIJKLMNOPQRST")); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTU", 21) == SipHash24("ABCDEFGHIJKLMNOPQRSTU")); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUV", 22) == SipHash24("ABCDEFGHIJKLMNOPQRSTUV")); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVW", 23) == SipHash24("ABCDEFGHIJKLMNOPQRSTUVW")); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVWX", 24) == SipHash24("ABCDEFGHIJKLMNOPQRSTUVWX")); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVWXY", 25) == SipHash24("ABCDEFGHIJKLMNOPQRSTUVWXY")); - REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26) == SipHash24("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); - } +TEST_CASE("Compile time hash", "[Hash],[CompileTime]") +{ + REQUIRE(sipHashRef("Hallo", 5) == SipHash24("Hallo")); + REQUIRE(sipHashRef("DasIstEinLangerTestAuchMitZahlenDrin12", 38) == + SipHash24("DasIstEinLangerTestAuchMitZahlenDrin12")); + REQUIRE(sipHashRef("A", 1) == SipHash24("A")); + REQUIRE(sipHashRef("AB", 2) == SipHash24("AB")); + REQUIRE(sipHashRef("ABC", 3) == SipHash24("ABC")); + REQUIRE(sipHashRef("ABCD", 4) == SipHash24("ABCD")); + REQUIRE(sipHashRef("ABCDE", 5) == SipHash24("ABCDE")); + REQUIRE(sipHashRef("ABCDEF", 6) == SipHash24("ABCDEF")); + REQUIRE(sipHashRef("ABCDEFG", 7) == SipHash24("ABCDEFG")); + REQUIRE(sipHashRef("ABCDEFGH", 8) == SipHash24("ABCDEFGH")); + REQUIRE(sipHashRef("ABCDEFGHI", 9) == SipHash24("ABCDEFGHI")); + REQUIRE(sipHashRef("ABCDEFGHIJ", 10) == SipHash24("ABCDEFGHIJ")); + REQUIRE(sipHashRef("ABCDEFGHIJK", 11) == SipHash24("ABCDEFGHIJK")); + REQUIRE(sipHashRef("ABCDEFGHIJKL", 12) == SipHash24("ABCDEFGHIJKL")); + REQUIRE(sipHashRef("ABCDEFGHIJKLM", 13) == SipHash24("ABCDEFGHIJKLM")); + REQUIRE(sipHashRef("ABCDEFGHIJKLMN", 14) == SipHash24("ABCDEFGHIJKLMN")); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNO", 15) == SipHash24("ABCDEFGHIJKLMNO")); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOP", 16) == SipHash24("ABCDEFGHIJKLMNOP")); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQ", 17) == SipHash24("ABCDEFGHIJKLMNOPQ")); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQR", 18) == SipHash24("ABCDEFGHIJKLMNOPQR")); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRS", 19) == SipHash24("ABCDEFGHIJKLMNOPQRS")); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRST", 20) == SipHash24("ABCDEFGHIJKLMNOPQRST")); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTU", 21) == SipHash24("ABCDEFGHIJKLMNOPQRSTU")); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUV", 22) == SipHash24("ABCDEFGHIJKLMNOPQRSTUV")); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVW", 23) == SipHash24("ABCDEFGHIJKLMNOPQRSTUVW")); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVWX", 24) == SipHash24("ABCDEFGHIJKLMNOPQRSTUVWX")); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVWXY", 25) == SipHash24("ABCDEFGHIJKLMNOPQRSTUVWXY")); + REQUIRE(sipHashRef("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26) == SipHash24("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); +} #endif TEST_CASE("String hash", "[Hash]") { REQUIRE(sipHashRef("Hallo", 5) == SipHash24runtime("Hallo")); - REQUIRE(sipHashRef("DasIstEinLangerTestAuchMitZahlenDrin12", 38) == SipHash24runtime("DasIstEinLangerTestAuchMitZahlenDrin12")); + REQUIRE(sipHashRef("DasIstEinLangerTestAuchMitZahlenDrin12", 38) == + SipHash24runtime("DasIstEinLangerTestAuchMitZahlenDrin12")); REQUIRE(sipHashRef("A", 1) == SipHash24runtime("A")); REQUIRE(sipHashRef("AB", 2) == SipHash24runtime("AB")); REQUIRE(sipHashRef("ABC", 3) == SipHash24runtime("ABC")); diff --git a/test/Subset.cpp b/test/Subset.cpp index b3408e3e4..176bcdb3a 100644 --- a/test/Subset.cpp +++ b/test/Subset.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and diff --git a/test/TimeIntegrator.cpp b/test/TimeIntegrator.cpp index cf501415c..8d6f2bbc1 100644 --- a/test/TimeIntegrator.cpp +++ b/test/TimeIntegrator.cpp @@ -24,80 +24,82 @@ namespace { - inline bool hasNaN(double const* const y, unsigned int size) +inline bool hasNaN(double const* const y, unsigned int size) +{ + for (unsigned int i = 0; i < size; ++i) { - for (unsigned int i = 0; i < size; ++i) - { - if (std::isnan(y[i])) - return true; - } - return false; + if (std::isnan(y[i])) + return true; } + return false; +} - inline bool hasNaN(const N_Vector p) - { - return hasNaN(NVEC_DATA(p), NVEC_LENGTH(p)); - } +inline bool hasNaN(const N_Vector p) +{ + return hasNaN(NVEC_DATA(p), NVEC_LENGTH(p)); +} - inline std::string getIDAReturnFlagName(int solverFlag) - { - char const* const retFlagName = IDAGetReturnFlagName(solverFlag); - const std::string flagName = retFlagName; - std::free(const_cast(retFlagName)); +inline std::string getIDAReturnFlagName(int solverFlag) +{ + char const* const retFlagName = IDAGetReturnFlagName(solverFlag); + const std::string flagName = retFlagName; + std::free(const_cast(retFlagName)); - return flagName; - } + return flagName; +} - /** - * @brief IDAS error handler function - * @details Handles errors reported by the IDAS solver. See section 4.6.2 of the IDAS manual for details. - */ - void idasErrorHandler(int error_code, const char* module, const char* function, char* msg, void* eh_data) - { - std::ostringstream oss; - oss << "In function '" << function << "' of module '" << module << "', error code '" << getIDAReturnFlagName(error_code) << "':\n" << msg; +/** + * @brief IDAS error handler function + * @details Handles errors reported by the IDAS solver. See section 4.6.2 of the IDAS manual for details. + */ +void idasErrorHandler(int error_code, const char* module, const char* function, char* msg, void* eh_data) +{ + std::ostringstream oss; + oss << "In function '" << function << "' of module '" << module << "', error code '" + << getIDAReturnFlagName(error_code) << "':\n" + << msg; - // @todo Find an error handling system and put it here - if (error_code < 0) - { - // Error - LOG(Error) << oss.str(); - } - else - { - // Warning - LOG(Warning) << oss.str(); - } + // @todo Find an error handling system and put it here + if (error_code < 0) + { + // Error + LOG(Error) << oss.str(); } - - /** - * @brief IDAS wrapper function to call the model's residual() method - */ - int residualDaeWrapper(double t, N_Vector y, N_Vector yDot, N_Vector res, void* userData) + else { - cadet::test::TimeIntegrator* const sim = static_cast(userData); - const int secIdx = sim->currentSection(); + // Warning + LOG(Warning) << oss.str(); + } +} - LOG(Trace) << "==> Residual at t = " << t << " sec = " << secIdx; +/** + * @brief IDAS wrapper function to call the model's residual() method + */ +int residualDaeWrapper(double t, N_Vector y, N_Vector yDot, N_Vector res, void* userData) +{ + cadet::test::TimeIntegrator* const sim = static_cast(userData); + const int secIdx = sim->currentSection(); - return sim->model()->residualWithJacobian(t, secIdx, NVEC_DATA(y), NVEC_DATA(yDot), NVEC_DATA(res)); - } + LOG(Trace) << "==> Residual at t = " << t << " sec = " << secIdx; - /** - * @brief IDAS wrapper function to call the model's linearSolve() method - */ - int linearSolveWrapper(IDAMem IDA_mem, N_Vector rhs, N_Vector weight, N_Vector y, N_Vector yDot, N_Vector res) - { - cadet::test::TimeIntegrator* const sim = static_cast(IDA_mem->ida_lmem); - const double t = IDA_mem->ida_tn; - const double alpha = IDA_mem->ida_cj; - const double tol = IDA_mem->ida_epsNewt; + return sim->model()->residualWithJacobian(t, secIdx, NVEC_DATA(y), NVEC_DATA(yDot), NVEC_DATA(res)); +} - LOG(Trace) << "==> Solve at t = " << t << " alpha = " << alpha << " tol = " << tol; +/** + * @brief IDAS wrapper function to call the model's linearSolve() method + */ +int linearSolveWrapper(IDAMem IDA_mem, N_Vector rhs, N_Vector weight, N_Vector y, N_Vector yDot, N_Vector res) +{ + cadet::test::TimeIntegrator* const sim = static_cast(IDA_mem->ida_lmem); + const double t = IDA_mem->ida_tn; + const double alpha = IDA_mem->ida_cj; + const double tol = IDA_mem->ida_epsNewt; - return sim->model()->linearSolve(t, alpha, tol, NVEC_DATA(rhs), NVEC_DATA(weight), NVEC_DATA(y), NVEC_DATA(yDot)); - } + LOG(Trace) << "==> Solve at t = " << t << " alpha = " << alpha << " tol = " << tol; + + return sim->model()->linearSolve(t, alpha, tol, NVEC_DATA(rhs), NVEC_DATA(weight), NVEC_DATA(y), NVEC_DATA(yDot)); } +} // namespace namespace cadet { @@ -105,429 +107,436 @@ namespace cadet namespace test { - TimeIntegrator::TimeIntegrator() : _model(nullptr), _idaMemBlock(nullptr), _vecStateY(nullptr), - _vecStateYdot(nullptr), _absTol(1, 1.0e-8), _relTol(1.0e-6), _initStepSize(1, 1.0e-6), - _maxSteps(10000), _maxStepSize(0.0), _maxNewtonIter(3), _maxErrorTestFail(7), _maxConvTestFail(10), - _curSec(0) - { - } +TimeIntegrator::TimeIntegrator() + : _model(nullptr), _idaMemBlock(nullptr), _vecStateY(nullptr), _vecStateYdot(nullptr), _absTol(1, 1.0e-8), + _relTol(1.0e-6), _initStepSize(1, 1.0e-6), _maxSteps(10000), _maxStepSize(0.0), _maxNewtonIter(3), + _maxErrorTestFail(7), _maxConvTestFail(10), _curSec(0) +{ +} - TimeIntegrator::~TimeIntegrator() CADET_NOEXCEPT - { - if (_vecStateYdot) - NVec_Destroy(_vecStateYdot); - if (_vecStateY) - NVec_Destroy(_vecStateY); +TimeIntegrator::~TimeIntegrator() CADET_NOEXCEPT +{ + if (_vecStateYdot) + NVec_Destroy(_vecStateYdot); + if (_vecStateY) + NVec_Destroy(_vecStateY); - if (_idaMemBlock) - IDAFree(&_idaMemBlock); - } + if (_idaMemBlock) + IDAFree(&_idaMemBlock); +} - void TimeIntegrator::initializeModel(IDiffEqModel& model) - { - _model = &model; +void TimeIntegrator::initializeModel(IDiffEqModel& model) +{ + _model = &model; - // Allocate and initialize state vectors - const unsigned int nDOFs = _model->numDofs(); - _vecStateY = NVec_New(nDOFs); - _vecStateYdot = NVec_New(nDOFs); + // Allocate and initialize state vectors + const unsigned int nDOFs = _model->numDofs(); + _vecStateY = NVec_New(nDOFs); + _vecStateYdot = NVec_New(nDOFs); - // Initialize with all zeros, correct initial conditions will be set later - NVec_Const(0.0, _vecStateY); - NVec_Const(0.0, _vecStateYdot); + // Initialize with all zeros, correct initial conditions will be set later + NVec_Const(0.0, _vecStateY); + NVec_Const(0.0, _vecStateYdot); - // Create IDAS internal memory - _idaMemBlock = IDACreate(); + // Create IDAS internal memory + _idaMemBlock = IDACreate(); - // IDAS Step 4.1: Specify error handler function - IDASetErrHandlerFn(_idaMemBlock, &idasErrorHandler, this); + // IDAS Step 4.1: Specify error handler function + IDASetErrHandlerFn(_idaMemBlock, &idasErrorHandler, this); - // IDAS Step 5: Initialize the solver - _model->applyInitialCondition(NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)); + // IDAS Step 5: Initialize the solver + _model->applyInitialCondition(NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)); - // Use 0.0 as beginning of simulation time if we haven't set section times yet - if (_sectionTimes.size() > 0) - IDAInit(_idaMemBlock, &residualDaeWrapper, _sectionTimes[0], _vecStateY, _vecStateYdot); - else - IDAInit(_idaMemBlock, &residualDaeWrapper, 0.0, _vecStateY, _vecStateYdot); + // Use 0.0 as beginning of simulation time if we haven't set section times yet + if (_sectionTimes.size() > 0) + IDAInit(_idaMemBlock, &residualDaeWrapper, _sectionTimes[0], _vecStateY, _vecStateYdot); + else + IDAInit(_idaMemBlock, &residualDaeWrapper, 0.0, _vecStateY, _vecStateYdot); - // IDAS Step 6: Specify integration tolerances (S: scalar; V: array) - updateMainErrorTolerances(); + // IDAS Step 6: Specify integration tolerances (S: scalar; V: array) + updateMainErrorTolerances(); - // IDAS Step 7.1: Set optional inputs + // IDAS Step 7.1: Set optional inputs - // Set time integrator parameters - IDASetMaxNumSteps(_idaMemBlock, _maxSteps); - IDASetMaxStep(_idaMemBlock, _maxStepSize); - IDASetMaxNonlinIters(_idaMemBlock, _maxNewtonIter); - IDASetMaxErrTestFails(_idaMemBlock, _maxErrorTestFail); - IDASetMaxConvFails(_idaMemBlock, _maxConvTestFail); - - // Specify the linear solver. - IDAMem IDA_mem = static_cast(_idaMemBlock); - - IDA_mem->ida_lsolve = &linearSolveWrapper; - IDA_mem->ida_lmem = this; - IDA_mem->ida_linit = nullptr; - IDA_mem->ida_lsetup = nullptr; - IDA_mem->ida_lperf = nullptr; - IDA_mem->ida_lfree = nullptr; + // Set time integrator parameters + IDASetMaxNumSteps(_idaMemBlock, _maxSteps); + IDASetMaxStep(_idaMemBlock, _maxStepSize); + IDASetMaxNonlinIters(_idaMemBlock, _maxNewtonIter); + IDASetMaxErrTestFails(_idaMemBlock, _maxErrorTestFail); + IDASetMaxConvFails(_idaMemBlock, _maxConvTestFail); + + // Specify the linear solver. + IDAMem IDA_mem = static_cast(_idaMemBlock); + + IDA_mem->ida_lsolve = &linearSolveWrapper; + IDA_mem->ida_lmem = this; + IDA_mem->ida_linit = nullptr; + IDA_mem->ida_lsetup = nullptr; + IDA_mem->ida_lperf = nullptr; + IDA_mem->ida_lfree = nullptr; // IDA_mem->ida_efun = &weightWrapper; // IDA_mem->ida_user_efun = 1; #if CADET_SUNDIALS_IFACE <= 2 - IDA_mem->ida_setupNonNull = false; + IDA_mem->ida_setupNonNull = false; #endif - // Attach user data structure - IDASetUserData(_idaMemBlock, this); - } + // Attach user data structure + IDASetUserData(_idaMemBlock, this); +} - void TimeIntegrator::configureTimeIntegrator(double relTol, double absTol, double initStepSize, unsigned int maxSteps, double maxStepSize) - { - _absTol.clear(); - _absTol.push_back(absTol); +void TimeIntegrator::configureTimeIntegrator(double relTol, double absTol, double initStepSize, unsigned int maxSteps, + double maxStepSize) +{ + _absTol.clear(); + _absTol.push_back(absTol); - _relTol = relTol; - _maxSteps = maxSteps; - _maxStepSize = maxStepSize; + _relTol = relTol; + _maxSteps = maxSteps; + _maxStepSize = maxStepSize; - _initStepSize.clear(); - _initStepSize.push_back(initStepSize); + _initStepSize.clear(); + _initStepSize.push_back(initStepSize); - updateMainErrorTolerances(); - } + updateMainErrorTolerances(); +} - void TimeIntegrator::configureTimeIntegrator(double relTol, double absTol, const std::vector& initStepSizes, unsigned int maxSteps, double maxStepSize) - { - _absTol.clear(); - _absTol.push_back(absTol); +void TimeIntegrator::configureTimeIntegrator(double relTol, double absTol, const std::vector& initStepSizes, + unsigned int maxSteps, double maxStepSize) +{ + _absTol.clear(); + _absTol.push_back(absTol); - _relTol = relTol; - _maxSteps = maxSteps; - _maxStepSize = maxStepSize; - _initStepSize = initStepSizes; + _relTol = relTol; + _maxSteps = maxSteps; + _maxStepSize = maxStepSize; + _initStepSize = initStepSizes; - updateMainErrorTolerances(); - } + updateMainErrorTolerances(); +} - void TimeIntegrator::updateMainErrorTolerances() +void TimeIntegrator::updateMainErrorTolerances() +{ + if (!_idaMemBlock) + return; + + if (_absTol.size() > 1) { - if (!_idaMemBlock) + if (!_model) return; - if (_absTol.size() > 1) + const unsigned int nDofs = _model->numDofs(); + N_Vector absTolTemp = NVec_New(nDofs); + + // Check whether user has given us full absolute error for all (pure) DOFs + if (_absTol.size() >= nDofs) { - if (!_model) - return; + // Copy error tolerances for pure data + std::copy(_absTol.data(), _absTol.data() + nDofs, NVEC_DATA(absTolTemp)); + } - const unsigned int nDofs = _model->numDofs(); - N_Vector absTolTemp = NVec_New(nDofs); + IDASVtolerances(_idaMemBlock, _relTol, absTolTemp); + NVec_Destroy(absTolTemp); + } + else + IDASStolerances(_idaMemBlock, _relTol, _absTol[0]); +} - // Check whether user has given us full absolute error for all (pure) DOFs - if (_absTol.size() >= nDofs) - { - // Copy error tolerances for pure data - std::copy(_absTol.data(), _absTol.data() + nDofs, NVEC_DATA(absTolTemp)); - } +const std::vector& TimeIntegrator::getSolutionTimes() const +{ + return _solutionTimes; +} + +void TimeIntegrator::setSectionTimes(const std::vector& sectionTimes) +{ + setSectionTimes(sectionTimes, std::vector(sectionTimes.size() - 1, false)); +} - IDASVtolerances(_idaMemBlock, _relTol, absTolTemp); - NVec_Destroy(absTolTemp); +void TimeIntegrator::setSectionTimes(const std::vector& sectionTimes, + const std::vector& sectionContinuity) +{ + // Ensure that at least one section is defined + if (sectionTimes.size() < 2) + throw std::invalid_argument("At least one section has to be specified!"); + + // Ensure that all section start times are smaller than their end times + for (std::size_t i = 0; i < sectionTimes.size() - 1; ++i) + if (sectionTimes[i] > sectionTimes[i + 1]) + { + LOG(Error) << "The end time of each section must be greater than its start time (failed for section " << i + << ")!"; + return; } - else - IDASStolerances(_idaMemBlock, _relTol, _absTol[0]); - } - const std::vector& TimeIntegrator::getSolutionTimes() const - { - return _solutionTimes; - } + _sectionTimes = sectionTimes; + _sectionContinuity = sectionContinuity; +} + +int TimeIntegrator::getNextSection(double t, int startIdx) const +{ + if (t < _sectionTimes[startIdx]) + return -1; - void TimeIntegrator::setSectionTimes(const std::vector& sectionTimes) + for (std::size_t i = startIdx; i < _sectionTimes.size() - 1; ++i) { - setSectionTimes(sectionTimes, std::vector(sectionTimes.size() - 1, false)); + if (_sectionTimes[i] >= t) + return i; } - void TimeIntegrator::setSectionTimes(const std::vector& sectionTimes, const std::vector& sectionContinuity) + return -1; +} + +void TimeIntegrator::integrate() +{ + // In this function the model is integrated by IDAS from the SUNDIALS package. + // The authors of IDAS recommend to restart the time integrator when a discontinuity + // is encountered (see https://computation.llnl.gov/casc/sundials/support/notes.html#disc). + // The sectionTime (together with the sectionContinuity) array indicates such + // discontinuitites and the solver is restarted accordingly. This also requires + // the computation of consistent initial values for each restart. + + std::vector::const_iterator it; + double tOut = 0.0; + + const bool writeAtUserTimes = _solutionTimes.size() > 0; + + // Decide whether to use user specified solution output times (IDA_NORMAL) + // or internal integrator steps (IDA_ONE_STEP) + int idaTask = IDA_ONE_STEP; + if (writeAtUserTimes) { - // Ensure that at least one section is defined - if (sectionTimes.size() < 2) - throw std::invalid_argument("At least one section has to be specified!"); + idaTask = IDA_NORMAL; + } - // Ensure that all section start times are smaller than their end times - for (std::size_t i = 0; i < sectionTimes.size() - 1; ++i) - if (sectionTimes[i] > sectionTimes[i + 1]) - { - LOG(Error) << "The end time of each section must be greater than its start time (failed for section " << i << ")!"; - return; - } + LOG(Debug) << "Integration span: [" << _sectionTimes[0] << ", " << _sectionTimes.back() << "] sections"; - _sectionTimes = sectionTimes; - _sectionContinuity = sectionContinuity; + if (writeAtUserTimes) + { + LOG(Debug) << "Solution time span: [" << _solutionTimes[0] << ", " << _solutionTimes.back() << "]"; } - int TimeIntegrator::getNextSection(double t, int startIdx) const + double curT = _sectionTimes[0]; + _curSec = 0; + const double tEnd = writeAtUserTimes ? _solutionTimes.back() : _sectionTimes.back(); + while (curT < tEnd) { - if (t < _sectionTimes[startIdx]) - return -1; - - for (std::size_t i = startIdx; i < _sectionTimes.size() - 1; ++i) + // Get smallest index with t_i >= curT (t_i being a _sectionTimes element) + // This will return i if curT == _sectionTimes[i], which effectively advances + // the index if required + _curSec = getNextSection(curT, _curSec); + const double startTime = _sectionTimes[_curSec]; + + // Determine continuous time slice + unsigned int skip = 1; // Always finish the current section + for (std::size_t i = _curSec; i < _sectionTimes.size() - 2; ++i) { - if (_sectionTimes[i] >= t) - return i; + if (!_sectionContinuity[i]) + break; + + // This is a continuous section transition, so we don't need to + // restart the integrator and just integrate for a longer time + ++skip; } - return -1; - } + const double endTime = + writeAtUserTimes ? std::min(_sectionTimes[_curSec + skip], tEnd) : _sectionTimes[_curSec + skip]; + curT = startTime; - void TimeIntegrator::integrate() - { - // In this function the model is integrated by IDAS from the SUNDIALS package. - // The authors of IDAS recommend to restart the time integrator when a discontinuity - // is encountered (see https://computation.llnl.gov/casc/sundials/support/notes.html#disc). - // The sectionTime (together with the sectionContinuity) array indicates such - // discontinuitites and the solver is restarted accordingly. This also requires - // the computation of consistent initial values for each restart. + LOG(Debug) << " ###### SECTION " << _curSec << " from " << startTime << " to " << endTime; - std::vector::const_iterator it; - double tOut = 0.0; + // IDAS Step 7.3: Set the initial step size + const double stepSize = _initStepSize.size() > 1 ? _initStepSize[_curSec] : _initStepSize[0]; + IDASetInitStep(_idaMemBlock, stepSize); - const bool writeAtUserTimes = _solutionTimes.size() > 0; + // IDAS Step 7.4: Set the stop time + IDASetStopTime(_idaMemBlock, endTime); - // Decide whether to use user specified solution output times (IDA_NORMAL) - // or internal integrator steps (IDA_ONE_STEP) - int idaTask = IDA_ONE_STEP; - if (writeAtUserTimes) - { - idaTask = IDA_NORMAL; - } + // Update Jacobian and compute consistent initial values + _model->notifyDiscontinuousSectionTransition(curT, _curSec, NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)); + + // IDAS Step 5.2: Re-initialization of the solver + IDAReInit(_idaMemBlock, startTime, _vecStateY, _vecStateYdot); - LOG(Debug) << "Integration span: [" << _sectionTimes[0] << ", " << _sectionTimes.back() << "] sections"; + // Inititalize the IDA solver flag + int solverFlag = IDA_SUCCESS; if (writeAtUserTimes) { - LOG(Debug) << "Solution time span: [" << _solutionTimes[0] << ", " << _solutionTimes.back() << "]"; + // Write initial conditions only if desired by user + if (_curSec == 0 && _solutionTimes.front() == curT) + _model->saveSolution(curT, NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)); + + // Initialize iterator and forward it to the first solution time that lies inside the current section + it = _solutionTimes.begin(); + while ((*it) <= startTime) + ++it; + } + else + { + // Always write initial conditions if solutions are written at integration times + if (_curSec == 0) + _model->saveSolution(curT, NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)); + + // Here tOut - only during the first call to IDASolve - specifies the direction + // and rough scale of the independent variable, see IDAS Guide p.33 + tOut = endTime; } - double curT = _sectionTimes[0]; - _curSec = 0; - const double tEnd = writeAtUserTimes ? _solutionTimes.back() : _sectionTimes.back(); - while (curT < tEnd) + // Main loop which integrates the system until reaching the end time of the current section + // or until an error occures + while ((solverFlag == IDA_SUCCESS) || (solverFlag == IDA_ROOT_RETURN)) { - // Get smallest index with t_i >= curT (t_i being a _sectionTimes element) - // This will return i if curT == _sectionTimes[i], which effectively advances - // the index if required - _curSec = getNextSection(curT, _curSec); - const double startTime = _sectionTimes[_curSec]; - - // Determine continuous time slice - unsigned int skip = 1; // Always finish the current section - for (std::size_t i = _curSec; i < _sectionTimes.size() - 2; ++i) + // Update tOut if we write solutions at user specified times + if (writeAtUserTimes) { - if (!_sectionContinuity[i]) + // Check if user specified times are sufficiently long. + // otherwise integrate till IDA_TSTOP_RETURN + if (it == _solutionTimes.end()) break; - - // This is a continuous section transition, so we don't need to - // restart the integrator and just integrate for a longer time - ++skip; + else + tOut = *it; } - const double endTime = writeAtUserTimes ? std::min(_sectionTimes[_curSec + skip], tEnd) : _sectionTimes[_curSec + skip]; - curT = startTime; + // IDA Step 11: Advance solution in time + solverFlag = IDASolve(_idaMemBlock, tOut, &curT, _vecStateY, _vecStateYdot, idaTask); + LOG(Debug) << "Solve from " << curT << " to " << tOut << " => " + << (solverFlag == IDA_SUCCESS ? "IDA_SUCCESS" : "") + << (solverFlag == IDA_TSTOP_RETURN ? "IDA_TSTOP_RETURN" : ""); - LOG(Debug) << " ###### SECTION " << _curSec << " from " << startTime << " to " << endTime; +#ifdef CADET_DEBUG + { + long nTimeSteps = 0; + IDAGetNumSteps(_idaMemBlock, &nTimeSteps); - // IDAS Step 7.3: Set the initial step size - const double stepSize = _initStepSize.size() > 1 ? _initStepSize[_curSec] : _initStepSize[0]; - IDASetInitStep(_idaMemBlock, stepSize); + double curStepSize = 0.0; + IDAGetCurrentStep(_idaMemBlock, &curStepSize); - // IDAS Step 7.4: Set the stop time - IDASetStopTime(_idaMemBlock, endTime); + double lastStepSize = 0.0; + IDAGetLastStep(_idaMemBlock, &lastStepSize); - // Update Jacobian and compute consistent initial values - _model->notifyDiscontinuousSectionTransition(curT, _curSec, NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)); + long nResEvals = 0; + IDAGetNumResEvals(_idaMemBlock, &nResEvals); - // IDAS Step 5.2: Re-initialization of the solver - IDAReInit(_idaMemBlock, startTime, _vecStateY, _vecStateYdot); + long nErrTestFail = 0; + IDAGetNumErrTestFails(_idaMemBlock, &nErrTestFail); - // Inititalize the IDA solver flag - int solverFlag = IDA_SUCCESS; + long nNonLin = 0; + IDAGetNumNonlinSolvIters(_idaMemBlock, &nNonLin); - if (writeAtUserTimes) - { - // Write initial conditions only if desired by user - if (_curSec == 0 && _solutionTimes.front() == curT) - _model->saveSolution(curT, NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)); + long nConvFail = 0; + IDAGetNumNonlinSolvConvFails(_idaMemBlock, &nConvFail); - // Initialize iterator and forward it to the first solution time that lies inside the current section - it = _solutionTimes.begin(); - while ((*it) <= startTime) ++it; + LOG(Debug) << "=== #Steps: " << nTimeSteps << "\n=== #Residual evals: " << nResEvals + << "\n=== #Error test fails: " << nErrTestFail << "\n=== #Newton iters: " << nNonLin + << "\n=== #Conv test fails: " << nConvFail << "\n=== Last step size: " << lastStepSize + << "\n=== Next step size: " << curStepSize; } - else +#endif + switch (solverFlag) { - // Always write initial conditions if solutions are written at integration times - if (_curSec == 0) - _model->saveSolution(curT, NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)); + case IDA_SUCCESS: + // tOut was reached + _model->saveSolution(curT, NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)); - // Here tOut - only during the first call to IDASolve - specifies the direction - // and rough scale of the independent variable, see IDAS Guide p.33 - tOut = endTime; - } - - // Main loop which integrates the system until reaching the end time of the current section - // or until an error occures - while ((solverFlag == IDA_SUCCESS) || (solverFlag == IDA_ROOT_RETURN)) - { - // Update tOut if we write solutions at user specified times if (writeAtUserTimes) + ++it; + + break; + case IDA_ROOT_RETURN: + // A root was found + // Eventually call some routine + break; + case IDA_TSTOP_RETURN: + // Section end time was reached (in previous step) + if (!writeAtUserTimes && (endTime == _sectionTimes.back())) { - // Check if user specified times are sufficiently long. - // otherwise integrate till IDA_TSTOP_RETURN - if (it == _solutionTimes.end()) - break; - else - tOut = *it; - } - - // IDA Step 11: Advance solution in time - solverFlag = IDASolve(_idaMemBlock, tOut, &curT, _vecStateY, _vecStateYdot, idaTask); - LOG(Debug) << "Solve from " << curT << " to " << tOut << " => " - << (solverFlag == IDA_SUCCESS ? "IDA_SUCCESS" : "") << (solverFlag == IDA_TSTOP_RETURN ? "IDA_TSTOP_RETURN" : ""); - -#ifdef CADET_DEBUG - { - long nTimeSteps = 0; - IDAGetNumSteps(_idaMemBlock, &nTimeSteps); - - double curStepSize = 0.0; - IDAGetCurrentStep(_idaMemBlock, &curStepSize); - - double lastStepSize = 0.0; - IDAGetLastStep(_idaMemBlock, &lastStepSize); - - long nResEvals = 0; - IDAGetNumResEvals(_idaMemBlock, &nResEvals); - - long nErrTestFail = 0; - IDAGetNumErrTestFails(_idaMemBlock, &nErrTestFail); - - long nNonLin = 0; - IDAGetNumNonlinSolvIters(_idaMemBlock, &nNonLin); - - long nConvFail = 0; - IDAGetNumNonlinSolvConvFails(_idaMemBlock, &nConvFail); - - LOG(Debug) << "=== #Steps: " << nTimeSteps << "\n=== #Residual evals: " << nResEvals << "\n=== #Error test fails: " << nErrTestFail - << "\n=== #Newton iters: " << nNonLin << "\n=== #Conv test fails: " << nConvFail << "\n=== Last step size: " << lastStepSize - << "\n=== Next step size: " << curStepSize; - } - #endif - switch (solverFlag) - { - case IDA_SUCCESS: - // tOut was reached + // Write a solution for the ultimate endTime in the last section, + // when we write at integration times. _model->saveSolution(curT, NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)); + } - if (writeAtUserTimes) - ++it; - - break; - case IDA_ROOT_RETURN: - // A root was found - // Eventually call some routine - break; - case IDA_TSTOP_RETURN: - // Section end time was reached (in previous step) - if (!writeAtUserTimes && (endTime == _sectionTimes.back())) - { - // Write a solution for the ultimate endTime in the last section, - // when we write at integration times. - _model->saveSolution(curT, NVEC_DATA(_vecStateY), NVEC_DATA(_vecStateYdot)); - } - - break; - default: - // An error occured - const std::string errorFlag = getIDAReturnFlagName(solverFlag); - LOG(Error) << "IDASolve returned " << errorFlag << " at t = " << curT; - - return; - } // switch + break; + default: + // An error occured + const std::string errorFlag = getIDAReturnFlagName(solverFlag); + LOG(Error) << "IDASolve returned " << errorFlag << " at t = " << curT; - } // while + return; + } // switch - } // for (_sec ...) + } // while - } + } // for (_sec ...) +} - void TimeIntegrator::setRelativeErrorTolerance(double relTol) - { - _relTol = relTol; - updateMainErrorTolerances(); - } +void TimeIntegrator::setRelativeErrorTolerance(double relTol) +{ + _relTol = relTol; + updateMainErrorTolerances(); +} - void TimeIntegrator::setAbsoluteErrorTolerance(double absTol) - { - _absTol.clear(); - _absTol.push_back(absTol); - updateMainErrorTolerances(); - } +void TimeIntegrator::setAbsoluteErrorTolerance(double absTol) +{ + _absTol.clear(); + _absTol.push_back(absTol); + updateMainErrorTolerances(); +} - void TimeIntegrator::setAbsoluteErrorTolerance(const std::vector& absTol) - { - _absTol = absTol; - updateMainErrorTolerances(); - } +void TimeIntegrator::setAbsoluteErrorTolerance(const std::vector& absTol) +{ + _absTol = absTol; + updateMainErrorTolerances(); +} - void TimeIntegrator::setInitialStepSize(double stepSize) - { - _initStepSize.clear(); - _initStepSize.push_back(stepSize); - } +void TimeIntegrator::setInitialStepSize(double stepSize) +{ + _initStepSize.clear(); + _initStepSize.push_back(stepSize); +} - void TimeIntegrator::setInitialStepSize(const std::vector& stepSize) - { - _initStepSize = stepSize; - } +void TimeIntegrator::setInitialStepSize(const std::vector& stepSize) +{ + _initStepSize = stepSize; +} - void TimeIntegrator::setMaximumSteps(unsigned int maxSteps) - { - _maxSteps = maxSteps; - if (_idaMemBlock) - IDASetMaxNumSteps(_idaMemBlock, _maxSteps); - } +void TimeIntegrator::setMaximumSteps(unsigned int maxSteps) +{ + _maxSteps = maxSteps; + if (_idaMemBlock) + IDASetMaxNumSteps(_idaMemBlock, _maxSteps); +} - void TimeIntegrator::setMaximumStepSize(double maxStepSize) - { - _maxStepSize = std::max(0.0, maxStepSize); - if (_idaMemBlock) - IDASetMaxStep(_idaMemBlock, _maxStepSize); - } +void TimeIntegrator::setMaximumStepSize(double maxStepSize) +{ + _maxStepSize = std::max(0.0, maxStepSize); + if (_idaMemBlock) + IDASetMaxStep(_idaMemBlock, _maxStepSize); +} - void TimeIntegrator::setMaxNewtonIteration(unsigned int nIter) - { - _maxNewtonIter = nIter; - if (_idaMemBlock) - IDASetMaxNonlinIters(_idaMemBlock, nIter); - } +void TimeIntegrator::setMaxNewtonIteration(unsigned int nIter) +{ + _maxNewtonIter = nIter; + if (_idaMemBlock) + IDASetMaxNonlinIters(_idaMemBlock, nIter); +} - void TimeIntegrator::setMaxErrorTestFails(unsigned int nFails) - { - _maxErrorTestFail = nFails; - if (_idaMemBlock) - IDASetMaxErrTestFails(_idaMemBlock, nFails); - } +void TimeIntegrator::setMaxErrorTestFails(unsigned int nFails) +{ + _maxErrorTestFail = nFails; + if (_idaMemBlock) + IDASetMaxErrTestFails(_idaMemBlock, nFails); +} - void TimeIntegrator::setMaxConvergenceFails(unsigned int nFails) - { - _maxConvTestFail = nFails; - if (_idaMemBlock) - IDASetMaxConvFails(_idaMemBlock, nFails); - } +void TimeIntegrator::setMaxConvergenceFails(unsigned int nFails) +{ + _maxConvTestFail = nFails; + if (_idaMemBlock) + IDASetMaxConvFails(_idaMemBlock, nFails); +} - void TimeIntegrator::setSolutionTimes(const std::vector& solutionTimes) - { - _solutionTimes = solutionTimes; - } +void TimeIntegrator::setSolutionTimes(const std::vector& solutionTimes) +{ + _solutionTimes = solutionTimes; +} } // namespace test diff --git a/test/TimeIntegrator.hpp b/test/TimeIntegrator.hpp index 4c2731b49..1264869bf 100644 --- a/test/TimeIntegrator.hpp +++ b/test/TimeIntegrator.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * ODE/DAE solver for tests */ @@ -35,7 +35,9 @@ namespace test class IDiffEqModel { public: - virtual ~IDiffEqModel() CADET_NOEXCEPT { } + virtual ~IDiffEqModel() CADET_NOEXCEPT + { + } /** * @brief Return the number of required DOFs @@ -48,23 +50,24 @@ class IDiffEqModel * @details This function is called after time integration of a section has finished and a new * section is about to be integrated. This allows the model to update internal state before * consistent initialization is performed. - * + * * This function is also called at the beginning of the time integration, which allows * the model to perform setup operations. - * + * * If AD is used by the model, the function has the opportunity to update the seed vectors. * The general initialization of the seed vectors is performed by prepareADvectors(). - * + * * @param [in] t Current time point * @param [in] secIdx Index of the new section that is about to be integrated * @param [in,out] vecStateY State of the simulation * @param [in,out] vecStateYdot Time derivative of simulation state */ - virtual void notifyDiscontinuousSectionTransition(double t, int secIdx, double* vecStateY, double* vecStateYdot) = 0; + virtual void notifyDiscontinuousSectionTransition(double t, int secIdx, double* vecStateY, + double* vecStateYdot) = 0; /** * @brief Computes the residual - * + * * @param [in] time Simulation time * @param [in] secIdx Current time section * @param [in] vecStateY State of the simulation @@ -76,7 +79,7 @@ class IDiffEqModel /** * @brief Computes the residual and updates the Jacobian - * + * * @param [in] time Simulation time * @param [in] secIdx Current time section * @param [in] vecStateY State of the simulation @@ -84,11 +87,12 @@ class IDiffEqModel * @param [out] res Pointer to global residual vector * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ - virtual int residualWithJacobian(double time, int secIdx, double const* vecStateY, double const* vecStateYdot, double* res) = 0; + virtual int residualWithJacobian(double time, int secIdx, double const* vecStateY, double const* vecStateYdot, + double* res) = 0; /** * @brief Computes the @f$ \ell^\infty@f$-norm of the residual vector - * + * * @param [in] time Simulation time * @param [in] secIdx Current time section * @param [in] vecStateY State of the simulation @@ -99,11 +103,12 @@ class IDiffEqModel /** * @brief Computes the solution of the linear system involving the system Jacobian - * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) x = b \f] - * has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the - * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, \dot{y}) \f$, - * may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned in @p rhs. - * + * @details The system \f[ \left( \frac{\partial F}{\partial y} + \alpha \frac{\partial F}{\partial \dot{y}} \right) + * x = b \f] has to be solved. The right hand side \f$ b \f$ is given by @p rhs, the Jacobians are evaluated at the + * point \f$(y, \dot{y})\f$ given by @p y and @p yDot. The residual @p res at this point, \f$ F(t, y, + * \dot{y}) \f$, may help with this. Error weights (see IDAS guide) are given in @p weight. The solution is returned + * in @p rhs. + * * Prior to calling linearSolve() the time integrator calls assembleDAEJacobian() with the same point * in time and state \f$(t, y, \dot{y})\f$. * @@ -117,7 +122,7 @@ class IDiffEqModel * @return @c 0 on success, @c -1 on non-recoverable error, and @c +1 on recoverable error */ virtual int linearSolve(double t, double alpha, double tol, double* rhs, double const* weight, - double const* vecStateY, double const* vecStateYdot) = 0; + double const* vecStateY, double const* vecStateYdot) = 0; /** * @brief Applies initial conditions to the state vector and its time derivative @@ -128,7 +133,7 @@ class IDiffEqModel * the initial conditions set by this function will be corrected for consistency. * Note that the state vector and its time derivative are pre-initialized with zero by the * time integrator. - * + * * @param [in,out] vecStateY State of the simulation * @param [in,out] vecStateYdot Time derivative of simulation state */ @@ -140,7 +145,7 @@ class IDiffEqModel * @param [in] vecStateY State of the simulation * @param [in] vecStateYdot Time derivative of simulation state */ - virtual void saveSolution(double t, double const* vecStateY, double const* vecStateYdot) = 0; + virtual void saveSolution(double t, double const* vecStateY, double const* vecStateYdot) = 0; }; /** @@ -151,7 +156,6 @@ class IDiffEqModel class TimeIntegrator { public: - TimeIntegrator(); ~TimeIntegrator() CADET_NOEXCEPT; @@ -164,8 +168,10 @@ class TimeIntegrator void integrate(); - void configureTimeIntegrator(double relTol, double absTol, double initStepSize, unsigned int maxSteps, double maxStepSize); - void configureTimeIntegrator(double relTol, double absTol, const std::vector& initStepSizes, unsigned int maxSteps, double maxStepSize); + void configureTimeIntegrator(double relTol, double absTol, double initStepSize, unsigned int maxSteps, + double maxStepSize); + void configureTimeIntegrator(double relTol, double absTol, const std::vector& initStepSizes, + unsigned int maxSteps, double maxStepSize); void setRelativeErrorTolerance(double relTol); void setAbsoluteErrorTolerance(double absTol); @@ -179,16 +185,24 @@ class TimeIntegrator void setMaxConvergenceFails(unsigned int nFails); void setMaxSensNewtonIteration(unsigned int nIter); - IDiffEqModel* model() CADET_NOEXCEPT { return _model; } - IDiffEqModel const* model() const CADET_NOEXCEPT { return _model; } + IDiffEqModel* model() CADET_NOEXCEPT + { + return _model; + } + IDiffEqModel const* model() const CADET_NOEXCEPT + { + return _model; + } - int currentSection() const CADET_NOEXCEPT { return _curSec; } + int currentSection() const CADET_NOEXCEPT + { + return _curSec; + } protected: - /** * @brief Computes the index of the next section from the given time @p t - * @details Returns the lowest index @c i with @f$ t_i \geq t @f$, where + * @details Returns the lowest index @c i with @f$ t_i \geq t @f$, where * @f$ t_i @f$ is an element of @c _sectionTimes. * @param [in] t Current time * @param [in] startIdx Index of the first section the search should begin with @@ -196,7 +210,7 @@ class TimeIntegrator */ int getNextSection(double t, int startIdx) const; - void updateMainErrorTolerances(); + void updateMainErrorTolerances(); IDiffEqModel* _model; //!< Simulated model, not owned by the Simulator @@ -204,27 +218,28 @@ class TimeIntegrator /** * @brief Determines whether the transition from section i to section i+1 is continuous. - * @details The solver will be reset only at discontinuous transitions. The i-th element - * corresponds to the transition from _sectionTimes[i+1] to _sectionTimes[i+2]. + * @details The solver will be reset only at discontinuous transitions. The i-th element + * corresponds to the transition from _sectionTimes[i+1] to _sectionTimes[i+2]. * Therefore size = nsec - 1. */ std::vector _sectionContinuity; - std::vector _solutionTimes; //!< Contains the time transformed user specified times for writing solutions to the output + std::vector + _solutionTimes; //!< Contains the time transformed user specified times for writing solutions to the output - N_Vector _vecStateY; //!< IDAS state vector - N_Vector _vecStateYdot; //!< IDAS state vector time derivative + N_Vector _vecStateY; //!< IDAS state vector + N_Vector _vecStateYdot; //!< IDAS state vector time derivative std::vector _sectionTimes; //!< Stores the section times - std::vector _absTol; //!< Absolute tolerance for the original system in the time integration - double _relTol; //!< Relative tolerance for the original system in the time integration + std::vector _absTol; //!< Absolute tolerance for the original system in the time integration + double _relTol; //!< Relative tolerance for the original system in the time integration std::vector _initStepSize; //!< Initial step size for the time integrator - unsigned int _maxSteps; //!< Maximum number of time integration steps - double _maxStepSize; //!< Maximum time step size + unsigned int _maxSteps; //!< Maximum number of time integration steps + double _maxStepSize; //!< Maximum time step size - unsigned int _maxNewtonIter; //!< Maximum number of Newton iterations for original DAE system + unsigned int _maxNewtonIter; //!< Maximum number of Newton iterations for original DAE system unsigned int _maxErrorTestFail; //!< Maximum number of local time integration error test failures - unsigned int _maxConvTestFail; //!< Maximum number of Newton iteration failures + unsigned int _maxConvTestFail; //!< Maximum number of Newton iteration failures int _curSec; //!< Index of the current section }; @@ -233,4 +248,4 @@ class TimeIntegrator } // namespace cadet -#endif // CADETTEST_TIMEINTEGRATOR_SKEL_HPP_ +#endif // CADETTEST_TIMEINTEGRATOR_SKEL_HPP_ diff --git a/test/TwoDimConvectionDispersionOperator.cpp b/test/TwoDimConvectionDispersionOperator.cpp index 2f9ea342e..7557cb0f6 100644 --- a/test/TwoDimConvectionDispersionOperator.cpp +++ b/test/TwoDimConvectionDispersionOperator.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -26,10 +26,11 @@ namespace { - inline void createAndConfigureOperator(cadet::model::parts::TwoDimensionalConvectionDispersionOperator& convDispOp, int nComp, int nCol, int nRad, int wenoOrder) - { - // Obtain parameters from some test case - cadet::JsonParameterProvider jpp(R"json({ +inline void createAndConfigureOperator(cadet::model::parts::TwoDimensionalConvectionDispersionOperator& convDispOp, + int nComp, int nCol, int nRad, int wenoOrder) +{ + // Obtain parameters from some test case + cadet::JsonParameterProvider jpp(R"json({ "COL_LENGTH": 10, "COL_RADIUS": 1, "COL_RADIUS_INNER": 0.001, @@ -47,59 +48,61 @@ namespace "WENO_ORDER": 3, "BOUNDARY_MODEL": 0, "WENO_EPS": 1e-10 - } + } } })json"); - cadet::test::column::setWenoOrder(jpp, wenoOrder); + cadet::test::column::setWenoOrder(jpp, wenoOrder); - std::vector cd(nRad * nComp, 1e-6); - jpp.set("COL_DISPERSION", cd); + std::vector cd(nRad * nComp, 1e-6); + jpp.set("COL_DISPERSION", cd); - std::vector cdr(nRad * nComp, 1e-4); - jpp.set("COL_DISPERSION_RADIAL", cdr); + std::vector cdr(nRad * nComp, 1e-4); + jpp.set("COL_DISPERSION_RADIAL", cdr); - std::vector v(2*nRad, 1.0); - for (int i = nRad; i < 2*nRad; ++i) - v[i] = -1.0; - jpp.set("VELOCITY", v); + std::vector v(2 * nRad, 1.0); + for (int i = nRad; i < 2 * nRad; ++i) + v[i] = -1.0; + jpp.set("VELOCITY", v); - // Configure the operator - typedef std::unordered_map ParameterMap; - ParameterMap parameters; - cadet::ModelBuilder builder; - REQUIRE(convDispOp.configureModelDiscretization(jpp, builder, nComp, nCol, nRad, false)); - REQUIRE(convDispOp.configure(0, jpp, parameters)); - } + // Configure the operator + typedef std::unordered_map ParameterMap; + ParameterMap parameters; + cadet::ModelBuilder builder; + REQUIRE(convDispOp.configureModelDiscretization(jpp, builder, nComp, nCol, nRad, false)); + REQUIRE(convDispOp.configure(0, jpp, parameters)); +} - inline void compareSparseJacobianAgainstFD(cadet::model::parts::TwoDimensionalConvectionDispersionOperator& convDispOp, int nInletDof, int nPureDof, double* y, double* jacCol1, double* jacCol2, double h, double relTol, double absTol) +inline void compareSparseJacobianAgainstFD(cadet::model::parts::TwoDimensionalConvectionDispersionOperator& convDispOp, + int nInletDof, int nPureDof, double* y, double* jacCol1, double* jacCol2, + double h, double relTol, double absTol) +{ + for (int col = 0; col < nPureDof; ++col) { - for (int col = 0; col < nPureDof; ++col) - { - const double ref = y[nInletDof + col]; + const double ref = y[nInletDof + col]; - // Central finite differences - y[nInletDof + col] = ref * (1.0 + h); - convDispOp.residual(DummyModel(), 0.0, 0u, y, nullptr, jacCol1, false, cadet::WithoutParamSensitivity()); + // Central finite differences + y[nInletDof + col] = ref * (1.0 + h); + convDispOp.residual(DummyModel(), 0.0, 0u, y, nullptr, jacCol1, false, cadet::WithoutParamSensitivity()); - y[nInletDof + col] = ref * (1.0 - h); - convDispOp.residual(DummyModel(), 0.0, 0u, y, nullptr, jacCol2, false, cadet::WithoutParamSensitivity()); + y[nInletDof + col] = ref * (1.0 - h); + convDispOp.residual(DummyModel(), 0.0, 0u, y, nullptr, jacCol2, false, cadet::WithoutParamSensitivity()); - y[nInletDof + col] = ref; + y[nInletDof + col] = ref; - for (int row = 0; row < nPureDof; ++row) - { - const double fdVal = (jacCol1[row + nInletDof] - jacCol2[row + nInletDof]) / (2.0 * ref * h); - const bool isFDnonZero = (fdVal != 0.0); + for (int row = 0; row < nPureDof; ++row) + { + const double fdVal = (jacCol1[row + nInletDof] - jacCol2[row + nInletDof]) / (2.0 * ref * h); + const bool isFDnonZero = (fdVal != 0.0); - if (isFDnonZero || convDispOp.jacobian().isNonZero(row, col)) - { - CAPTURE(row); - CAPTURE(col); - CHECK(convDispOp.jacobian().native(row, col) == cadet::test::makeApprox(fdVal, relTol, absTol)); - } + if (isFDnonZero || convDispOp.jacobian().isNonZero(row, col)) + { + CAPTURE(row); + CAPTURE(col); + CHECK(convDispOp.jacobian().native(row, col) == cadet::test::makeApprox(fdVal, relTol, absTol)); } - } + } } +} } // namespace @@ -127,24 +130,31 @@ void testBulk2DJacobianWenoForwardBackward(int wenoOrder) std::vector jacCol2(nDof, 0.0); // Fill state vector with some values - cadet::test::util::populate(y.data() + nInletDof, [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + std::abs(std::sin(idx * 0.3)) + 1e-4; }, nPureDof); + cadet::test::util::populate( + y.data() + nInletDof, + [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + std::abs(std::sin(idx * 0.3)) + 1e-4; }, + nPureDof); // Compute Jacobian for (int i = 0; i < nRad; ++i) convDispOp.setFlowRates(i, 1e-2 * convDispOp.crossSection(i) * convDispOp.columnPorosity(i), 0.0); convDispOp.notifyDiscontinuousSectionTransition(0.0, 0u); - convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, jacCol1.data(), true, cadet::WithoutParamSensitivity()); + convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, jacCol1.data(), true, + cadet::WithoutParamSensitivity()); // Compare Jacobian pattern against FD - compareSparseJacobianAgainstFD(convDispOp, nInletDof, nPureDof, y.data(), jacCol1.data(), jacCol2.data(), h, relTol, absTol); + compareSparseJacobianAgainstFD(convDispOp, nInletDof, nPureDof, y.data(), jacCol1.data(), jacCol2.data(), h, + relTol, absTol); // Reverse flow convDispOp.notifyDiscontinuousSectionTransition(0.0, 1u); - convDispOp.residual(DummyModel(), 0.0, 1u, y.data(), nullptr, jacCol1.data(), true, cadet::WithoutParamSensitivity()); + convDispOp.residual(DummyModel(), 0.0, 1u, y.data(), nullptr, jacCol1.data(), true, + cadet::WithoutParamSensitivity()); // Compare Jacobian pattern against FD - compareSparseJacobianAgainstFD(convDispOp, nInletDof, nPureDof, y.data(), jacCol1.data(), jacCol2.data(), h, relTol, absTol); + compareSparseJacobianAgainstFD(convDispOp, nInletDof, nPureDof, y.data(), jacCol1.data(), jacCol2.data(), h, + relTol, absTol); } } @@ -169,14 +179,18 @@ void testBulk2DJacobianSparsityWeno(int wenoOrder, bool forwardFlow) std::vector jacCol2(nDof, 0.0); // Fill state vector with some values - cadet::test::util::populate(y.data() + nInletDof, [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + std::abs(std::sin(idx * 0.3)) + 1e-4; }, nPureDof); + cadet::test::util::populate( + y.data() + nInletDof, + [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + std::abs(std::sin(idx * 0.3)) + 1e-4; }, + nPureDof); // Compute Jacobian for (int i = 0; i < nRad; ++i) convDispOp.setFlowRates(i, 1e-2 * convDispOp.crossSection(i) * convDispOp.columnPorosity(i), 0.0); convDispOp.notifyDiscontinuousSectionTransition(0.0, 0u); - convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, jacCol1.data(), true, cadet::WithoutParamSensitivity()); + convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, jacCol1.data(), true, + cadet::WithoutParamSensitivity()); // Compare Jacobian pattern with FD for (int col = 0; col < nPureDof; ++col) @@ -185,10 +199,12 @@ void testBulk2DJacobianSparsityWeno(int wenoOrder, bool forwardFlow) // Central finite differences y[nInletDof + col] = ref * (1.0 + h); - convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, jacCol1.data(), false, cadet::WithoutParamSensitivity()); + convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, jacCol1.data(), false, + cadet::WithoutParamSensitivity()); y[nInletDof + col] = ref * (1.0 - h); - convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, jacCol2.data(), false, cadet::WithoutParamSensitivity()); + convDispOp.residual(DummyModel(), 0.0, 0u, y.data(), nullptr, jacCol2.data(), false, + cadet::WithoutParamSensitivity()); y[nInletDof + col] = ref; @@ -206,14 +222,16 @@ void testBulk2DJacobianSparsityWeno(int wenoOrder, bool forwardFlow) } } -TEST_CASE("TwoDimensionalConvectionDispersionOperator Jacobian forward vs backward flow", "[2D],[Operator],[Residual],[Jacobian]") +TEST_CASE("TwoDimensionalConvectionDispersionOperator Jacobian forward vs backward flow", + "[2D],[Operator],[Residual],[Jacobian]") { // Test all WENO orders for (unsigned int i = 1; i <= cadet::Weno::maxOrder(); ++i) testBulk2DJacobianWenoForwardBackward(i); } -TEST_CASE("TwoDimensionalConvectionDispersionOperator Jacobian sparsity pattern vs FD", "[2D],[Operator],[Residual],[Jacobian],[SparseMatrix]") +TEST_CASE("TwoDimensionalConvectionDispersionOperator Jacobian sparsity pattern vs FD", + "[2D],[Operator],[Residual],[Jacobian],[SparseMatrix]") { SECTION("Forward flow") { diff --git a/test/UnitOperationTests.cpp b/test/UnitOperationTests.cpp index 6fb5ae38f..6159cffea 100644 --- a/test/UnitOperationTests.cpp +++ b/test/UnitOperationTests.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -41,323 +41,341 @@ namespace test namespace unitoperation { - cadet::IUnitOperation* createAndConfigureUnit(cadet::JsonParameterProvider& jpp, cadet::IModelBuilder& mb) - { - return createAndConfigureUnit(jpp.getString("UNIT_TYPE"), mb, jpp); - } - - cadet::IUnitOperation* createAndConfigureUnit(const std::string& uoType, cadet::IModelBuilder& mb, cadet::JsonParameterProvider& jpp) - { - // Create a unit - cadet::IModel* const iUnit = mb.createUnitOperation(jpp, 0); - REQUIRE(nullptr != iUnit); +cadet::IUnitOperation* createAndConfigureUnit(cadet::JsonParameterProvider& jpp, cadet::IModelBuilder& mb) +{ + return createAndConfigureUnit(jpp.getString("UNIT_TYPE"), mb, jpp); +} - cadet::IUnitOperation* const unit = reinterpret_cast(iUnit); +cadet::IUnitOperation* createAndConfigureUnit(const std::string& uoType, cadet::IModelBuilder& mb, + cadet::JsonParameterProvider& jpp) +{ + // Create a unit + cadet::IModel* const iUnit = mb.createUnitOperation(jpp, 0); + REQUIRE(nullptr != iUnit); - // Configure - cadet::ModelBuilder& temp = *reinterpret_cast(&mb); - REQUIRE(unit->configureModelDiscretization(jpp, temp)); - REQUIRE(unit->configure(jpp)); + cadet::IUnitOperation* const unit = reinterpret_cast(iUnit); - return unit; - } + // Configure + cadet::ModelBuilder& temp = *reinterpret_cast(&mb); + REQUIRE(unit->configureModelDiscretization(jpp, temp)); + REQUIRE(unit->configure(jpp)); - void testJacobianAD(cadet::JsonParameterProvider& jpp, const double absTolFDpattern) - { - cadet::IModelBuilder* const mb = cadet::createModelBuilder(); - REQUIRE(nullptr != mb); + return unit; +} - cadet::IUnitOperation* const unitAna = createAndConfigureUnit(jpp, *mb); - cadet::IUnitOperation* const unitAD = createAndConfigureUnit(jpp, *mb); +void testJacobianAD(cadet::JsonParameterProvider& jpp, const double absTolFDpattern) +{ + cadet::IModelBuilder* const mb = cadet::createModelBuilder(); + REQUIRE(nullptr != mb); + + cadet::IUnitOperation* const unitAna = createAndConfigureUnit(jpp, *mb); + cadet::IUnitOperation* const unitAD = createAndConfigureUnit(jpp, *mb); + + // Enable AD + cadet::ad::setDirections(cadet::ad::getMaxDirections()); + unitAD->useAnalyticJacobian(false); + + cadet::active* adRes = new cadet::active[unitAD->numDofs()]; + cadet::active* adY = new cadet::active[unitAD->numDofs()]; + + const AdJacobianParams noParams{nullptr, nullptr, 0u}; + const AdJacobianParams adParams{adRes, adY, 0u}; + + unitAD->prepareADvectors(adParams); + + // Obtain memory for state, Jacobian multiply direction, Jacobian column + std::vector y(unitAD->numDofs(), 0.0); + std::vector jacDir(unitAD->numDofs(), 0.0); + std::vector jacCol1(unitAD->numDofs(), 0.0); + std::vector jacCol2(unitAD->numDofs(), 0.0); + cadet::util::ThreadLocalStorage tls; + tls.resize(unitAna->threadLocalMemorySize()); + + // Fill state vector with some values + util::populate( + y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, unitAna->numDofs()); + // util::populate(y.data(), [](unsigned int idx) { return 1.0; }, unitAna->numDofs()); + + // Setup matrices + unitAna->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, noParams); + unitAD->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, adParams); + + // Compute state Jacobian + unitAna->residualWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), nullptr}, jacDir.data(), + noParams, tls); + unitAD->residualWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), nullptr}, jacDir.data(), + adParams, tls); + std::fill(jacDir.begin(), jacDir.end(), 0.0); + + // Compare Jacobians + cadet::test::checkJacobianPatternFD(unitAna, unitAD, y.data(), nullptr, jacDir.data(), jacCol1.data(), + jacCol2.data(), tls, absTolFDpattern); + cadet::test::checkJacobianPatternFD(unitAna, unitAna, y.data(), nullptr, jacDir.data(), jacCol1.data(), + jacCol2.data(), tls, absTolFDpattern); + cadet::test::compareJacobian(unitAna, unitAD, nullptr, nullptr, jacDir.data(), jacCol1.data(), jacCol2.data()); + // cadet::test::compareJacobianFD(unitAna, unitAD, y.data(), nullptr, jacDir.data(), jacCol1.data(), + // jacCol2.data()); + + delete[] adRes; + delete[] adY; + mb->destroyUnitOperation(unitAna); + mb->destroyUnitOperation(unitAD); + destroyModelBuilder(mb); +} + +void testTimeDerivativeJacobianFD(cadet::JsonParameterProvider& jpp, double h, double absTol, double relTol) +{ + cadet::IModelBuilder* const mb = cadet::createModelBuilder(); + REQUIRE(nullptr != mb); + + cadet::IUnitOperation* const unit = createAndConfigureUnit(jpp, *mb); + + // Obtain memory for state, Jacobian multiply direction, Jacobian column + const unsigned int nDof = unit->numDofs(); + std::vector y(nDof, 0.0); + std::vector yDot(nDof, 0.0); + std::vector jacDir(nDof, 0.0); + std::vector jacCol1(nDof, 0.0); + std::vector jacCol2(nDof, 0.0); + cadet::util::ThreadLocalStorage tls; + tls.resize(unit->threadLocalMemorySize()); + + // Fill state vectors with some values + util::populate(y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); + util::populate(yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); + + // Setup matrices + unit->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, + AdJacobianParams{nullptr, nullptr, 0u}); + + // Compare Jacobians + cadet::test::compareTimeDerivativeJacobianFD(unit, unit, y.data(), yDot.data(), jacDir.data(), jacCol1.data(), + jacCol2.data(), tls, h, absTol, relTol); + + mb->destroyUnitOperation(unit); + destroyModelBuilder(mb); +} + +void testConsistentInitialization(cadet::IUnitOperation* const unit, bool adEnabled, double* const y, double consTol, + double absTol) +{ + cadet::active* adRes = nullptr; + cadet::active* adY = nullptr; - // Enable AD + // Enable AD + if (adEnabled) + { cadet::ad::setDirections(cadet::ad::getMaxDirections()); - unitAD->useAnalyticJacobian(false); - - cadet::active* adRes = new cadet::active[unitAD->numDofs()]; - cadet::active* adY = new cadet::active[unitAD->numDofs()]; - - const AdJacobianParams noParams{nullptr, nullptr, 0u}; - const AdJacobianParams adParams{adRes, adY, 0u}; - - unitAD->prepareADvectors(adParams); - - // Obtain memory for state, Jacobian multiply direction, Jacobian column - std::vector y(unitAD->numDofs(), 0.0); - std::vector jacDir(unitAD->numDofs(), 0.0); - std::vector jacCol1(unitAD->numDofs(), 0.0); - std::vector jacCol2(unitAD->numDofs(), 0.0); - cadet::util::ThreadLocalStorage tls; - tls.resize(unitAna->threadLocalMemorySize()); - - // Fill state vector with some values - util::populate(y.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, unitAna->numDofs()); -// util::populate(y.data(), [](unsigned int idx) { return 1.0; }, unitAna->numDofs()); - - // Setup matrices - unitAna->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, noParams); - unitAD->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, adParams); - - // Compute state Jacobian - unitAna->residualWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), nullptr}, jacDir.data(), noParams, tls); - unitAD->residualWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y.data(), nullptr}, jacDir.data(), adParams, tls); - std::fill(jacDir.begin(), jacDir.end(), 0.0); - - // Compare Jacobians - cadet::test::checkJacobianPatternFD(unitAna, unitAD, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data(), tls, absTolFDpattern); - cadet::test::checkJacobianPatternFD(unitAna, unitAna, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data(), tls, absTolFDpattern); - cadet::test::compareJacobian(unitAna, unitAD, nullptr, nullptr, jacDir.data(), jacCol1.data(), jacCol2.data()); -// cadet::test::compareJacobianFD(unitAna, unitAD, y.data(), nullptr, jacDir.data(), jacCol1.data(), jacCol2.data()); - - delete[] adRes; - delete[] adY; - mb->destroyUnitOperation(unitAna); - mb->destroyUnitOperation(unitAD); - destroyModelBuilder(mb); - } + unit->useAnalyticJacobian(false); - void testTimeDerivativeJacobianFD(cadet::JsonParameterProvider& jpp, double h, double absTol, double relTol) + adRes = new cadet::active[unit->numDofs()]; + adY = new cadet::active[unit->numDofs()]; + unit->prepareADvectors(AdJacobianParams{adRes, adY, 0}); + } + else { - cadet::IModelBuilder* const mb = cadet::createModelBuilder(); - REQUIRE(nullptr != mb); - - cadet::IUnitOperation* const unit = createAndConfigureUnit(jpp, *mb); - - // Obtain memory for state, Jacobian multiply direction, Jacobian column - const unsigned int nDof = unit->numDofs(); - std::vector y(nDof, 0.0); - std::vector yDot(nDof, 0.0); - std::vector jacDir(nDof, 0.0); - std::vector jacCol1(nDof, 0.0); - std::vector jacCol2(nDof, 0.0); - cadet::util::ThreadLocalStorage tls; - tls.resize(unit->threadLocalMemorySize()); - - // Fill state vectors with some values - util::populate(y.data(), [=](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, nDof); - util::populate(yDot.data(), [=](unsigned int idx) { return std::abs(std::sin((idx + nDof) * 0.13)) + 1e-4; }, nDof); - - // Setup matrices - unit->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), yDot.data()}, AdJacobianParams{nullptr, nullptr, 0u}); - - // Compare Jacobians - cadet::test::compareTimeDerivativeJacobianFD(unit, unit, y.data(), yDot.data(), jacDir.data(), jacCol1.data(), jacCol2.data(), tls, h, absTol, relTol); - - mb->destroyUnitOperation(unit); - destroyModelBuilder(mb); + unit->useAnalyticJacobian(true); } - void testConsistentInitialization(cadet::IUnitOperation* const unit, bool adEnabled, double* const y, double consTol, double absTol) - { - cadet::active* adRes = nullptr; - cadet::active* adY = nullptr; + const AdJacobianParams adParams{adRes, adY, 0}; - // Enable AD - if (adEnabled) - { - cadet::ad::setDirections(cadet::ad::getMaxDirections()); - unit->useAnalyticJacobian(false); + // Obtain memory + std::vector yIn(y, y + unit->numComponents() * unit->numInletPorts()); + std::vector yDot(unit->numDofs(), 0.0); + std::vector res(unit->numDofs(), 0.0); + cadet::util::ThreadLocalStorage tls; + tls.resize(unit->threadLocalMemorySize()); - adRes = new cadet::active[unit->numDofs()]; - adY = new cadet::active[unit->numDofs()]; - unit->prepareADvectors(AdJacobianParams{adRes, adY, 0}); - } - else - { - unit->useAnalyticJacobian(true); - } + // Setup matrices + unit->notifyDiscontinuousSectionTransition(0.0, 0u, {y, yDot.data()}, adParams); - const AdJacobianParams adParams{adRes, adY, 0}; + // Initialize algebraic variables in state vector + unit->consistentInitialState(SimulationTime{0.0, 0u}, y, adParams, consTol, tls); - // Obtain memory - std::vector yIn(y, y + unit->numComponents() * unit->numInletPorts()); - std::vector yDot(unit->numDofs(), 0.0); - std::vector res(unit->numDofs(), 0.0); - cadet::util::ThreadLocalStorage tls; - tls.resize(unit->threadLocalMemorySize()); + // Make sure inlet DOFs have not been touched + for (std::size_t i = 0; i < yIn.size(); ++i) + CHECK(yIn[i] == y[i]); - // Setup matrices - unit->notifyDiscontinuousSectionTransition(0.0, 0u, {y, yDot.data()}, adParams); + // Evaluate residual without yDot and update Jacobians + // Store residual in yDot + unit->residualWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y, nullptr}, yDot.data(), adParams, tls); + CAPTURE(yDot); - // Initialize algebraic variables in state vector - unit->consistentInitialState(SimulationTime{0.0, 0u}, y, adParams, consTol, tls); + // Initialize time derivative of state + unit->consistentInitialTimeDerivative(SimulationTime{0.0, 0u}, y, yDot.data(), tls); - // Make sure inlet DOFs have not been touched - for (std::size_t i = 0; i < yIn.size(); ++i) - CHECK(yIn[i] == y[i]); + // Check norm of residual (but skip over connection DOFs at the beginning of the local state vector slice) + unit->residual(SimulationTime{0.0, 0u}, ConstSimulationState{y, yDot.data()}, res.data(), tls); + INFO("Residual " << linalg::linfNorm(res.data() + unit->numComponents() * unit->numInletPorts(), + unit->numDofs() - unit->numComponents() * unit->numInletPorts())); + CAPTURE(res); + CHECK(linalg::linfNorm(res.data() + unit->numComponents() * unit->numInletPorts(), + unit->numDofs() - unit->numComponents() * unit->numInletPorts()) <= absTol); - // Evaluate residual without yDot and update Jacobians - // Store residual in yDot - unit->residualWithJacobian(SimulationTime{0.0, 0u}, ConstSimulationState{y, nullptr}, yDot.data(), adParams, tls); - CAPTURE(yDot); + // Clean up + delete[] adRes; + delete[] adY; +} - // Initialize time derivative of state - unit->consistentInitialTimeDerivative(SimulationTime{0.0, 0u}, y, yDot.data(), tls); +void testConsistentInitializationSensitivity(cadet::IUnitOperation* const unit, bool adEnabled, double const* const y, + double const* const yDot, double absTol) +{ + const unsigned int nSens = unit->numSensParams(); + REQUIRE(nSens > 0); - // Check norm of residual (but skip over connection DOFs at the beginning of the local state vector slice) - unit->residual(SimulationTime{0.0, 0u}, ConstSimulationState{y, yDot.data()}, res.data(), tls); - INFO("Residual " << linalg::linfNorm(res.data() + unit->numComponents() * unit->numInletPorts(), unit->numDofs() - unit->numComponents() * unit->numInletPorts())); - CAPTURE(res); - CHECK(linalg::linfNorm(res.data() + unit->numComponents() * unit->numInletPorts(), unit->numDofs() - unit->numComponents() * unit->numInletPorts()) <= absTol); + cadet::active* adRes = new cadet::active[unit->numDofs()]; + cadet::active* adY = nullptr; - // Clean up - delete[] adRes; - delete[] adY; - } + // Enable AD + if (adEnabled) + { + cadet::ad::setDirections(cadet::ad::getMaxDirections()); + unit->useAnalyticJacobian(false); - void testConsistentInitializationSensitivity(cadet::IUnitOperation* const unit, bool adEnabled, double const* const y, double const* const yDot, double absTol) + adY = new cadet::active[unit->numDofs()]; + unit->prepareADvectors(AdJacobianParams{adRes, adY, nSens}); + } + else { - const unsigned int nSens = unit->numSensParams(); - REQUIRE(nSens > 0); + unit->useAnalyticJacobian(true); + } - cadet::active* adRes = new cadet::active[unit->numDofs()]; - cadet::active* adY = nullptr; + const AdJacobianParams adParams{adRes, adY, nSens}; - // Enable AD - if (adEnabled) - { - cadet::ad::setDirections(cadet::ad::getMaxDirections()); - unit->useAnalyticJacobian(false); + cadet::util::ThreadLocalStorage tls; + tls.resize(unit->threadLocalMemorySize()); - adY = new cadet::active[unit->numDofs()]; - unit->prepareADvectors(AdJacobianParams{adRes, adY, nSens}); - } - else - { - unit->useAnalyticJacobian(true); - } + // Setup matrices + unit->notifyDiscontinuousSectionTransition(0.0, 0u, {nullptr, nullptr}, adParams); - const AdJacobianParams adParams{adRes, adY, nSens}; + // Calculate dres / dp and Jacobians + const SimulationTime simTime{0.0, 0u}; + unit->residualSensFwdWithJacobian(simTime, ConstSimulationState{y, yDot}, adParams, tls); - cadet::util::ThreadLocalStorage tls; - tls.resize(unit->threadLocalMemorySize()); + // Obtain memory + std::vector sensY(unit->numDofs() * nSens, 0.0); + std::vector sensYdot(unit->numDofs() * nSens, 0.0); + std::vector res(unit->numDofs() * nSens, 0.0); - // Setup matrices - unit->notifyDiscontinuousSectionTransition(0.0, 0u, {nullptr, nullptr}, adParams); + std::vector vecSensY(nSens, nullptr); + std::vector vecSensYdot(nSens, nullptr); + std::vector cVecSensY(nSens, nullptr); + std::vector cVecSensYdot(nSens, nullptr); + std::vector vecRes(nSens, nullptr); - // Calculate dres / dp and Jacobians - const SimulationTime simTime{0.0, 0u}; - unit->residualSensFwdWithJacobian(simTime, ConstSimulationState{y, yDot}, adParams, tls); + for (unsigned int i = 0; i < nSens; ++i) + { + vecSensY[i] = sensY.data() + i * unit->numDofs(); + vecSensYdot[i] = sensYdot.data() + i * unit->numDofs(); + cVecSensY[i] = sensY.data() + i * unit->numDofs(); + cVecSensYdot[i] = sensYdot.data() + i * unit->numDofs(); + vecRes[i] = res.data() + i * unit->numDofs(); + } - // Obtain memory - std::vector sensY(unit->numDofs() * nSens, 0.0); - std::vector sensYdot(unit->numDofs() * nSens, 0.0); - std::vector res(unit->numDofs() * nSens, 0.0); + std::vector tmp1(unit->numDofs(), 0.0); + std::vector tmp2(unit->numDofs(), 0.0); + std::vector tmp3(unit->numDofs(), 0.0); - std::vector vecSensY(nSens, nullptr); - std::vector vecSensYdot(nSens, nullptr); - std::vector cVecSensY(nSens, nullptr); - std::vector cVecSensYdot(nSens, nullptr); - std::vector vecRes(nSens, nullptr); + // Fill sensY and sensYdot with some values + util::populate(sensY.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, sensY.size()); + util::populate( + sensYdot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, sensYdot.size()); - for (unsigned int i = 0; i < nSens; ++i) - { - vecSensY[i] = sensY.data() + i * unit->numDofs(); - vecSensYdot[i] = sensYdot.data() + i * unit->numDofs(); - cVecSensY[i] = sensY.data() + i * unit->numDofs(); - cVecSensYdot[i] = sensYdot.data() + i * unit->numDofs(); - vecRes[i] = res.data() + i * unit->numDofs(); - } + // Initialize sensitivity state vectors + unit->consistentInitialSensitivity(simTime, ConstSimulationState{y, yDot}, vecSensY, vecSensYdot, adRes, tls); - std::vector tmp1(unit->numDofs(), 0.0); - std::vector tmp2(unit->numDofs(), 0.0); - std::vector tmp3(unit->numDofs(), 0.0); + // Check norm of residual (but skip over connection DOFs at the beginning of the local state vector slice) + unit->residualSensFwdAdOnly(simTime, ConstSimulationState{y, yDot}, adRes, tls); + unit->residualSensFwdCombine(simTime, ConstSimulationState{y, yDot}, cVecSensY, cVecSensYdot, vecRes, adRes, + tmp1.data(), tmp2.data(), tmp3.data()); - // Fill sensY and sensYdot with some values - util::populate(sensY.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.13)) + 1e-4; }, sensY.size()); - util::populate(sensYdot.data(), [](unsigned int idx) { return std::abs(std::sin(idx * 0.9)) + 1e-4; }, sensYdot.size()); + for (unsigned int i = 0; i < nSens; ++i) + { + INFO("Residual " << i << ": " + << linalg::linfNorm(vecRes[i] + unit->numComponents() * unit->numInletPorts(), + unit->numDofs() - unit->numComponents() * unit->numInletPorts())); + CHECK(linalg::linfNorm(vecRes[i] + unit->numComponents() * unit->numInletPorts(), + unit->numDofs() - unit->numComponents() * unit->numInletPorts()) <= absTol); + } - // Initialize sensitivity state vectors - unit->consistentInitialSensitivity(simTime, ConstSimulationState{y, yDot}, vecSensY, vecSensYdot, adRes,tls); + // Clean up + delete[] adRes; + delete[] adY; +} - // Check norm of residual (but skip over connection DOFs at the beginning of the local state vector slice) - unit->residualSensFwdAdOnly(simTime, ConstSimulationState{y, yDot}, adRes, tls); - unit->residualSensFwdCombine(simTime, ConstSimulationState{y, yDot}, cVecSensY, cVecSensYdot, vecRes, adRes, tmp1.data(), tmp2.data(), tmp3.data()); +void testInletDofJacobian(cadet::IUnitOperation* const unit, bool adEnabled) +{ + cadet::active* adRes = nullptr; + cadet::active* adY = nullptr; - for (unsigned int i = 0; i < nSens; ++i) - { - INFO("Residual " << i << ": " << linalg::linfNorm(vecRes[i] + unit->numComponents() * unit->numInletPorts(), unit->numDofs() - unit->numComponents() * unit->numInletPorts())); - CHECK(linalg::linfNorm(vecRes[i] + unit->numComponents() * unit->numInletPorts(), unit->numDofs() - unit->numComponents() * unit->numInletPorts()) <= absTol); - } + // Enable AD + if (adEnabled) + { + cadet::ad::setDirections(cadet::ad::getMaxDirections()); + unit->useAnalyticJacobian(false); - // Clean up - delete[] adRes; - delete[] adY; + adRes = new cadet::active[unit->numDofs()]; + adY = new cadet::active[unit->numDofs()]; + unit->prepareADvectors(AdJacobianParams{adRes, adY, 0}); } - - void testInletDofJacobian(cadet::IUnitOperation* const unit, bool adEnabled) + else { - cadet::active* adRes = nullptr; - cadet::active* adY = nullptr; - - // Enable AD - if (adEnabled) - { - cadet::ad::setDirections(cadet::ad::getMaxDirections()); - unit->useAnalyticJacobian(false); + unit->useAnalyticJacobian(true); + } - adRes = new cadet::active[unit->numDofs()]; - adY = new cadet::active[unit->numDofs()]; - unit->prepareADvectors(AdJacobianParams{adRes, adY, 0}); - } - else - { - unit->useAnalyticJacobian(true); - } + const AdJacobianParams adParams{adRes, adY, 0}; - const AdJacobianParams adParams{adRes, adY, 0}; + // Obtain memory + std::vector y(unit->numDofs(), 0.0); + std::vector jac(unit->numDofs(), 0.0); + cadet::util::ThreadLocalStorage tls; + tls.resize(unit->threadLocalMemorySize()); - // Obtain memory - std::vector y(unit->numDofs(), 0.0); - std::vector jac(unit->numDofs(), 0.0); - cadet::util::ThreadLocalStorage tls; - tls.resize(unit->threadLocalMemorySize()); + // Setup matrices + unit->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, adParams); - // Setup matrices - unit->notifyDiscontinuousSectionTransition(0.0, 0u, {y.data(), nullptr}, adParams); + // Assemble Jacobian + const SimulationTime simTime{0.0, 0u}; + const ConstSimulationState simState{y.data(), nullptr}; + unit->residualWithJacobian(simTime, simState, jac.data(), adParams, tls); - // Assemble Jacobian - const SimulationTime simTime{0.0, 0u}; - const ConstSimulationState simState{y.data(), nullptr}; - unit->residualWithJacobian(simTime, simState, jac.data(), adParams, tls); + // Check ports and components + const unsigned int inletBlockSize = unit->numInletPorts() * unit->numComponents(); + CHECK(inletBlockSize == unit->numDofs() - unit->numPureDofs()); - // Check ports and components - const unsigned int inletBlockSize = unit->numInletPorts() * unit->numComponents(); - CHECK(inletBlockSize == unit->numDofs() - unit->numPureDofs()); + for (unsigned int port = 0; port < unit->numInletPorts(); ++port) + { + const unsigned int inletOffset = unit->localInletComponentIndex(port); + const unsigned int inletStride = unit->localInletComponentStride(port); - for (unsigned int port = 0; port < unit->numInletPorts(); ++port) + for (unsigned int comp = 0; comp < unit->numComponents(); ++comp) { - const unsigned int inletOffset = unit->localInletComponentIndex(port); - const unsigned int inletStride = unit->localInletComponentStride(port); + // Get Jacobian column + const unsigned int curItem = inletOffset + comp * inletStride; - for (unsigned int comp = 0; comp < unit->numComponents(); ++comp) + y[curItem] = 1.0; + unit->multiplyWithJacobian(SimulationTime{0.0, 0u}, simState, y.data(), 1.0, 0.0, jac.data()); + y[curItem] = 0.0; + + // Check inlet block of Jacobian column + for (unsigned int i = 0; i < inletBlockSize; ++i) { - // Get Jacobian column - const unsigned int curItem = inletOffset + comp * inletStride; - - y[curItem] = 1.0; - unit->multiplyWithJacobian(SimulationTime{0.0, 0u}, simState, y.data(), 1.0, 0.0, jac.data()); - y[curItem] = 0.0; - - // Check inlet block of Jacobian column - for (unsigned int i = 0; i < inletBlockSize; ++i) - { - CAPTURE(port); - CAPTURE(comp); - CAPTURE(curItem); - if (i == curItem) - CHECK(jac[i] == 1.0); - else - CHECK(jac[i] == 0.0); - } + CAPTURE(port); + CAPTURE(comp); + CAPTURE(curItem); + if (i == curItem) + CHECK(jac[i] == 1.0); + else + CHECK(jac[i] == 0.0); } } - - // Clean up - delete[] adRes; - delete[] adY; } + // Clean up + delete[] adRes; + delete[] adY; +} + } // namespace unitoperation } // namespace test } // namespace cadet diff --git a/test/UnitOperationTests.hpp b/test/UnitOperationTests.hpp index 1c4cca985..900486963 100644 --- a/test/UnitOperationTests.hpp +++ b/test/UnitOperationTests.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines tests for general unit operations. */ @@ -30,71 +30,74 @@ namespace test namespace unitoperation { - /** - * @brief Creates a runnable unit operation model - * @details Creates a unit operation model and configures it using the given IParameterProvider @p jpp. - * @param [in] jpp Configuration of the model - * @param [in] mb ModelBuilder - * @return Runnable unit operation model - */ - cadet::IUnitOperation* createAndConfigureUnit(cadet::JsonParameterProvider& jpp, cadet::IModelBuilder& mb); +/** + * @brief Creates a runnable unit operation model + * @details Creates a unit operation model and configures it using the given IParameterProvider @p jpp. + * @param [in] jpp Configuration of the model + * @param [in] mb ModelBuilder + * @return Runnable unit operation model + */ +cadet::IUnitOperation* createAndConfigureUnit(cadet::JsonParameterProvider& jpp, cadet::IModelBuilder& mb); - /** - * @brief Creates a runnable unit operation model - * @details Creates a unit operation model and configures it using the given IParameterProvider @p jpp. - * @param [in] uoType Unit operation type - * @param [in] mb ModelBuilder - * @param [in] jpp Configuration of the model - * @return Runnable unit operation model - */ - cadet::IUnitOperation* createAndConfigureUnit(const std::string& uoType, cadet::IModelBuilder& mb, cadet::JsonParameterProvider& jpp); +/** + * @brief Creates a runnable unit operation model + * @details Creates a unit operation model and configures it using the given IParameterProvider @p jpp. + * @param [in] uoType Unit operation type + * @param [in] mb ModelBuilder + * @param [in] jpp Configuration of the model + * @return Runnable unit operation model + */ +cadet::IUnitOperation* createAndConfigureUnit(const std::string& uoType, cadet::IModelBuilder& mb, + cadet::JsonParameterProvider& jpp); - /** - * @brief Checks the full analytic Jacobian against AD for a given model - * @param [in] jpp Unit operation configuration - * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern - */ - void testJacobianAD(cadet::JsonParameterProvider& jpp, const double absTolFDpattern=0.0); +/** + * @brief Checks the full analytic Jacobian against AD for a given model + * @param [in] jpp Unit operation configuration + * @param [in] absTolFDpattern absolute tolerance when comparing the sign in the FD Jacobian pattern + */ +void testJacobianAD(cadet::JsonParameterProvider& jpp, const double absTolFDpattern = 0.0); - /** - * @brief Checks the (analytic) time derivative Jacobian against FD for a given model - * @details Uses centered finite differences. - * @param [in] jpp Unit operation configuration - * @param [in] h Step size of centered finite differences - * @param [in] absTol Absolute error tolerance - * @param [in] relTol Relative error tolerance - */ - void testTimeDerivativeJacobianFD(cadet::JsonParameterProvider& jpp, double h, double absTol, double relTol); +/** + * @brief Checks the (analytic) time derivative Jacobian against FD for a given model + * @details Uses centered finite differences. + * @param [in] jpp Unit operation configuration + * @param [in] h Step size of centered finite differences + * @param [in] absTol Absolute error tolerance + * @param [in] relTol Relative error tolerance + */ +void testTimeDerivativeJacobianFD(cadet::JsonParameterProvider& jpp, double h, double absTol, double relTol); - /** - * @brief Checks the consistent initialization of a generic unit operation - * @param [in] unit Configured unit operation - * @param [in] adEnabled Determines whether AD is used for calculating the Jacobian - * @param [in] y Initial state to start consistent initialization from - * @param [in] consTol Tolerance for the consistent initialization algorithm - * @param [in] absTol Allowed maximum residual error after consistent initialization - */ - void testConsistentInitialization(IUnitOperation* const unit, bool adEnabled, double* const y, double consTol, double absTol); +/** + * @brief Checks the consistent initialization of a generic unit operation + * @param [in] unit Configured unit operation + * @param [in] adEnabled Determines whether AD is used for calculating the Jacobian + * @param [in] y Initial state to start consistent initialization from + * @param [in] consTol Tolerance for the consistent initialization algorithm + * @param [in] absTol Allowed maximum residual error after consistent initialization + */ +void testConsistentInitialization(IUnitOperation* const unit, bool adEnabled, double* const y, double consTol, + double absTol); - /** - * @brief Checks the consistent initialization of the sensitivies of a generic unit operation - * @param [in] unit Configured unit operation - * @param [in] adEnabled Determines whether AD is used for calculating the Jacobian - * @param [in] y State of original system - * @param [in] yDot Time derivative of state of original system - * @param [in] absTol Allowed maximum residual error after consistent initialization - */ - void testConsistentInitializationSensitivity(cadet::IUnitOperation* const unit, bool adEnabled, double const* const y, double const* const yDot, double absTol); +/** + * @brief Checks the consistent initialization of the sensitivies of a generic unit operation + * @param [in] unit Configured unit operation + * @param [in] adEnabled Determines whether AD is used for calculating the Jacobian + * @param [in] y State of original system + * @param [in] yDot Time derivative of state of original system + * @param [in] absTol Allowed maximum residual error after consistent initialization + */ +void testConsistentInitializationSensitivity(cadet::IUnitOperation* const unit, bool adEnabled, double const* const y, + double const* const yDot, double absTol); - /** - * @brief Checks whether the inlet DOFs produce the identity matrix in the Jacobian of the unit operation - * @param [in] unit Configured unit operation - * @param [in] adEnabled Determines whether AD is used for calculating the Jacobian - */ - void testInletDofJacobian(cadet::IUnitOperation* const unit, bool adEnabled); +/** + * @brief Checks whether the inlet DOFs produce the identity matrix in the Jacobian of the unit operation + * @param [in] unit Configured unit operation + * @param [in] adEnabled Determines whether AD is used for calculating the Jacobian + */ +void testInletDofJacobian(cadet::IUnitOperation* const unit, bool adEnabled); } // namespace unitoperation } // namespace test } // namespace cadet -#endif // CADETTEST_UNITOPTESTS_HPP_ +#endif // CADETTEST_UNITOPTESTS_HPP_ diff --git a/test/Utils.hpp b/test/Utils.hpp index 8a05a525f..afddddf98 100644 --- a/test/Utils.hpp +++ b/test/Utils.hpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -11,7 +11,7 @@ // ============================================================================= /** - * @file + * @file * Defines utility functions for tests. */ @@ -32,189 +32,184 @@ namespace test namespace util { - /** - * @brief Fills the vector with the values of a given function - * @details The function @p f uses the current index to assign a value. - * @param [out] y Populated vector - * @param [in] f Function for computing the content of the vector - * @param [in] numDofs Size of the vector - */ - inline void populate(double* y, std::function f, unsigned int numDofs) +/** + * @brief Fills the vector with the values of a given function + * @details The function @p f uses the current index to assign a value. + * @param [out] y Populated vector + * @param [in] f Function for computing the content of the vector + * @param [in] numDofs Size of the vector + */ +inline void populate(double* y, std::function f, unsigned int numDofs) +{ + for (unsigned int i = 0; i < numDofs; ++i) + y[i] = f(i); +} + +/** + * @brief Sequentially places a sequence multiple times into an array + * @details Writes a given sequence multiple times into an array by concatenation. + * Writes a total of @c size * times elements. + * @param [out] dest Target array + * @param [in] src Source sequence + * @param [in] size Length of the source sequence + * @param [in] times Number of repetitions of the source sequence + */ +inline void repeat(double* dest, double const* src, unsigned int size, unsigned int times) +{ + for (unsigned int i = 0; i < times; ++i, dest += size) { - for (unsigned int i = 0; i < numDofs; ++i) - y[i] = f(i); + std::copy(src, src + size, dest); } +} +/** + * @brief ParameterProvider scope guard for unit_XYZ group + * @details Opens and closes the unit_XYZ ParameterProvider scope. + */ +template class ModelGroupScope +{ +public: /** - * @brief Sequentially places a sequence multiple times into an array - * @details Writes a given sequence multiple times into an array by concatenation. - * Writes a total of @c size * times elements. - * @param [out] dest Target array - * @param [in] src Source sequence - * @param [in] size Length of the source sequence - * @param [in] times Number of repetitions of the source sequence + * @brief Enters a given unit_XYZ scope + * @param [in,out] jpp ParameterProvider + * @param [in] idxUnit Index of unit operation */ - inline void repeat(double* dest, double const* src, unsigned int size, unsigned int times) + ModelGroupScope(ParamProvider& jpp, unsigned int idxUnit) : _jpp(jpp) { - for (unsigned int i = 0; i < times; ++i, dest += size) + _active = jpp.exists("model"); + if (_active) { - std::copy(src, src + size, dest); + std::ostringstream ss; + _jpp.pushScope("model"); + ss << "unit_" << std::setfill('0') << std::setw(3) << idxUnit; + _jpp.pushScope(ss.str()); } } /** - * @brief ParameterProvider scope guard for unit_XYZ group - * @details Opens and closes the unit_XYZ ParameterProvider scope. + * @brief Enters unit_000 scope + * @param [in,out] jpp ParameterProvider */ - template - class ModelGroupScope + ModelGroupScope(ParamProvider& jpp) : ModelGroupScope(jpp, 0) + { + } + + ~ModelGroupScope() { - public: - - /** - * @brief Enters a given unit_XYZ scope - * @param [in,out] jpp ParameterProvider - * @param [in] idxUnit Index of unit operation - */ - ModelGroupScope(ParamProvider& jpp, unsigned int idxUnit) : _jpp(jpp) + if (_active) { - _active = jpp.exists("model"); - if (_active) - { - std::ostringstream ss; - _jpp.pushScope("model"); - ss << "unit_" << std::setfill('0') << std::setw(3) << idxUnit; - _jpp.pushScope(ss.str()); - } + _jpp.popScope(); + _jpp.popScope(); } + } - /** - * @brief Enters unit_000 scope - * @param [in,out] jpp ParameterProvider - */ - ModelGroupScope(ParamProvider& jpp) : ModelGroupScope(jpp, 0) { } +private: + bool _active; + ParamProvider& _jpp; +}; - ~ModelGroupScope() - { - if (_active) - { - _jpp.popScope(); - _jpp.popScope(); - } - } +/** + * @brief Enters a given unit_XYZ and leaves it when leaving the current scope + * @param [in,out] jpp ParameterProvider + * @param [in] idxUnit Index of unit operation + */ +template +inline ModelGroupScope makeModelGroupScope(ParamProvider& jpp, unsigned int idxUnit) +{ + return ModelGroupScope(jpp, idxUnit); +} - private: - bool _active; - ParamProvider& _jpp; - }; +/** + * @brief Enters a given unit_000 and leaves it when leaving the current scope + * @param [in,out] jpp ParameterProvider + */ +template inline ModelGroupScope makeModelGroupScope(ParamProvider& jpp) +{ + return ModelGroupScope(jpp); +} +/** + * @brief ParameterProvider scope guard for possibly existent group + * @details Opens and closes the given ParameterProvider scope if it exists. + */ +template class OptionalGroupScope +{ +public: /** - * @brief Enters a given unit_XYZ and leaves it when leaving the current scope + * @brief Enters a given scope if it exists * @param [in,out] jpp ParameterProvider - * @param [in] idxUnit Index of unit operation + * @param [in] grp Name of group */ - template - inline ModelGroupScope makeModelGroupScope(ParamProvider& jpp, unsigned int idxUnit) + OptionalGroupScope(ParamProvider& jpp, const std::string& grp) : _jpp(jpp) { - return ModelGroupScope(jpp, idxUnit); + _active = jpp.exists(grp); + if (_active) + _jpp.pushScope(grp); } - /** - * @brief Enters a given unit_000 and leaves it when leaving the current scope - * @param [in,out] jpp ParameterProvider - */ - template - inline ModelGroupScope makeModelGroupScope(ParamProvider& jpp) + ~OptionalGroupScope() { - return ModelGroupScope(jpp); + if (_active) + _jpp.popScope(); } - /** - * @brief ParameterProvider scope guard for possibly existent group - * @details Opens and closes the given ParameterProvider scope if it exists. - */ - template - class OptionalGroupScope - { - public: - - /** - * @brief Enters a given scope if it exists - * @param [in,out] jpp ParameterProvider - * @param [in] grp Name of group - */ - OptionalGroupScope(ParamProvider& jpp, const std::string& grp) : _jpp(jpp) - { - _active = jpp.exists(grp); - if (_active) - _jpp.pushScope(grp); - } - - ~OptionalGroupScope() - { - if (_active) - _jpp.popScope(); - } +private: + bool _active; + ParamProvider& _jpp; +}; - private: - bool _active; - ParamProvider& _jpp; - }; +/** + * @brief Enters a given group and leaves it when leaving the current scope + * @details Does nothing if the group does not exist. + * @param [in,out] jpp ParameterProvider + * @param [in] grp Name of the group + */ +template +inline OptionalGroupScope makeOptionalGroupScope(ParamProvider& jpp, const std::string& grp) +{ + return OptionalGroupScope(jpp, grp); +} +/** + * @brief ParameterProvider scope guard for existent group + * @details Opens and closes the given ParameterProvider scope. + */ +template class GroupScope +{ +public: /** - * @brief Enters a given group and leaves it when leaving the current scope - * @details Does nothing if the group does not exist. + * @brief Enters a given scope if it exists * @param [in,out] jpp ParameterProvider - * @param [in] grp Name of the group + * @param [in] grp Name of group */ - template - inline OptionalGroupScope makeOptionalGroupScope(ParamProvider& jpp, const std::string& grp) + GroupScope(ParamProvider& jpp, const std::string& grp) : _jpp(jpp) { - return OptionalGroupScope(jpp, grp); + _jpp.pushScope(grp); } - /** - * @brief ParameterProvider scope guard for existent group - * @details Opens and closes the given ParameterProvider scope. - */ - template - class GroupScope + ~GroupScope() { - public: - - /** - * @brief Enters a given scope if it exists - * @param [in,out] jpp ParameterProvider - * @param [in] grp Name of group - */ - GroupScope(ParamProvider& jpp, const std::string& grp) : _jpp(jpp) - { - _jpp.pushScope(grp); - } - - ~GroupScope() - { - _jpp.popScope(); - } + _jpp.popScope(); + } - private: - ParamProvider& _jpp; - }; +private: + ParamProvider& _jpp; +}; - /** - * @brief Enters a given group and leaves it when leaving the current scope - * @details The group has to exist. - * @param [in,out] jpp ParameterProvider - * @param [in] grp Name of the group - */ - template - inline GroupScope makeGroupScope(ParamProvider& jpp, const std::string& grp) - { - return GroupScope(jpp, grp); - } +/** + * @brief Enters a given group and leaves it when leaving the current scope + * @details The group has to exist. + * @param [in,out] jpp ParameterProvider + * @param [in] grp Name of the group + */ +template +inline GroupScope makeGroupScope(ParamProvider& jpp, const std::string& grp) +{ + return GroupScope(jpp, grp); +} } // namespace util } // namespace test } // namespace cadet -#endif // CADETTEST_TESTUTILS_HPP_ +#endif // CADETTEST_TESTUTILS_HPP_ diff --git a/test/data/configuration_CSTR_MichaelisMenten_benchmark1.json b/test/data/configuration_CSTR_MichaelisMenten_benchmark1.json index b1088b561..065195668 100644 --- a/test/data/configuration_CSTR_MichaelisMenten_benchmark1.json +++ b/test/data/configuration_CSTR_MichaelisMenten_benchmark1.json @@ -1079,4 +1079,4 @@ "RELTOL": 1e-06 } } -} \ No newline at end of file +} diff --git a/test/data/configuration_CSTR_MichaelisMenten_benchmark2.json b/test/data/configuration_CSTR_MichaelisMenten_benchmark2.json index 62490af7f..1fa5b17aa 100644 --- a/test/data/configuration_CSTR_MichaelisMenten_benchmark2.json +++ b/test/data/configuration_CSTR_MichaelisMenten_benchmark2.json @@ -1074,4 +1074,4 @@ "RELTOL": 1e-06 } } -} \ No newline at end of file +} diff --git a/test/data/configuration_CSTR_MicroKineticsSMA_benchmark1.json b/test/data/configuration_CSTR_MicroKineticsSMA_benchmark1.json index 9025d075c..0c22c092a 100644 --- a/test/data/configuration_CSTR_MicroKineticsSMA_benchmark1.json +++ b/test/data/configuration_CSTR_MicroKineticsSMA_benchmark1.json @@ -1082,4 +1082,4 @@ "RELTOL": 1e-06 } } -} \ No newline at end of file +} diff --git a/test/data/convergence_GRM_dynLin_1comp_sensbenchmark1.json b/test/data/convergence_GRM_dynLin_1comp_sensbenchmark1.json index ae0eddd20..0e81b1be4 100644 --- a/test/data/convergence_GRM_dynLin_1comp_sensbenchmark1.json +++ b/test/data/convergence_GRM_dynLin_1comp_sensbenchmark1.json @@ -410,4 +410,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/convergence_GRM_reqSMA_4comp_sensbenchmark1.json b/test/data/convergence_GRM_reqSMA_4comp_sensbenchmark1.json index c0f3fed14..0ed03ea49 100644 --- a/test/data/convergence_GRM_reqSMA_4comp_sensbenchmark1.json +++ b/test/data/convergence_GRM_reqSMA_4comp_sensbenchmark1.json @@ -366,4 +366,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/convergence_LRMP_dynLin_1comp_sensbenchmark1.json b/test/data/convergence_LRMP_dynLin_1comp_sensbenchmark1.json index 82fcda80b..ef920822a 100644 --- a/test/data/convergence_LRMP_dynLin_1comp_sensbenchmark1.json +++ b/test/data/convergence_LRMP_dynLin_1comp_sensbenchmark1.json @@ -574,4 +574,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/convergence_LRMP_reqSMA_4comp_sensbenchmark1.json b/test/data/convergence_LRMP_reqSMA_4comp_sensbenchmark1.json index e5054ce5e..cffc2e369 100644 --- a/test/data/convergence_LRMP_reqSMA_4comp_sensbenchmark1.json +++ b/test/data/convergence_LRMP_reqSMA_4comp_sensbenchmark1.json @@ -414,4 +414,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/convergence_LRM_dynLin_1comp_sensbenchmark1.json b/test/data/convergence_LRM_dynLin_1comp_sensbenchmark1.json index 4de593cb3..1fb9ee899 100644 --- a/test/data/convergence_LRM_dynLin_1comp_sensbenchmark1.json +++ b/test/data/convergence_LRM_dynLin_1comp_sensbenchmark1.json @@ -574,4 +574,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/convergence_LRM_reqSMA_4comp_sensbenchmark1.json b/test/data/convergence_LRM_reqSMA_4comp_sensbenchmark1.json index c37722220..759927a15 100644 --- a/test/data/convergence_LRM_reqSMA_4comp_sensbenchmark1.json +++ b/test/data/convergence_LRM_reqSMA_4comp_sensbenchmark1.json @@ -454,4 +454,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/convergence_radGRM_dynLin_1comp_sensbenchmark1.json b/test/data/convergence_radGRM_dynLin_1comp_sensbenchmark1.json index 57d0899ca..157237e6a 100644 --- a/test/data/convergence_radGRM_dynLin_1comp_sensbenchmark1.json +++ b/test/data/convergence_radGRM_dynLin_1comp_sensbenchmark1.json @@ -322,4 +322,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/convergence_radLRMP_dynLin_1comp_sensbenchmark1.json b/test/data/convergence_radLRMP_dynLin_1comp_sensbenchmark1.json index 75c798332..be8d8763a 100644 --- a/test/data/convergence_radLRMP_dynLin_1comp_sensbenchmark1.json +++ b/test/data/convergence_radLRMP_dynLin_1comp_sensbenchmark1.json @@ -334,4 +334,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/convergence_radLRM_dynLin_1comp_sensbenchmark1.json b/test/data/convergence_radLRM_dynLin_1comp_sensbenchmark1.json index 7583b16be..e29bc9b09 100644 --- a/test/data/convergence_radLRM_dynLin_1comp_sensbenchmark1.json +++ b/test/data/convergence_radLRM_dynLin_1comp_sensbenchmark1.json @@ -494,4 +494,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/model_GRM_dynLin_1comp_benchmark1.json b/test/data/model_GRM_dynLin_1comp_benchmark1.json index 9d58457e7..69c413594 100644 --- a/test/data/model_GRM_dynLin_1comp_benchmark1.json +++ b/test/data/model_GRM_dynLin_1comp_benchmark1.json @@ -106,4 +106,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/model_GRM_reqSMA_4comp_benchmark1.json b/test/data/model_GRM_reqSMA_4comp_benchmark1.json index 6b014cf4c..478f34867 100644 --- a/test/data/model_GRM_reqSMA_4comp_benchmark1.json +++ b/test/data/model_GRM_reqSMA_4comp_benchmark1.json @@ -189,4 +189,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/model_LRMP_dynLin_1comp_benchmark1.json b/test/data/model_LRMP_dynLin_1comp_benchmark1.json index 74b93ff0b..d14ea32a6 100644 --- a/test/data/model_LRMP_dynLin_1comp_benchmark1.json +++ b/test/data/model_LRMP_dynLin_1comp_benchmark1.json @@ -108,4 +108,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/model_LRMP_reqSMA_4comp_benchmark1.json b/test/data/model_LRMP_reqSMA_4comp_benchmark1.json index fdb2a6223..9fcece949 100644 --- a/test/data/model_LRMP_reqSMA_4comp_benchmark1.json +++ b/test/data/model_LRMP_reqSMA_4comp_benchmark1.json @@ -189,4 +189,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/model_LRM_dynLin_1comp_benchmark1.json b/test/data/model_LRM_dynLin_1comp_benchmark1.json index 3a1811b36..7590c4dfe 100644 --- a/test/data/model_LRM_dynLin_1comp_benchmark1.json +++ b/test/data/model_LRM_dynLin_1comp_benchmark1.json @@ -94,4 +94,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/model_LRM_noBnd_1comp_MCTbenchmark.json b/test/data/model_LRM_noBnd_1comp_MCTbenchmark.json index 2594bba86..6c8e57a78 100644 --- a/test/data/model_LRM_noBnd_1comp_MCTbenchmark.json +++ b/test/data/model_LRM_noBnd_1comp_MCTbenchmark.json @@ -866,4 +866,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/model_LRM_reqSMA_4comp_benchmark1.json b/test/data/model_LRM_reqSMA_4comp_benchmark1.json index 0afc5904a..470aabce2 100644 --- a/test/data/model_LRM_reqSMA_4comp_benchmark1.json +++ b/test/data/model_LRM_reqSMA_4comp_benchmark1.json @@ -189,4 +189,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/model_MCT1ch_noEx_noReac_benchmark1.json b/test/data/model_MCT1ch_noEx_noReac_benchmark1.json index fff06a9ba..d998fd7f6 100644 --- a/test/data/model_MCT1ch_noEx_noReac_benchmark1.json +++ b/test/data/model_MCT1ch_noEx_noReac_benchmark1.json @@ -887,4 +887,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/model_MCT1ch_noEx_reac_benchmark1.json b/test/data/model_MCT1ch_noEx_reac_benchmark1.json index 6ee0750aa..24253f595 100644 --- a/test/data/model_MCT1ch_noEx_reac_benchmark1.json +++ b/test/data/model_MCT1ch_noEx_reac_benchmark1.json @@ -887,4 +887,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/model_MCT2ch_1comp_benchmark1.json b/test/data/model_MCT2ch_1comp_benchmark1.json index 6e1ccf06f..6d663082c 100644 --- a/test/data/model_MCT2ch_1comp_benchmark1.json +++ b/test/data/model_MCT2ch_1comp_benchmark1.json @@ -138,4 +138,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/model_MCT2ch_oneWayEx_reac_benchmark1.json b/test/data/model_MCT2ch_oneWayEx_reac_benchmark1.json index 9bd2ae1d3..7c19a2dbc 100644 --- a/test/data/model_MCT2ch_oneWayEx_reac_benchmark1.json +++ b/test/data/model_MCT2ch_oneWayEx_reac_benchmark1.json @@ -898,4 +898,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/model_MCT3ch_twoWayExc_reac_benchmark1.json b/test/data/model_MCT3ch_twoWayExc_reac_benchmark1.json index ff1d96760..3e147cfd7 100644 --- a/test/data/model_MCT3ch_twoWayExc_reac_benchmark1.json +++ b/test/data/model_MCT3ch_twoWayExc_reac_benchmark1.json @@ -899,4 +899,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/model_radGRM_dynLin_1comp_sensbenchmark1.json b/test/data/model_radGRM_dynLin_1comp_sensbenchmark1.json index e9249d0cd..b5a6bde4e 100644 --- a/test/data/model_radGRM_dynLin_1comp_sensbenchmark1.json +++ b/test/data/model_radGRM_dynLin_1comp_sensbenchmark1.json @@ -112,4 +112,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/model_radLRMP_dynLin_1comp_sensbenchmark1.json b/test/data/model_radLRMP_dynLin_1comp_sensbenchmark1.json index b26e08d74..b466a7f11 100644 --- a/test/data/model_radLRMP_dynLin_1comp_sensbenchmark1.json +++ b/test/data/model_radLRMP_dynLin_1comp_sensbenchmark1.json @@ -112,4 +112,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/data/model_radLRM_dynLin_1comp_sensbenchmark1.json b/test/data/model_radLRM_dynLin_1comp_sensbenchmark1.json index b6e6748f1..d63616aae 100644 --- a/test/data/model_radLRM_dynLin_1comp_sensbenchmark1.json +++ b/test/data/model_radLRM_dynLin_1comp_sensbenchmark1.json @@ -100,4 +100,4 @@ ] } } -} \ No newline at end of file +} diff --git a/test/smaNonlinearProblem.m b/test/smaNonlinearProblem.m index f8988c9d6..aa7778b6d 100644 --- a/test/smaNonlinearProblem.m +++ b/test/smaNonlinearProblem.m @@ -13,20 +13,20 @@ function smaNonlinearProblem() 'TolFun', 1e-12, 'TolX', 0, 'Display', 'iter', ... 'Algorithm', {'levenberg-marquardt',.001}); [sol, res] = fsolve(@residual, qStart, opts); - + function [res, J] = residual(x) res = zeros(size(x)); q0bar = x(1) - sum(sigma .* x); res(1) = x(1) - lambda + sum(nu .* x); - + c0powNu = (yCp(1)).^nu; q0barPowNu = q0bar.^nu; - + res(2:end) = kD(2:end) .* x(2:end) .* c0powNu(2:end) - kA(2:end) .* yCp(2:end) .* q0barPowNu(2:end); q0barPowNuM1 = q0bar.^(nu - 1.0); - + J = zeros(length(x)); J(1,:) = [1.0, nu(2:end)]; for i = 2:length(x) diff --git a/test/testAdaptiveTRNewton.cpp b/test/testAdaptiveTRNewton.cpp index 654a3169d..259e07b6c 100644 --- a/test/testAdaptiveTRNewton.cpp +++ b/test/testAdaptiveTRNewton.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -30,27 +30,39 @@ void printVector(const char* prefix, double const* const vec, unsigned int size) struct StdOutIterateOutputPolicy { - inline static void outerIteration(unsigned int idxIter, double residualNorm, double const* const res, double const* const x, double const* const dx, unsigned int size) + inline static void outerIteration(unsigned int idxIter, double residualNorm, double const* const res, + double const* const x, double const* const dx, unsigned int size) { std::cout << idxIter << " Res " << residualNorm << " dx " << cadet::linalg::l2Norm(dx, size) << std::endl; } - inline static void innerIteration(unsigned int idxIter, double residualNorm, double const* const res, double const* const x, double const* const dx, double damping, double mu, unsigned int size) + inline static void innerIteration(unsigned int idxIter, double residualNorm, double const* const res, + double const* const x, double const* const dx, double damping, double mu, + unsigned int size) { - std::cout << " -> lambda " << damping << " mu " << mu << " Res " << residualNorm << " dx " << cadet::linalg::l2Norm(dx, size) << std::endl; + std::cout << " -> lambda " << damping << " mu " << mu << " Res " << residualNorm << " dx " + << cadet::linalg::l2Norm(dx, size) << std::endl; } }; struct Problem1 { - const double initPoint[1] = { 10.0 }; - const double solution[1] = { 2.0 }; + const double initPoint[1] = {10.0}; + const double solution[1] = {2.0}; double jac; - inline const char* name() const { return "Problem1"; } - inline unsigned int size() const { return 1; } - void init() { } + inline const char* name() const + { + return "Problem1"; + } + inline unsigned int size() const + { + return 1; + } + void init() + { + } bool residual(double const* const x, double* const res) { @@ -74,14 +86,23 @@ struct Problem1 struct Problem2 { - const double initPoint[2] = { -5.0, 4.0 }; - const double solution[2] = { 1.0, 0.0 }; + const double initPoint[2] = {-5.0, 4.0}; + const double solution[2] = {1.0, 0.0}; cadet::linalg::DenseMatrix jac; - inline const char* name() const { return "Problem2"; } - inline unsigned int size() const { return 2; } - void init() { jac.resize(size(), size()); } + inline const char* name() const + { + return "Problem2"; + } + inline unsigned int size() const + { + return 2; + } + void init() + { + jac.resize(size(), size()); + } bool residual(double const* const x, double* const res) { @@ -93,10 +114,10 @@ struct Problem2 bool jacobianSolve(double const* const x, double* const sol) { // Build Jacobian at x - jac.native(0,0) = 2.0 * (x[0] - 1.0); - jac.native(0,1) = 2 * x[1]; - jac.native(1,0) = 0.0; - jac.native(1,1) = std::exp(x[1]); + jac.native(0, 0) = 2.0 * (x[0] - 1.0); + jac.native(0, 1) = 2 * x[1]; + jac.native(1, 0) = 0.0; + jac.native(1, 1) = std::exp(x[1]); if (!jac.factorize()) return false; @@ -115,14 +136,23 @@ struct Problem2 struct Problem3 { - const double initPoint[2] = { 1.0, 1.0 }; - const double solution[2] = { 5e1, 10.0 }; + const double initPoint[2] = {1.0, 1.0}; + const double solution[2] = {5e1, 10.0}; cadet::linalg::DenseMatrix jac; - inline const char* name() const { return "Problem3"; } - inline unsigned int size() const { return 2; } - void init() { jac.resize(size(), size()); } + inline const char* name() const + { + return "Problem3"; + } + inline unsigned int size() const + { + return 2; + } + void init() + { + jac.resize(size(), size()); + } bool residual(double const* const x, double* const res) { @@ -134,10 +164,10 @@ struct Problem3 bool jacobianSolve(double const* const x, double* const sol) { // Build Jacobian at x - jac.native(0,0) = 0.0; - jac.native(0,1) = 1.0; - jac.native(1,0) = x[1]; - jac.native(1,1) = x[0]; + jac.native(0, 0) = 0.0; + jac.native(0, 1) = 1.0; + jac.native(1, 0) = x[1]; + jac.native(1, 1) = x[0]; if (!jac.factorize()) return false; @@ -154,9 +184,7 @@ struct Problem3 } }; - -template -void run() +template void run() { Prob p; std::cout << "===== " << p.name() << " =====\n"; @@ -174,16 +202,18 @@ void run() std::vector sol(p.initPoint, p.initPoint + p.size()); std::vector tempMem(4 * p.size(), 0.0); -/* - const bool success = cadet::nonlin::adaptiveTrustRegionNewtonMethod([&](double const* const x, double* const res) { return p.residual(x, res); }, - [&](double const* const x, double* const res) { return p.jacobianSolve(x, res); }, maxIter, resTol, - initDamping, minDamping, sol.data(), tempMem.data(), p.size()); -*/ + /* + const bool success = cadet::nonlin::adaptiveTrustRegionNewtonMethod([&](double const* + const x, double* const res) { return p.residual(x, res); }, + [&](double const* const x, double* const res) { return p.jacobianSolve(x, res); }, maxIter, resTol, + initDamping, minDamping, sol.data(), tempMem.data(), p.size()); + */ - const bool success = cadet::nonlin::robustAdaptiveTrustRegionNewtonMethod([&](double const* const x, double* const res) { return p.residual(x, res); }, + const bool success = cadet::nonlin::robustAdaptiveTrustRegionNewtonMethod( + [&](double const* const x, double* const res) { return p.residual(x, res); }, [&](double const* const x, double* const res) { return p.jacobianSolve(x, res); }, - [&](double* const res) { return p.jacobianResolve(res); }, maxIter, resTol, - initDamping, minDamping, sol.data(), tempMem.data(), p.size()); + [&](double* const res) { return p.jacobianResolve(res); }, maxIter, resTol, initDamping, minDamping, sol.data(), + tempMem.data(), p.size()); std::cout << "Method " << (success ? "SUCCESS" : "FAIL") << std::endl; printVector("Residual", tempMem.data(), p.size()); @@ -198,7 +228,6 @@ void run() std::cout << "Linf-Error: " << errorLinf << std::endl; } - int main(int argc, char** argv) { std::cout << std::scientific << std::setprecision(std::numeric_limits::digits10 + 1); diff --git a/test/testCAPIv1.cpp b/test/testCAPIv1.cpp index ac8a9ece9..f969cb8f9 100644 --- a/test/testCAPIv1.cpp +++ b/test/testCAPIv1.cpp @@ -20,7 +20,8 @@ typedef cdtResult (*cdtGetAPIv010000_t)(cdtAPIv010000* ptr); typedef void (*cdtSetLogReceiver_t)(cdtLogHandler recv); typedef void (*cdtSetLogLevel_t)(int lvl); -void logHandler(const char* file, const char* func, const unsigned int line, int lvl, const char* lvlStr, const char* message) +void logHandler(const char* file, const char* func, const unsigned int line, int lvl, const char* lvlStr, + const char* message) { std::cout << lvlStr << " [" << func << ":" << line << "] " << message << std::endl; } @@ -44,7 +45,10 @@ class LibLoader } } - bool isValid() { return _hdLib; } + bool isValid() + { + return _hdLib; + } template T load(char const* func) { @@ -55,7 +59,6 @@ class LibLoader HINSTANCE _hdLib; }; - json createColumnWithSMAJson(const std::string& uoType) { json config; @@ -82,7 +85,7 @@ json createColumnWithSMAJson(const std::string& uoType) // Adsorption config["ADSORPTION_MODEL"] = std::string("STERIC_MASS_ACTION"); - config["NBOUND"] = { 1, 1, 1, 1 }; + config["NBOUND"] = {1, 1, 1, 1}; { json ads; ads["IS_KINETIC"] = 1; @@ -202,8 +205,8 @@ json createLWEJson(const std::string& uoType) // Connection list is 3x7 since we have 1 connection between // the two unit operations with 3 ports (and we need to have 7 columns) sw["CONNECTIONS"] = {1.0, 0.0, 0.0, 0.0, -1.0, -1.0, 7.42637597e-09, - 1.0, 0.0, 0.0, 1.0, -1.0, -1.0, 2.22791279e-08, - 1.0, 0.0, 0.0, 2.0, -1.0, -1.0, 3.71318798e-08}; + 1.0, 0.0, 0.0, 1.0, -1.0, -1.0, 2.22791279e-08, + 1.0, 0.0, 0.0, 2.0, -1.0, -1.0, 3.71318798e-08}; // Connections: From unit operation 1 port 0 // to unit operation 0 port 0, // connect component -1 (i.e., all components) @@ -215,7 +218,7 @@ json createLWEJson(const std::string& uoType) // Connection list is 1x7 since we have 1 connection between // the two unit operations (and we need to have 7 columns) sw["CONNECTIONS"] = {1.0, 0.0, -1.0, -1.0, -1.0, -1.0, 1.0}; - // Connections: From unit operation 1 port -1 (i.e., all ports) + // Connections: From unit operation 1 port -1 (i.e., all ports) // to unit operation 0 port -1 (i.e., all ports), // connect component -1 (i.e., all components) // to component -1 (i.e., all components) with @@ -245,14 +248,14 @@ json createLWEJson(const std::string& uoType) { json ret; ret["WRITE_SOLUTION_TIMES"] = true; - + json grm; grm["WRITE_SOLUTION_BULK"] = false; grm["WRITE_SOLUTION_PARTICLE"] = false; grm["WRITE_SOLUTION_FLUX"] = false; grm["WRITE_SOLUTION_INLET"] = true; grm["WRITE_SOLUTION_OUTLET"] = true; - + ret["unit_000"] = grm; config["return"] = ret; } @@ -325,7 +328,6 @@ json createLWEJson(const std::string& uoType) return config; } - class JsonNavigator { public: @@ -333,7 +335,9 @@ class JsonNavigator { _opened.push(&_root); } - ~JsonNavigator() { } + ~JsonNavigator() + { + } void pushScope(const std::string& scope) { @@ -355,7 +359,10 @@ class JsonNavigator _scopePath.erase(idx); } - const json& current() const { return *_opened.top(); } + const json& current() const + { + return *_opened.top(); + } private: const json& _root; @@ -375,7 +382,7 @@ cdtResult getDouble(void* userData, const char* paramName, double* val) *val = pp.get(); return cdtOK; } - catch(const std::exception& e) + catch (const std::exception& e) { std::cout << "[PP] GET scalar [double] ERROR: " << e.what() << std::endl; return cdtError; @@ -402,7 +409,7 @@ cdtResult getInt(void* userData, const char* paramName, int* val) } return cdtOK; } - catch(const std::exception& e) + catch (const std::exception& e) { std::cout << "[PP] GET scalar [int] ERROR: " << e.what() << std::endl; return cdtError; @@ -429,7 +436,7 @@ cdtResult getBool(void* userData, const char* paramName, uint8_t* val) } return cdtOK; } - catch(const std::exception& e) + catch (const std::exception& e) { std::cout << "[PP] GET scalar [bool] ERROR: " << e.what() << std::endl; return cdtError; @@ -448,7 +455,7 @@ cdtResult getString(void* userData, const char* paramName, char const** val) *val = pp.get_ptr()->c_str(); return cdtOK; } - catch(const std::exception& e) + catch (const std::exception& e) { std::cout << "[PP] GET scalar [string] ERROR: " << e.what() << std::endl; return cdtError; @@ -475,7 +482,8 @@ cdtResult getDoubleArrayItem(void* userData, const char* paramName, int idx, dou } if ((idx > 0) && (idx >= p.size())) { - std::cout << "[PP] GET array (" << idx << ") [double] ERROR: Index out of bounds (size is " << p.size() << ")" << std::endl; + std::cout << "[PP] GET array (" << idx << ") [double] ERROR: Index out of bounds (size is " << p.size() + << ")" << std::endl; return cdtError; } @@ -485,7 +493,7 @@ cdtResult getDoubleArrayItem(void* userData, const char* paramName, int idx, dou *val = pp.get(); return cdtOK; } - catch(const std::exception& e) + catch (const std::exception& e) { std::cout << "[PP] GET array (" << idx << ") [double] ERROR: " << e.what() << std::endl; return cdtError; @@ -512,7 +520,8 @@ cdtResult getIntArrayItem(void* userData, const char* paramName, int idx, int* v } if ((idx > 0) && (idx >= p.size())) { - std::cout << "[PP] GET array (" << idx << ") [int] ERROR: Index out of bounds (size is " << p.size() << ")" << std::endl; + std::cout << "[PP] GET array (" << idx << ") [int] ERROR: Index out of bounds (size is " << p.size() << ")" + << std::endl; return cdtError; } @@ -520,7 +529,8 @@ cdtResult getIntArrayItem(void* userData, const char* paramName, int idx, int* v if (pp.is_boolean()) { - std::cout << "[PP] GET array (" << idx << ") [int] " << paramName << " = " << static_cast(pp.get()) << "\n"; + std::cout << "[PP] GET array (" << idx << ") [int] " << paramName << " = " + << static_cast(pp.get()) << "\n"; *val = pp.get(); } else @@ -530,7 +540,7 @@ cdtResult getIntArrayItem(void* userData, const char* paramName, int idx, int* v } return cdtOK; } - catch(const std::exception& e) + catch (const std::exception& e) { std::cout << "[PP] GET array (" << idx << ") [int] ERROR: " << e.what() << std::endl; return cdtError; @@ -557,7 +567,8 @@ cdtResult getBoolArrayItem(void* userData, const char* paramName, int idx, uint8 } if ((idx > 0) && (idx >= p.size())) { - std::cout << "[PP] GET array (" << idx << ") [bool] ERROR: Index out of bounds (size is " << p.size() << ")" << std::endl; + std::cout << "[PP] GET array (" << idx << ") [bool] ERROR: Index out of bounds (size is " << p.size() << ")" + << std::endl; return cdtError; } @@ -565,7 +576,8 @@ cdtResult getBoolArrayItem(void* userData, const char* paramName, int idx, uint8 if (pp.is_number_integer()) { - std::cout << "[PP] GET array (" << idx << ") [bool] " << paramName << " = " << static_cast(pp.get()) << "\n"; + std::cout << "[PP] GET array (" << idx << ") [bool] " << paramName << " = " + << static_cast(pp.get()) << "\n"; *val = static_cast(pp.get()); } else @@ -575,7 +587,7 @@ cdtResult getBoolArrayItem(void* userData, const char* paramName, int idx, uint8 } return cdtOK; } - catch(const std::exception& e) + catch (const std::exception& e) { std::cout << "[PP] GET array (" << idx << ") [bool] ERROR: " << e.what() << std::endl; return cdtError; @@ -591,7 +603,8 @@ cdtResult getStringArrayItem(void* userData, const char* paramName, int idx, cha if ((idx == 0) && !p.is_array()) { - std::cout << "[PP] GET array (" << idx << ") [string] " << paramName << " = " << p.get_ref() << "\n"; + std::cout << "[PP] GET array (" << idx << ") [string] " << paramName << " = " + << p.get_ref() << "\n"; *val = p.get_ptr()->c_str(); return cdtOK; } @@ -602,17 +615,19 @@ cdtResult getStringArrayItem(void* userData, const char* paramName, int idx, cha } if ((idx > 0) && (idx >= p.size())) { - std::cout << "[PP] GET array (" << idx << ") [string] ERROR: Index out of bounds (size is " << p.size() << ")" << std::endl; + std::cout << "[PP] GET array (" << idx << ") [string] ERROR: Index out of bounds (size is " << p.size() + << ")" << std::endl; return cdtError; } const json& pp = p[idx]; - std::cout << "[PP] GET array (" << idx << ") [string] " << paramName << " = " << pp.get_ref() << "\n"; + std::cout << "[PP] GET array (" << idx << ") [string] " << paramName << " = " + << pp.get_ref() << "\n"; *val = pp.get_ptr()->c_str(); return cdtOK; } - catch(const std::exception& e) + catch (const std::exception& e) { std::cout << "[PP] GET array (" << idx << ") [string] ERROR: " << e.what() << std::endl; return cdtError; @@ -644,7 +659,7 @@ cdtResult isArray(void* userData, const char* paramName, uint8_t* res) std::cout << "[PP] ISARRAY " << paramName << " = " << p.is_array() << "\n"; return cdtOK; } - catch(const std::exception& e) + catch (const std::exception& e) { std::cout << "[PP] ISARRAY ERROR: " << e.what() << std::endl; return cdtError; @@ -660,7 +675,7 @@ int numElements(void* userData, const char* paramName) std::cout << "[PP] NUMELEMENTS " << paramName << " = " << p.size() << "\n"; return p.size(); } - catch(const std::exception& e) + catch (const std::exception& e) { std::cout << "[PP] NUMELEMENTS ERROR: " << e.what() << std::endl; return -1; @@ -675,7 +690,7 @@ cdtResult pushScope(void* userData, const char* scope) jn.pushScope(scope); return cdtOK; } - catch(const std::exception& e) + catch (const std::exception& e) { std::cout << "[PP] PUSHSCOPE ERROR: " << e.what() << std::endl; return cdtError; @@ -689,7 +704,6 @@ cdtResult popScope(void* userData) return cdtOK; } - int main(int argc, char** argv) { LibLoader loader(getLibBinaryPath()); @@ -733,37 +747,32 @@ int main(int argc, char** argv) return 1; } - std::unique_ptr> drv(api.createDriver(), [&api](cdtDriver* ptr) - { - api.deleteDriver(ptr); - std::cout << "Delete driver" << std::endl; - } - ); + std::unique_ptr> drv(api.createDriver(), [&api](cdtDriver* ptr) { + api.deleteDriver(ptr); + std::cout << "Delete driver" << std::endl; + }); const json simSpec = createLWEJson("GENERAL_RATE_MODEL"); JsonNavigator jn(simSpec); - cdtParameterProvider pp - { - &jn, - &getDouble, - &getInt, - &getBool, - &getString, - nullptr, - nullptr, - nullptr, - nullptr, - &getDoubleArrayItem, - &getIntArrayItem, - &getBoolArrayItem, - &getStringArrayItem, - &exists, - &isArray, - &numElements, - &pushScope, - &popScope - }; + cdtParameterProvider pp{&jn, + &getDouble, + &getInt, + &getBool, + &getString, + nullptr, + nullptr, + nullptr, + nullptr, + &getDoubleArrayItem, + &getIntArrayItem, + &getBoolArrayItem, + &getStringArrayItem, + &exists, + &isArray, + &numElements, + &pushScope, + &popScope}; const cdtResult resSim = api.runSimulation(drv.get(), &pp); std::cout << "runSimulation() = " << resSim << std::endl; @@ -781,7 +790,8 @@ int main(int argc, char** argv) int nComp = 0; const cdtResult resSol = api.getSolutionOutlet(drv.get(), 0, &time, &outlet, &nTime, &nPort, &nComp); - std::cout << "getSolutionOutlet() = " << resSol << " nTime = " << nTime << " nPort = " << nPort << " nComp = " << nComp << std::endl; + std::cout << "getSolutionOutlet() = " << resSol << " nTime = " << nTime << " nPort = " << nPort + << " nComp = " << nComp << std::endl; return 0; } diff --git a/test/testCompileTimeMurmur.cpp b/test/testCompileTimeMurmur.cpp index eed2b310d..6baa1d5a5 100644 --- a/test/testCompileTimeMurmur.cpp +++ b/test/testCompileTimeMurmur.cpp @@ -5,162 +5,197 @@ struct uint128_t { - constexpr uint128_t() : h1(0), h2(0) {} - constexpr uint128_t( uint64_t _h1, uint64_t _h2 ) : h1(_h1), h2(_h2) {} - constexpr bool operator == (const uint128_t& rhs) const { return h1 == rhs.h1 && h2 == rhs.h2; } - constexpr bool operator != (const uint128_t& rhs) const { return !(*this == rhs); } - uint64_t h1; - uint64_t h2; + constexpr uint128_t() : h1(0), h2(0) + { + } + constexpr uint128_t(uint64_t _h1, uint64_t _h2) : h1(_h1), h2(_h2) + { + } + constexpr bool operator==(const uint128_t& rhs) const + { + return h1 == rhs.h1 && h2 == rhs.h2; + } + constexpr bool operator!=(const uint128_t& rhs) const + { + return !(*this == rhs); + } + uint64_t h1; + uint64_t h2; }; - - + class ConstString { public: - template - constexpr ConstString( const char(&s)[N] ) : m_Str( s ), m_Size( N-1 ) {} - constexpr ConstString( const char* s, size_t len ) : m_Str( s ), m_Size( len ) {} - - constexpr size_t size() const { return m_Size; } - - constexpr uint8_t operator[] (size_t n) const - { - return (uint8_t)m_Str[n]; - } - - constexpr uint8_t getU8(size_t n) const - { - return (uint8_t)m_Str[n]; - } - - constexpr uint64_t getU64(size_t n) const - { - return uint64_t( (uint8_t)m_Str[n*8 + 0]) << 0 | uint64_t( (uint8_t)m_Str[n*8 + 1]) << 8 | - uint64_t( (uint8_t)m_Str[n*8 + 2]) << 16 | uint64_t( (uint8_t)m_Str[n*8 + 3]) << 24 | - uint64_t( (uint8_t)m_Str[n*8 + 4]) << 32 | uint64_t( (uint8_t)m_Str[n*8 + 5]) << 40 | - uint64_t( (uint8_t)m_Str[n*8 + 6]) << 48 | uint64_t( (uint8_t)m_Str[n*8 + 7]) << 56; - } - - constexpr inline uint128_t hash128( const uint64_t seed=0x1234567) const - { - return _calcfinal( size(), _calcrest( *this, (size()/16)*16, size() & 15, _calcblocks( *this, size() / 16, 0, uint128_t(seed, seed)) ) ); - } - - constexpr inline uint64_t hash(uint64_t seed=0x1234567) const - { - return hash128(seed).h1; - } - + template constexpr ConstString(const char (&s)[N]) : m_Str(s), m_Size(N - 1) + { + } + constexpr ConstString(const char* s, size_t len) : m_Str(s), m_Size(len) + { + } + + constexpr size_t size() const + { + return m_Size; + } + + constexpr uint8_t operator[](size_t n) const + { + return (uint8_t)m_Str[n]; + } + + constexpr uint8_t getU8(size_t n) const + { + return (uint8_t)m_Str[n]; + } + + constexpr uint64_t getU64(size_t n) const + { + return uint64_t((uint8_t)m_Str[n * 8 + 0]) << 0 | uint64_t((uint8_t)m_Str[n * 8 + 1]) << 8 | + uint64_t((uint8_t)m_Str[n * 8 + 2]) << 16 | uint64_t((uint8_t)m_Str[n * 8 + 3]) << 24 | + uint64_t((uint8_t)m_Str[n * 8 + 4]) << 32 | uint64_t((uint8_t)m_Str[n * 8 + 5]) << 40 | + uint64_t((uint8_t)m_Str[n * 8 + 6]) << 48 | uint64_t((uint8_t)m_Str[n * 8 + 7]) << 56; + } + + constexpr inline uint128_t hash128(const uint64_t seed = 0x1234567) const + { + return _calcfinal(size(), _calcrest(*this, (size() / 16) * 16, size() & 15, + _calcblocks(*this, size() / 16, 0, uint128_t(seed, seed)))); + } + + constexpr inline uint64_t hash(uint64_t seed = 0x1234567) const + { + return hash128(seed).h1; + } + private: - // The code here is a bit messy, but is essentially a functional representation of the MurmurHash3_x64_128 implementation - // rebuilt from the ground up. - - constexpr inline static uint64_t _c1() { return 0x87c37b91114253d5LLU; } - constexpr inline static uint64_t _c2() { return 0x4cf5ad432745937fLLU; } - - constexpr inline static uint64_t rotl64c( uint64_t x, int8_t r ) - { - return (x << r) | (x >> (64 - r)); - } - - constexpr inline static uint64_t _downshift_and_xor( uint64_t k ) - { - return k ^ (k >> 33); - } - - constexpr inline static uint64_t _calcblock_h( const uint128_t value, const uint64_t h1, const uint64_t h2 ) - { - return (h2 + rotl64c(h1 ^ (_c2() * rotl64c(value.h1 * _c1(),31)), 27)) * 5 + 0x52dce729; - } - - constexpr inline static uint128_t _calcblock( const uint128_t value, uint64_t h1, uint64_t h2 ) - { - return uint128_t( _calcblock_h(value, h1, h2), - (_calcblock_h(value, h1, h2) + rotl64c(h2 ^ (_c1() * rotl64c(value.h2 * _c2(), 33)), 31)) * 5 + 0x38495ab5 ); - } - - constexpr inline static uint128_t _calcblocks( const ConstString cs, const int nblocks, const int index, const uint128_t accum) - { - return nblocks == 0 ? accum : index == nblocks-1 ? _calcblock( uint128_t(cs.getU64(index*2+0), cs.getU64(index*2+1)), accum.h1, accum.h2 ) : - _calcblocks( cs, nblocks, index+1, _calcblock( uint128_t(cs.getU64(index*2+0), cs.getU64(index*2+1)), accum.h1, accum.h2 ) ); - } - - constexpr static uint128_t _add( const uint128_t value ) - { - return uint128_t( value.h1 + value.h2, value.h2 * 2 + value.h1 ); - } - - constexpr static uint64_t _fmix_64( uint64_t k ) - { - return _downshift_and_xor( _downshift_and_xor( _downshift_and_xor( k ) * 0xff51afd7ed558ccdLLU ) * 0xc4ceb9fe1a85ec53LLU ); - } - - constexpr static uint128_t _fmix( const uint128_t value ) - { - return uint128_t( _fmix_64(value.h1), _fmix_64(value.h2) ); - } - - constexpr static uint64_t _calcrest_xor(const ConstString cs, const int offset, const int index, const uint64_t k) - { - return k ^ (uint64_t( cs[offset + index] ) << (index * 8)); - } - - constexpr static uint64_t _calcrest_k(const ConstString cs, const size_t offset, const size_t index, const size_t len, const uint64_t k) - { - return index == (len-1) ? _calcrest_xor(cs, offset, index, k) : _calcrest_xor(cs, offset, index, _calcrest_k(cs, offset, index+1, len, k) ); - } - - constexpr static uint128_t _calcrest(const ConstString cs, const uint64_t offset, const size_t restlen, const uint128_t value) - { - return restlen == 0 ? value : - restlen > 8 ? uint128_t( value.h1 ^ (rotl64c( _calcrest_k( cs, offset, 0, restlen > 8 ? 8 : restlen, 0 ) * _c1(), 31) * _c2()), - value.h2 ^ (rotl64c( _calcrest_k( cs, offset+8, 0, restlen-8, 0 ) * _c2(), 33) * _c1()) ) - : uint128_t(value.h1 ^ (rotl64c( _calcrest_k( cs, offset, 0, restlen > 8 ? 8 : restlen, 0 ) * _c1(), 31) * _c2()), - value.h2); - } - - constexpr static uint128_t _calcfinal(const size_t len, const uint128_t value) - { - return _add( _fmix( _add( uint128_t(value.h1 ^ len, value.h2 ^ len) ) ) ); - } - - const char* m_Str; - size_t m_Size; + // The code here is a bit messy, but is essentially a functional representation of the MurmurHash3_x64_128 + // implementation rebuilt from the ground up. + + constexpr inline static uint64_t _c1() + { + return 0x87c37b91114253d5LLU; + } + constexpr inline static uint64_t _c2() + { + return 0x4cf5ad432745937fLLU; + } + + constexpr inline static uint64_t rotl64c(uint64_t x, int8_t r) + { + return (x << r) | (x >> (64 - r)); + } + + constexpr inline static uint64_t _downshift_and_xor(uint64_t k) + { + return k ^ (k >> 33); + } + + constexpr inline static uint64_t _calcblock_h(const uint128_t value, const uint64_t h1, const uint64_t h2) + { + return (h2 + rotl64c(h1 ^ (_c2() * rotl64c(value.h1 * _c1(), 31)), 27)) * 5 + 0x52dce729; + } + + constexpr inline static uint128_t _calcblock(const uint128_t value, uint64_t h1, uint64_t h2) + { + return uint128_t(_calcblock_h(value, h1, h2), + (_calcblock_h(value, h1, h2) + rotl64c(h2 ^ (_c1() * rotl64c(value.h2 * _c2(), 33)), 31)) * 5 + + 0x38495ab5); + } + + constexpr inline static uint128_t _calcblocks(const ConstString cs, const int nblocks, const int index, + const uint128_t accum) + { + return nblocks == 0 ? accum + : index == nblocks - 1 + ? _calcblock(uint128_t(cs.getU64(index * 2 + 0), cs.getU64(index * 2 + 1)), accum.h1, accum.h2) + : _calcblocks( + cs, nblocks, index + 1, + _calcblock(uint128_t(cs.getU64(index * 2 + 0), cs.getU64(index * 2 + 1)), accum.h1, accum.h2)); + } + + constexpr static uint128_t _add(const uint128_t value) + { + return uint128_t(value.h1 + value.h2, value.h2 * 2 + value.h1); + } + + constexpr static uint64_t _fmix_64(uint64_t k) + { + return _downshift_and_xor(_downshift_and_xor(_downshift_and_xor(k) * 0xff51afd7ed558ccdLLU) * + 0xc4ceb9fe1a85ec53LLU); + } + + constexpr static uint128_t _fmix(const uint128_t value) + { + return uint128_t(_fmix_64(value.h1), _fmix_64(value.h2)); + } + + constexpr static uint64_t _calcrest_xor(const ConstString cs, const int offset, const int index, const uint64_t k) + { + return k ^ (uint64_t(cs[offset + index]) << (index * 8)); + } + + constexpr static uint64_t _calcrest_k(const ConstString cs, const size_t offset, const size_t index, + const size_t len, const uint64_t k) + { + return index == (len - 1) ? _calcrest_xor(cs, offset, index, k) + : _calcrest_xor(cs, offset, index, _calcrest_k(cs, offset, index + 1, len, k)); + } + + constexpr static uint128_t _calcrest(const ConstString cs, const uint64_t offset, const size_t restlen, + const uint128_t value) + { + return restlen == 0 ? value + : restlen > 8 + ? uint128_t( + value.h1 ^ + (rotl64c(_calcrest_k(cs, offset, 0, restlen > 8 ? 8 : restlen, 0) * _c1(), 31) * _c2()), + value.h2 ^ (rotl64c(_calcrest_k(cs, offset + 8, 0, restlen - 8, 0) * _c2(), 33) * _c1())) + : uint128_t( + value.h1 ^ + (rotl64c(_calcrest_k(cs, offset, 0, restlen > 8 ? 8 : restlen, 0) * _c1(), 31) * _c2()), + value.h2); + } + + constexpr static uint128_t _calcfinal(const size_t len, const uint128_t value) + { + return _add(_fmix(_add(uint128_t(value.h1 ^ len, value.h2 ^ len)))); + } + + const char* m_Str; + size_t m_Size; }; - - + //----------------------------------------------------------------------------- - - + constexpr uint64_t MurmurHash3c_64(const ConstString& cs, uint64_t seed = 0x1234567) { - return cs.hash(seed); + return cs.hash(seed); } - -constexpr uint64_t operator "" _hash(const char* str, size_t len) + +constexpr uint64_t operator"" _hash(const char* str, size_t len) { - return ConstString(str, len).hash(); + return ConstString(str, len).hash(); } - -inline void MurmurHash3c_x64_128( const char* key, int len, const uint32_t seed, void* out ) + +inline void MurmurHash3c_x64_128(const char* key, int len, const uint32_t seed, void* out) { - ((uint128_t*)out)[0] = ConstString( key, len ).hash128(seed); + ((uint128_t*)out)[0] = ConstString(key, len).hash128(seed); } enum ETest { - testval1 = MurmurHash3c_64( "hello enum" ), - testval2 = "hello enum"_hash, + testval1 = MurmurHash3c_64("hello enum"), + testval2 = "hello enum"_hash, }; #include int main(int argc, char** argv) { - constexpr uint64_t sh = "hello string"_hash; + constexpr uint64_t sh = "hello string"_hash; - std::cout << "Hash: " << sh << std::endl; - std::cout << "Hash: " << testval1 << std::endl; - std::cout << "Hash: " << testval2 << std::endl; - return 0; + std::cout << "Hash: " << sh << std::endl; + std::cout << "Hash: " << testval1 << std::endl; + std::cout << "Hash: " << testval2 << std::endl; + return 0; } diff --git a/test/testLogging.cpp b/test/testLogging.cpp index 7a2f84a25..cf4f1ccf5 100644 --- a/test/testLogging.cpp +++ b/test/testLogging.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -13,21 +13,19 @@ #include "cadet/Logging.hpp" #include "common/Logger.hpp" - -typedef cadet::log::NonFilteringLogger GlobalLogger; +typedef cadet::log::NonFilteringLogger + GlobalLogger; typedef cadet::log::Logger NonFilteringLogger; typedef cadet::log::Logger FilterLogger; typedef cadet::log::Logger, cadet::LogLevel::Normal> RtFilterLogger; -template <> -cadet::LogLevel cadet::log::RuntimeFilteringLogger::_minLvl = cadet::LogLevel::Trace; +template <> cadet::LogLevel cadet::log::RuntimeFilteringLogger::_minLvl = cadet::LogLevel::Trace; #define LOG_GLOBAL(lvl) LOG_BASE(NonFilteringLogger, lvl) #define LOG_FILTER(lvl) LOG_BASE(FilterLogger, lvl) #define LOG_RT_FILTER(lvl) LOG_BASE(RtFilterLogger, lvl) - int main(int argc, char** argv) { diff --git a/test/testRadialKernel.cpp b/test/testRadialKernel.cpp index 9a4e9dbf1..4ce9afa7a 100644 --- a/test/testRadialKernel.cpp +++ b/test/testRadialKernel.cpp @@ -27,35 +27,39 @@ #include #include -//#define TEST_BREAKTHROUGH 1 +// #define TEST_BREAKTHROUGH 1 #define TEST_MANUFACTURED 1 #define TEST_MANUFACTURED_TEXPT 1 // Uncomment the next line to enable logging output of CADET in unit tests -//#define CADETTEST_ENABLE_LOG +// #define CADETTEST_ENABLE_LOG #ifdef CADETTEST_ENABLE_LOG - #include "cadet/Logging.hpp" - #include +#include "cadet/Logging.hpp" +#include - class LogReceiver : public cadet::ILogReceiver +class LogReceiver : public cadet::ILogReceiver +{ +public: + LogReceiver() { - public: - LogReceiver() { } + } - virtual void message(const char* file, const char* func, const unsigned int line, cadet::LogLevel lvl, const char* lvlStr, const char* message) - { - std::cout << '[' << lvlStr << ": " << func << "::" << line << "] " << message << std::flush; - } - }; + virtual void message(const char* file, const char* func, const unsigned int line, cadet::LogLevel lvl, + const char* lvlStr, const char* message) + { + std::cout << '[' << lvlStr << ": " << func << "::" << line << "] " << message << std::flush; + } +}; #endif class RadialFlowModel : public cadet::test::IDiffEqModel { public: - RadialFlowModel(int nComp, int nCol) : _nComp(nComp), _nCol(nCol), - _params{0.0, nullptr, nullptr, nullptr, nullptr, nullptr, 0, 0, 0, 0, 0, nullptr, _dummyModel}, - _stencilMemory(sizeof(cadet::active) * 5) + RadialFlowModel(int nComp, int nCol) + : _nComp(nComp), _nCol(nCol), + _params{0.0, nullptr, nullptr, nullptr, nullptr, nullptr, 0, 0, 0, 0, 0, nullptr, _dummyModel}, + _stencilMemory(sizeof(cadet::active) * 5) { const int nPureDof = _nCol * _nComp; _jacDisc.resize(nPureDof, 2 * nComp, 2 * nComp); @@ -65,7 +69,7 @@ class RadialFlowModel : public cadet::test::IDiffEqModel const double colLen = 0.1; -// equidistantCells(0.1, 0.4, _nCol); + // equidistantCells(0.1, 0.4, _nCol); equidistantCells(1.0, 4.0, _nCol); _params.u = fromVolumetricFlowRate(8e-1, colLen); @@ -88,8 +92,14 @@ class RadialFlowModel : public cadet::test::IDiffEqModel delete _params.parDep; } - int numPureDofs() const CADET_NOEXCEPT { return _nComp * _nCol; } - virtual int numDofs() const CADET_NOEXCEPT { return _nComp * (_nCol + 1); } + int numPureDofs() const CADET_NOEXCEPT + { + return _nComp * _nCol; + } + virtual int numDofs() const CADET_NOEXCEPT + { + return _nComp * (_nCol + 1); + } virtual void notifyDiscontinuousSectionTransition(double t, int secIdx, double* vecStateY, double* vecStateYdot) { @@ -114,9 +124,13 @@ class RadialFlowModel : public cadet::test::IDiffEqModel { const double denom = static_cast(_cellCenters[i]) * static_cast(_cellSizes[i]); const double left = static_cast(_cellBounds[i]); - const double right = static_cast(_cellBounds[i+1]); + const double right = static_cast(_cellBounds[i + 1]); - const double val = (right * right - left*left) / denom + (std::sin(fourPiOverRout * right) * right - std::sin(fourPiOverRout * left) * left + (std::cos(fourPiOverRout * right) - std::cos(fourPiOverRout * left)) / fourPiOverRout) / (fourPiOverRout * denom) * expFactor; + const double val = + (right * right - left * left) / denom + + (std::sin(fourPiOverRout * right) * right - std::sin(fourPiOverRout * left) * left + + (std::cos(fourPiOverRout * right) - std::cos(fourPiOverRout * left)) / fourPiOverRout) / + (fourPiOverRout * denom) * expFactor; for (int comp = 0; comp < _nComp; ++comp, ++idx) { vecStateY[idx] = val; @@ -128,9 +142,9 @@ class RadialFlowModel : public cadet::test::IDiffEqModel { const double denom = static_cast(_cellCenters[i]) * static_cast(_cellSizes[i]); const double left = static_cast(_cellBounds[i]); - const double right = static_cast(_cellBounds[i+1]); + const double right = static_cast(_cellBounds[i + 1]); - const double val = (right * right - left*left) / denom; + const double val = (right * right - left * left) / denom; for (int comp = 0; comp < _nComp; ++comp, ++idx) { vecStateY[idx] = val; @@ -145,9 +159,7 @@ class RadialFlowModel : public cadet::test::IDiffEqModel double* const yDot = vecStateYdot + _nComp; for (int i = 0; i < numPureDofs(); ++i) yDot[i] = -resBulk[i]; - } - } virtual int residual(double time, int secIdx, double const* vecStateY, double const* vecStateYdot, double* res) @@ -155,7 +167,8 @@ class RadialFlowModel : public cadet::test::IDiffEqModel return residualWithJacobian(time, secIdx, vecStateY, vecStateYdot, res); } - virtual int residualWithJacobian(double time, int secIdx, double const* vecStateY, double const* vecStateYdot, double* res) + virtual int residualWithJacobian(double time, int secIdx, double const* vecStateY, double const* vecStateYdot, + double* res) { _jac.setAll(0.0); @@ -163,11 +176,11 @@ class RadialFlowModel : public cadet::test::IDiffEqModel for (int i = 0; i < _nComp; ++i) res[i] = vecStateY[i] - inlet(time, secIdx, i); - const int ret = cadet::model::parts::convdisp::residualKernelRadial( - cadet::SimulationTime{time, static_cast(secIdx)}, - vecStateY, vecStateYdot, res, _jac.row(0), _params - ); - + const int ret = + cadet::model::parts::convdisp::residualKernelRadial( + cadet::SimulationTime{time, static_cast(secIdx)}, vecStateY, vecStateYdot, res, + _jac.row(0), _params); #if defined(TEST_MANUFACTURED) && !defined(TEST_MANUFACTURED_TEXPT) const double pi = 3.14159265358979323846; @@ -183,7 +196,7 @@ class RadialFlowModel : public cadet::test::IDiffEqModel { const double denom = static_cast(_cellCenters[i]) * static_cast(_cellSizes[i]); const double left = static_cast(_cellBounds[i]); - const double right = static_cast(_cellBounds[i+1]); + const double right = static_cast(_cellBounds[i + 1]); const double sinLeft = std::sin(fourPiOverRout * left); const double cosLeft = std::cos(fourPiOverRout * left); @@ -197,7 +210,11 @@ class RadialFlowModel : public cadet::test::IDiffEqModel const double leftTerm = fourPiOverRout * left * sinLeft * d_rad + u * cosLeft; const double rightTerm = fourPiOverRout * right * sinRight * d_rad + u * cosRight; - const double val = (tMinusFive * (left / (4.0 * fourPiOverRout) * sinLeft - right / (4.0 * fourPiOverRout) * sinRight + fourPiOverRoutSq / 4.0 * (-cosRight + cosLeft)) - leftTerm + rightTerm) * expFactor / denom; + const double val = + (tMinusFive * (left / (4.0 * fourPiOverRout) * sinLeft - right / (4.0 * fourPiOverRout) * sinRight + + fourPiOverRoutSq / 4.0 * (-cosRight + cosLeft)) - + leftTerm + rightTerm) * + expFactor / denom; res[idx] -= val; } } @@ -215,7 +232,7 @@ class RadialFlowModel : public cadet::test::IDiffEqModel { const double denom = static_cast(_cellCenters[i]) * static_cast(_cellSizes[i]); const double left = static_cast(_cellBounds[i]); - const double right = static_cast(_cellBounds[i+1]); + const double right = static_cast(_cellBounds[i + 1]); const double sinLeft = std::sin(fourPiOverRout * left); const double cosLeft = std::cos(fourPiOverRout * left); @@ -247,7 +264,7 @@ class RadialFlowModel : public cadet::test::IDiffEqModel } virtual int linearSolve(double t, double alpha, double tol, double* rhs, double const* weight, - double const* vecStateY, double const* vecStateYdot) + double const* vecStateY, double const* vecStateYdot) { _jacDisc.copyOver(_jac); @@ -269,7 +286,8 @@ class RadialFlowModel : public cadet::test::IDiffEqModel // A * inlet + J * x = rhs // J * x = rhs - A * inlet const int idxInletCell = (_params.u >= 0.0) ? 0 : _nCol - 1; - const double factor = static_cast(_params.u) / (static_cast(_params.cellCenters[idxInletCell]) * static_cast(_params.cellSizes[idxInletCell])); + const double factor = static_cast(_params.u) / (static_cast(_params.cellCenters[idxInletCell]) * + static_cast(_params.cellSizes[idxInletCell])); double* const rhsBulkInlet = rhs + _nComp * (idxInletCell + 1); for (int i = 0; i < _nComp; ++i) { @@ -313,12 +331,30 @@ class RadialFlowModel : public cadet::test::IDiffEqModel } } - const std::vector& solutionTimes() const CADET_NOEXCEPT { return _solTimes; } - const std::vector& solution() const CADET_NOEXCEPT { return _solution; } - const std::vector& solutionInlet() const CADET_NOEXCEPT { return _solutionInlet; } - const std::vector& solutionOutlet() const CADET_NOEXCEPT { return _solutionOutlet; } - int numComp() const CADET_NOEXCEPT { return _nComp; } - int numCol() const CADET_NOEXCEPT { return _nCol; } + const std::vector& solutionTimes() const CADET_NOEXCEPT + { + return _solTimes; + } + const std::vector& solution() const CADET_NOEXCEPT + { + return _solution; + } + const std::vector& solutionInlet() const CADET_NOEXCEPT + { + return _solutionInlet; + } + const std::vector& solutionOutlet() const CADET_NOEXCEPT + { + return _solutionOutlet; + } + int numComp() const CADET_NOEXCEPT + { + return _nComp; + } + int numCol() const CADET_NOEXCEPT + { + return _nCol; + } const std::vector& referenceSolution() CADET_NOEXCEPT { @@ -327,7 +363,6 @@ class RadialFlowModel : public cadet::test::IDiffEqModel _trueSolution = std::vector(_solTimes.size() * numPureDofs(), 0.0); - #if defined(TEST_MANUFACTURED) && !defined(TEST_MANUFACTURED_TEXPT) const double pi = 3.14159265358979323846; const double fourPi = 4.0 * pi; @@ -386,9 +421,13 @@ class RadialFlowModel : public cadet::test::IDiffEqModel { const double denom = static_cast(_cellCenters[j]) * static_cast(_cellSizes[j]); const double left = static_cast(_cellBounds[j]); - const double right = static_cast(_cellBounds[j+1]); + const double right = static_cast(_cellBounds[j + 1]); - const double val = (right * right - left*left) / denom + (std::sin(fourPiOverRout * right) * right - std::sin(fourPiOverRout * left) * left + (std::cos(fourPiOverRout * right) - std::cos(fourPiOverRout * left)) / fourPiOverRout) / (fourPiOverRout * denom) * expFactor; + const double val = + (right * right - left * left) / denom + + (std::sin(fourPiOverRout * right) * right - std::sin(fourPiOverRout * left) * left + + (std::cos(fourPiOverRout * right) - std::cos(fourPiOverRout * left)) / fourPiOverRout) / + (fourPiOverRout * denom) * expFactor; for (int comp = 0; comp < _nComp; ++comp, ++idx) { _trueSolution[idx] = val; @@ -428,7 +467,7 @@ class RadialFlowModel : public cadet::test::IDiffEqModel std::vector coords(_cellCenters.size(), 0.0); for (int i = 0; i < _cellCenters.size(); ++i) coords[i] = static_cast(_cellCenters[i]); - + return coords; } @@ -457,28 +496,31 @@ class RadialFlowModel : public cadet::test::IDiffEqModel double inlet(double t, int secIdx, int comp) const CADET_NOEXCEPT { -/* - if (t <= 10.0) - return 0.1 * t; - if (t <= 50.0) - return 1.0; - if (t <= 60.0) - return 1.0 - (t-50.0) * 0.1; - return 0.0; -*/ + /* + if (t <= 10.0) + return 0.1 * t; + if (t <= 50.0) + return 1.0; + if (t <= 60.0) + return 1.0 - (t-50.0) * 0.1; + return 0.0; + */ #ifdef TEST_BREAKTHROUGH return 1.0; #elif defined(TEST_MANUFACTURED) const double pi = 3.14159265358979323846; - const double fourPiRinOverRout = 4.0 * pi * static_cast(_cellBounds[0]) / static_cast(_cellBounds.back()); + const double fourPiRinOverRout = + 4.0 * pi * static_cast(_cellBounds[0]) / static_cast(_cellBounds.back()); const double tMinusFiveSq = (t - 5.0) * (t - 5.0); const double expFactor = t * std::exp(-0.125 * tMinusFiveSq); - return 2.0 + (fourPiRinOverRout * static_cast(_params.d_rad[comp]) / static_cast(_params.u) * std::sin(fourPiRinOverRout) + std::cos(fourPiRinOverRout)) * expFactor; + return 2.0 + (fourPiRinOverRout * static_cast(_params.d_rad[comp]) / static_cast(_params.u) * + std::sin(fourPiRinOverRout) + + std::cos(fourPiRinOverRout)) * + expFactor; #endif - } void equidistantCells(double inner, double outer, int nCol) @@ -506,7 +548,6 @@ class RadialFlowModel : public cadet::test::IDiffEqModel } }; - int main(int argc, char* argv[]) { #ifdef CADETTEST_ENABLE_LOG @@ -537,7 +578,7 @@ int main(int argc, char* argv[]) RadialFlowModel model(1, 1250); - cadet::test::TimeIntegrator sim; + cadet::test::TimeIntegrator sim; sim.configureTimeIntegrator(1e-6, 1e-8, 1e-4, 100000, 0.0); sim.setSectionTimes(secTimes); sim.setSolutionTimes(solTimes); @@ -548,12 +589,16 @@ int main(int argc, char* argv[]) cadet::io::HDF5Writer writer; writer.openFile("radial.h5", "co"); writer.vector("SOLUTION_TIMES", model.solutionTimes()); - const std::vector dims = {model.solutionTimes().size(), static_cast(model.numCol()), static_cast(model.numComp())}; + const std::vector dims = {model.solutionTimes().size(), static_cast(model.numCol()), + static_cast(model.numComp())}; writer.template tensor("SOLUTION", 3, dims.data(), model.solution()); - writer.template matrix("SOLUTION_INLET", model.solutionTimes().size(), model.numComp(), model.solutionInlet()); - writer.template matrix("SOLUTION_OUTLET", model.solutionTimes().size(), model.numComp(), model.solutionOutlet()); + writer.template matrix("SOLUTION_INLET", model.solutionTimes().size(), model.numComp(), + model.solutionInlet()); + writer.template matrix("SOLUTION_OUTLET", model.solutionTimes().size(), model.numComp(), + model.solutionOutlet()); writer.template tensor("REF", 3, dims.data(), model.referenceSolution()); - writer.template matrix("REF_OUTLET", model.solutionTimes().size(), model.numComp(), model.referenceOutlet()); + writer.template matrix("REF_OUTLET", model.solutionTimes().size(), model.numComp(), + model.referenceOutlet()); writer.template vector("COORDS", model.coordinates()); writer.closeFile(); return 0; diff --git a/test/testRunner.cpp b/test/testRunner.cpp index f6ee88cad..d3854603c 100644 --- a/test/testRunner.cpp +++ b/test/testRunner.cpp @@ -14,33 +14,34 @@ #include #ifdef CADET_PARALLELIZE - #define TBB_PREVIEW_GLOBAL_CONTROL 1 - #include +#define TBB_PREVIEW_GLOBAL_CONTROL 1 +#include - #ifdef CADET_TBB_GLOBALCTRL - #include - #endif +#ifdef CADET_TBB_GLOBALCTRL +#include +#endif #endif - // Uncomment the next line to enable logging output of CADET in unit tests -//#define CADETTEST_ENABLE_LOG - +// #define CADETTEST_ENABLE_LOG #ifdef CADETTEST_ENABLE_LOG - #include "cadet/Logging.hpp" - #include +#include "cadet/Logging.hpp" +#include - class LogReceiver : public cadet::ILogReceiver +class LogReceiver : public cadet::ILogReceiver +{ +public: + LogReceiver() { - public: - LogReceiver() { } + } - virtual void message(const char* file, const char* func, const unsigned int line, cadet::LogLevel lvl, const char* lvlStr, const char* message) - { - std::cout << '[' << lvlStr << ": " << func << "::" << line << "] " << message << std::flush; - } - }; + virtual void message(const char* file, const char* func, const unsigned int line, cadet::LogLevel lvl, + const char* lvlStr, const char* message) + { + std::cout << '[' << lvlStr << ": " << func << "::" << line << "] " << message << std::flush; + } +}; #endif int main(int argc, char* argv[]) @@ -54,11 +55,11 @@ int main(int argc, char* argv[]) #endif #ifdef CADET_PARALLELIZE - #ifdef CADET_TBB_GLOBALCTRL - int nThreads = tbb::this_task_arena::max_concurrency(); - #else - int nThreads = tbb::task_scheduler_init::automatic; - #endif +#ifdef CADET_TBB_GLOBALCTRL + int nThreads = tbb::this_task_arena::max_concurrency(); +#else + int nThreads = tbb::task_scheduler_init::automatic; +#endif #else int nThreads = 0; #endif @@ -66,7 +67,8 @@ int main(int argc, char* argv[]) Catch::Session session; // Add command line option for threads to CATCH's argument parser - session.cli(session.cli() | Catch::clara::Opt( nThreads, "number" )["--tbbthreads"]("number of TBB threads"));; + session.cli(session.cli() | Catch::clara::Opt(nThreads, "number")["--tbbthreads"]("number of TBB threads")); + ; // Parse the command line and check for error const int returnCode = session.applyCommandLine(argc, argv); @@ -74,14 +76,15 @@ int main(int argc, char* argv[]) return returnCode; #ifdef CADET_PARALLELIZE - #ifdef CADET_TBB_GLOBALCTRL - tbb::global_control tbbGlobalControl(tbb::global_control::max_allowed_parallelism, (nThreads <= 0) ? tbb::this_task_arena::max_concurrency() : nThreads); - #else - if (nThreads <= 0) - nThreads = tbb::task_scheduler_init::automatic; +#ifdef CADET_TBB_GLOBALCTRL + tbb::global_control tbbGlobalControl(tbb::global_control::max_allowed_parallelism, + (nThreads <= 0) ? tbb::this_task_arena::max_concurrency() : nThreads); +#else + if (nThreads <= 0) + nThreads = tbb::task_scheduler_init::automatic; - tbb::task_scheduler_init taskSchedulerInit(nThreads); - #endif + tbb::task_scheduler_init taskSchedulerInit(nThreads); +#endif #endif // Run tests diff --git a/test/testSMANonlinearSolve.cpp b/test/testSMANonlinearSolve.cpp index 484d49c81..e0710acc4 100644 --- a/test/testSMANonlinearSolve.cpp +++ b/test/testSMANonlinearSolve.cpp @@ -1,9 +1,9 @@ // ============================================================================= // CADET -// +// // Copyright © The CADET Authors // Please see the CONTRIBUTORS.md file. -// +// // All rights reserved. This program and the accompanying materials // are made available under the terms of the GNU Public License v3.0 (or, at // your option, any later version) which accompanies this distribution, and @@ -24,7 +24,7 @@ #include #include "common/TclapUtils.hpp" -//#define USE_QR_FACTORIZATION +// #define USE_QR_FACTORIZATION void printVector(const char* prefix, double const* const vec, unsigned int size) { @@ -44,37 +44,45 @@ void printDiffVector(const char* prefix, double const* const vecA, double const* struct StdOutNewtonIterateOutputPolicy { - inline static void outerIteration(unsigned int idxIter, double residualNorm, double const* const res, double const* const x, double const* const dx, unsigned int size) + inline static void outerIteration(unsigned int idxIter, double residualNorm, double const* const res, + double const* const x, double const* const dx, unsigned int size) { std::cout << idxIter << " Res " << residualNorm << " dx " << cadet::linalg::l2Norm(dx, size) << std::endl; } - inline static void innerIteration(unsigned int idxIter, double residualNorm, double const* const res, double const* const x, double const* const dx, double damping, double mu, unsigned int size) + inline static void innerIteration(unsigned int idxIter, double residualNorm, double const* const res, + double const* const x, double const* const dx, double damping, double mu, + unsigned int size) { - std::cout << " -> lambda " << damping << " mu " << mu << " Res " << residualNorm << " dx " << cadet::linalg::l2Norm(dx, size) << std::endl; + std::cout << " -> lambda " << damping << " mu " << mu << " Res " << residualNorm << " dx " + << cadet::linalg::l2Norm(dx, size) << std::endl; } }; struct StdOutLevMarIterateOutputPolicy { - inline static void outerIteration(unsigned int idxIter, double residualNorm, double const* const res, double const* const x, double const* const dx, double damping, unsigned int size) + inline static void outerIteration(unsigned int idxIter, double residualNorm, double const* const res, + double const* const x, double const* const dx, double damping, unsigned int size) { std::cout << idxIter << " Res " << residualNorm << " lambda " << damping; if (dx) std::cout << " dx " << cadet::linalg::l2Norm(dx, size); std::cout << std::endl; } - inline static void innerIteration(unsigned int idxIter, double residualNorm, double const* const res, double const* const x, double const* const dx, double damping, unsigned int size) + inline static void innerIteration(unsigned int idxIter, double residualNorm, double const* const res, + double const* const x, double const* const dx, double damping, unsigned int size) { - std::cout << " -> lambda " << damping << " Res " << residualNorm << " dx " << cadet::linalg::l2Norm(dx, size) << std::endl; + std::cout << " -> lambda " << damping << " Res " << residualNorm << " dx " << cadet::linalg::l2Norm(dx, size) + << std::endl; } }; - struct SMAProblem { - const double initPoint[4] = { 1.0485785488181000e+03, 1.1604726694141368e+01, 1.1469542586742687e+01, 9.7852311988018670e+00 }; - const double yCp[4] = { 5.8377002519964755e+01, 2.9352296732047269e-03, 1.5061023667222263e-02, 1.3523701213590386e-01 }; + const double initPoint[4] = {1.0485785488181000e+03, 1.1604726694141368e+01, 1.1469542586742687e+01, + 9.7852311988018670e+00}; + const double yCp[4] = {5.8377002519964755e+01, 2.9352296732047269e-03, 1.5061023667222263e-02, + 1.3523701213590386e-01}; const double _kA[4] = {0.0, 35.5, 1.59, 7.7}; const double _kD[4] = {0.0, 1000.0, 1000.0, 1000.0}; const double _lambda = 1.2e3; @@ -84,22 +92,28 @@ struct SMAProblem cadet::linalg::DenseMatrix _jacMatrix; #ifdef USE_QR_FACTORIZATION std::vector _workspace; -#endif +#endif - inline const char* name() const { return "SMAProblem"; } - inline int size() const { return 4; } + inline const char* name() const + { + return "SMAProblem"; + } + inline int size() const + { + return 4; + } void init() { _jacMatrix.resize(size(), size()); -#ifdef USE_QR_FACTORIZATION +#ifdef USE_QR_FACTORIZATION _workspace = std::vector(2 * size(), 0.0); #endif } bool residual(double const* const x, double* const res) { - // Salt equation: q_0 - Lambda + Sum[nu_j * q_j, j] == 0 - // <=> q_0 == Lambda - Sum[nu_j * q_j, j] + // Salt equation: q_0 - Lambda + Sum[nu_j * q_j, j] == 0 + // <=> q_0 == Lambda - Sum[nu_j * q_j, j] // Also compute \bar{q}_0 = q_0 - Sum[sigma_j * q_j, j] res[0] = x[0] - _lambda; double q0_bar = x[0]; @@ -145,15 +159,16 @@ struct SMAProblem // We have already computed \bar{q}_0 in the loop above for (int i = 1; i < size(); ++i) { - // Getting to c_{p,0}: -bndIdx takes us to q_0, another -nComp to c_{p,0}. This means jac[-bndIdx - nComp] corresponds to c_{p,0}. - // Getting to c_{p,i}: -bndIdx takes us to q_0, another -nComp to c_{p,0} and a +i to c_{p,i}. + // Getting to c_{p,0}: -bndIdx takes us to q_0, another -nComp to c_{p,0}. This means jac[-bndIdx - nComp] + // corresponds to c_{p,0}. Getting to c_{p,i}: -bndIdx takes us to q_0, another -nComp to c_{p,0} and a +i + // to c_{p,i}. // This means jac[i - bndIdx - nComp] corresponds to c_{p,i}. const double ka = _kA[i]; const double kd = _kD[i]; const double nu = _nu[i]; - const double c0_pow_nu = pow(yCp[0], nu); + const double c0_pow_nu = pow(yCp[0], nu); const double q0_bar_pow_nu_m1 = pow(q0_bar, nu - 1.0); // dres_i / dq_0 @@ -164,7 +179,8 @@ struct SMAProblem { // dres_i / dq_j jac[j - i] = -ka * yCp[i] * nu * q0_bar_pow_nu_m1 * (-_sigma[j]); - // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] corresponds to q_j. + // Getting to q_j: -bndIdx takes us to q_0, another +bndIdx2 to q_j. This means jac[bndIdx2 - bndIdx] + // corresponds to q_j. } // Add to dres_i / dq_i @@ -209,9 +225,7 @@ struct SMAProblem } }; - -template -void runNewtonRes(double stdDev) +template void runNewtonRes(double stdDev) { Prob p; std::cout << "======= NEWTON RESIDUAL ======\n"; @@ -242,9 +256,10 @@ void runNewtonRes(double stdDev) printVector("Start", sol.data(), p.size()); printDiffVector("Diffe", sol.data(), p.initPoint, p.size()); - const bool success = cadet::nonlin::adaptiveTrustRegionNewtonMethod([&](double const* const x, double* const res) { return p.residual(x, res); }, - [&](double const* const x, double* const res) { return p.jacobianSolve(x, res); }, maxIter, resTol, - initDamping, minDamping, sol.data(), tempMem.data(), p.size()); + const bool success = cadet::nonlin::adaptiveTrustRegionNewtonMethod( + [&](double const* const x, double* const res) { return p.residual(x, res); }, + [&](double const* const x, double* const res) { return p.jacobianSolve(x, res); }, maxIter, resTol, initDamping, + minDamping, sol.data(), tempMem.data(), p.size()); p.residual(sol.data(), tempMem.data()); @@ -256,9 +271,7 @@ void runNewtonRes(double stdDev) printDiffVector("Differen", sol.data(), p.initPoint, p.size()); } - -template -void runNewtonErr(double stdDev) +template void runNewtonErr(double stdDev) { Prob p; std::cout << "======== NEWTON ERROR ========\n"; @@ -289,10 +302,11 @@ void runNewtonErr(double stdDev) printVector("Start", sol.data(), p.size()); printDiffVector("Diffe", sol.data(), p.initPoint, p.size()); - const bool success = cadet::nonlin::robustAdaptiveTrustRegionNewtonMethod([&](double const* const x, double* const res) { return p.residual(x, res); }, + const bool success = cadet::nonlin::robustAdaptiveTrustRegionNewtonMethod( + [&](double const* const x, double* const res) { return p.residual(x, res); }, [&](double const* const x, double* const res) { return p.jacobianSolve(x, res); }, - [&](double* const res) { return p.jacobianResolve(res); }, maxIter, resTol, - initDamping, minDamping, sol.data(), tempMem.data(), p.size()); + [&](double* const res) { return p.jacobianResolve(res); }, maxIter, resTol, initDamping, minDamping, sol.data(), + tempMem.data(), p.size()); p.residual(sol.data(), tempMem.data()); @@ -304,8 +318,7 @@ void runNewtonErr(double stdDev) printDiffVector("Differen", sol.data(), p.initPoint, p.size()); } -template -void runLevMar(double stdDev) +template void runLevMar(double stdDev) { Prob p; std::cout << "===== LEVENBERG-MARQUADRT ====\n"; @@ -333,9 +346,10 @@ void runLevMar(double stdDev) printVector("Origi", p.initPoint, p.size()); printVector("Start", sol.data(), p.size()); printDiffVector("Diffe", sol.data(), p.initPoint, p.size()); - const bool success = cadet::nonlin::levenbergMarquardt([&](double const* const x, double* const res) { return p.residual(x, res); }, - [&](double const* const x, cadet::linalg::detail::DenseMatrixBase& mat) { return p.jacobian(x, mat); }, maxIter, resTol, - initDamping, sol.data(), tempMem.data(), p._jacMatrix, p.size()); + const bool success = cadet::nonlin::levenbergMarquardt( + [&](double const* const x, double* const res) { return p.residual(x, res); }, + [&](double const* const x, cadet::linalg::detail::DenseMatrixBase& mat) { return p.jacobian(x, mat); }, maxIter, + resTol, initDamping, sol.data(), tempMem.data(), p._jacMatrix, p.size()); p.residual(sol.data(), tempMem.data()); @@ -359,14 +373,19 @@ int main(int argc, char** argv) TCLAP::CmdLine cmd("Tests nonlinear solvers with a small SMA example", ' ', "1.0"); cmd.setOutput(&customOut); - cmd >> (new TCLAP::SwitchArg("r", "newtonRes", "Residual based adaptive trust region Newton"))->storeIn(&useTRNRes); - cmd >> (new TCLAP::SwitchArg("e", "newtonErr", "Error based adaptive trust region Newton"))->storeIn(&useTRNErr); + cmd >> (new TCLAP::SwitchArg("r", "newtonRes", "Residual based adaptive trust region Newton")) + ->storeIn(&useTRNRes); + cmd >> + (new TCLAP::SwitchArg("e", "newtonErr", "Error based adaptive trust region Newton"))->storeIn(&useTRNErr); cmd >> (new TCLAP::SwitchArg("l", "levMar", "Levenberg-Marquardt"))->storeIn(&useLevMar); - cmd >> (new TCLAP::ValueArg("s", "stddev", "Standard deviation of relative random perturbation (default: 0.01)", false, 0.01, "StdDev"))->storeIn(&stdDev); + cmd >> (new TCLAP::ValueArg("s", "stddev", + "Standard deviation of relative random perturbation (default: 0.01)", false, + 0.01, "StdDev")) + ->storeIn(&stdDev); cmd.parse(argc, argv); } - catch (const TCLAP::ArgException &e) + catch (const TCLAP::ArgException& e) { std::cerr << "ERROR: " << e.error() << " for argument " << e.argId() << std::endl; return 1;